GenEx: Add $<STRING> generator expression

Fixes: #27188
This commit is contained in:
Marc Chevrier
2025-10-20 16:56:31 +02:00
parent 7564cbae12
commit fd7e305097
71 changed files with 1947 additions and 50 deletions

View File

@@ -321,6 +321,21 @@ Version Comparisons
String Expressions
------------------
Most of the expressions in this section are closely associated with the
:command:`string` command, providing the same capabilities, but in
the form of a generator expression.
In each of the following string-related generator expressions, the ``string``
must not contain any commas if that generator expression expects something to
be provided after the ``string``. For example, the expression
``$<STRING:FIND,string,value>`` requires a ``value`` after the ``string``.
Since a comma is used to separate the ``string`` and the ``value``, the
``string`` cannot itself contain a comma. This restriction does not apply to
the :command:`string` command, it is specific to the string-handling generator
expressions only. The :genex:`$<COMMA>` generator expression can be used to
specify a comma as part of the arguments of the string-related generator
expressions.
.. _`String Comparisons Generator Expressions`:
String Comparisons
@@ -333,7 +348,7 @@ evaluates to ``1`` if ``${foo}`` is any of ``BAR``, ``Bar``, ``bar``, etc.
.. code-block:: cmake
$<STREQUAL:$<UPPER_CASE:${foo}>,BAR>
$<STREQUAL:$<STRING:TOUPPER,${foo}>,BAR>
.. genex:: $<STREQUAL:string1,string2>
@@ -366,10 +381,337 @@ evaluates to ``1`` if ``${foo}`` is any of ``BAR``, ``Bar``, ``bar``, etc.
``1`` if ``string1`` is lexicographically greater than or equal to
``string2``, else ``0``.
.. _`String Queries Generator Expressions`:
String Queries
^^^^^^^^^^^^^^
.. genex:: $<STRING:LENGTH,string>
.. versionadded:: 4.3
The given string's length in bytes. Note that this means, if ``string``
contains multi-byte characters, the result will *not* be the number of
characters.
.. genex:: $<STRING:SUBSTRING,string,begin,length>
.. versionadded:: 4.3
The substring of the given ``string``. If ``length`` is ``-1`` or greater
than the ``string`` length the remainder of the string starting at ``begin``
will be returned.
Both ``begin`` and ``length`` are counted in bytes, so care must
be exercised if ``string`` could contain multi-byte characters.
.. genex:: $<STRING:FIND,string[,FROM:(BEGIN|END)],substring>
The position where the given ``substring`` was found in the supplied
``string``. If the ``substring`` is not found, a position of -1 is returned.
The ``FROM:`` option defines how the search will be done:
``BEGIN``
The search will start at the beginning of the ``string``. This the default.
``END``
The search will start from the end of the ``string``.
The ``$<STRING:FIND>`` generator expression treats all strings as ASCII-only
characters. The index returned will also be counted in bytes, so strings
containing multi-byte characters may lead to unexpected results.
.. genex:: $<STRING:MATCH,string[,SEEK:(ONCE|ALL)],regular_expression>
.. versionadded:: 4.3
Match, in the ``string``, the ``regular_expression``.
The ``SEEK:`` option specifies the match behavior:
``ONCE``
Match only the first occurrence. This is the default.
``ALL``
Match as many times as possible and return the matches as a list.
See the :ref:`Regular expressions specification <Regex Specification>` for
the syntax of the ``regular_expression`` parameter.
.. _`String Generating Generator Expressions`:
String Generations
^^^^^^^^^^^^^^^^^^
.. genex:: $<STRING:JOIN,glue,input[,input]...>
.. versionadded:: 4.3
Join all the ``input`` arguments together using the ``glue`` string.
.. genex:: $<STRING:ASCII,number[,number]...>
.. versionadded:: 4.3
Convert all numbers, in the range 1-255, into corresponding ASCII
characters. Any number outside this range will raise an error.
.. genex:: $<STRING:TIMESTAMP[,(UTC|format)]...>
.. versionadded:: 4.3
Produce a string representation of the current date and/or time.
If the generator expression is unable to obtain a timestamp, the result will
be the empty string ``""``.
The optional ``UTC`` flag requests the current date/time representation to
be in Coordinated Universal Time (UTC) rather than local time.
If the ``SOURCE_DATE_EPOCH`` environment variable is set, its value will be
used instead of the current time.
See https://reproducible-builds.org/specs/source-date-epoch/ for details.
The optional ``<format>`` may contain the following format specifiers:
``%%``
A literal percent sign (%).
``%d``
The day of the current month (01-31).
``%H``
The hour on a 24-hour clock (00-23).
``%I``
The hour on a 12-hour clock (01-12).
``%j``
The day of the current year (001-366).
``%m``
The month of the current year (01-12).
``%b``
Abbreviated month name (e.g. Oct).
``%B``
Full month name (e.g. October).
``%M``
The minute of the current hour (00-59).
``%s``
Seconds since midnight (UTC) 1-Jan-1970 (UNIX time).
``%S``
The second of the current minute. 60 represents a leap second. (00-60)
``%f``
The microsecond of the current second (000000-999999).
``%U``
The week number of the current year (00-53).
``%V``
The ISO 8601 week number of the current year (01-53).
``%w``
The day of the current week. 0 is Sunday. (0-6)
``%a``
Abbreviated weekday name (e.g. Fri).
``%A``
Full weekday name (e.g. Friday).
``%y``
The last two digits of the current year (00-99).
``%Y``
The current year.
``%z``
The offset of the time zone from UTC, in hours and minutes,
with format ``+hhmm`` or ``-hhmm``.
``%Z``
The time zone name.
Unknown format specifiers will be ignored and copied to the output
as-is.
If no explicit ``format`` is given, it will default to:
* ``%Y-%m-%dT%H:%M:%S`` for local time.
* ``%Y-%m-%dT%H:%M:%SZ`` for UTC.
.. genex:: $<STRING:RANDOM[,(LENGTH:length|ALPHABET:alphabet|RANDOM_SEED:seed)]...>
.. versionadded:: 4.3
Produce a random string of ASCII characters. The possible options are:
``LENGTH:length``
Define the length of the string. The default length is 5 characters.
``ALPHABET:alphabet``
Define the characters used for the generation. The alphabet is always
interpreted as holding ASCII characters. The default alphabet is all
numbers and upper and lower case letters.
``RANDOM_SEED:seed``
Specify an integer which will be used to seed the random number generator.
.. genex:: $<STRING:UUID,NAMESPACE:namespace,TYPE:(MD5|SHA1)[,NAME:name][,CASE:(LOWER|UPPER)]>
.. versionadded:: 4.3
Create a universally unique identifier (aka GUID) as per RFC4122.
A UUID has the format ``xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx``
where each ``x`` represents an hexadecimal character.
The UUID is based on the hash of the combined values of:
``NAMESPACE:namespace``
``namespace`` which has to be a valid UUID.
``NAME:name``
``name`` is an arbitrary string.
``TYPE:``
The hash algorithm can be either:
``MD5``
Version 3 UUID.
``SHA1``
Version 5 UUID.
``CASE:``
Specify the case of the hexadecimal characters.
``LOWER``
Hexadecimal characters are all of lowercase. This is the default.
``UPPER``
Hexadecimal characters are all of uppercase.
.. _`String Transforming Generator Expressions`:
String Transformations
^^^^^^^^^^^^^^^^^^^^^^
.. genex:: $<STRING:REPLACE[,(STRING|REGEX)],string,match_string,replace_string>
.. versionadded:: 4.3
Replace all occurrences of ``match_string`` in the ``string`` with
``replace_string``.
The ``match_string`` can be of two different types:
``STRING``
``match_string`` is a literal string and match will be done by simple
string comparison. This is the default.
``REGEX``
``match_string`` is a regular expression. Match this regular_expression as
many times as possible and substitute the ``replace_string`` for the match
in the ``string``.
The ``replace_string`` may refer to parenthesis-delimited subexpressions of
the match using \\1, \\2, ..., \\9. Note that two backslashes (\\\\1) are
required in CMake code to get a backslash through argument parsing.
.. genex:: $<STRING:APPEND,string,input[,input]...>
.. versionadded:: 4.3
Append all the ``input`` arguments to the ``string``.
.. genex:: $<STRING:PREPEND,string,input[,input]...>
.. versionadded:: 4.3
Prepend all the ``input`` arguments to the ``string``.
.. genex:: $<STRING:TOLOWER,string>
.. versionadded:: 4.3
Content of ``string`` converted to lower case.
.. genex:: $<STRING:TOUPPER,string>
.. versionadded:: 4.3
Content of ``string`` converted to upper case.
.. genex:: $<STRING:STRIP,SPACES,string>
.. versionadded:: 4.3
Remove the specified elements from the ``string``. The possible options are:
``SPACES``
Remove the leading and trailing spaces of the ``string``.
.. genex:: $<STRING:QUOTE,REGEX,string>
.. versionadded:: 4.3
Escape the specified elements of the ``string``. The possible options are:
``REGEX``
Escape all characters that have special meaning in a regular expressions,
such that the ``string`` can be used as part of a regular expression to
match the input literally.
.. genex:: $<STRING:HEX,string>
.. versionadded:: 4.3
Convert each byte in the ``string`` to its hexadecimal representation.
Letters in the result (a through f) are in lowercase.
.. genex:: $<STRING:HASH,string,ALGORITHM:algorithm>
.. versionadded:: 4.3
Compute a cryptographic hash of the ``string``. The supported algorithm
names, as specified by the ``ALGORITHM:`` option are:
``MD5``
Message-Digest Algorithm 5, RFC 1321.
``SHA1``
US Secure Hash Algorithm 1, RFC 3174.
``SHA224``
US Secure Hash Algorithms, RFC 4634.
``SHA256``
US Secure Hash Algorithms, RFC 4634.
``SHA384``
US Secure Hash Algorithms, RFC 4634.
``SHA512``
US Secure Hash Algorithms, RFC 4634.
``SHA3_224``
Keccak SHA-3.
``SHA3_256``
Keccak SHA-3.
``SHA3_384``
Keccak SHA-3.
``SHA3_512``
Keccak SHA-3.
.. genex:: $<STRING:MAKE_C_IDENTIFIER,string>
.. versionadded:: 4.3
Convert each non-alphanumeric character in the ``string`` to an underscore.
If the first character of the ``string`` is a digit, an underscore will also
be prepended.
.. genex:: $<LOWER_CASE:string>
Content of ``string`` converted to lower case.
@@ -380,8 +722,8 @@ String Transformations
.. genex:: $<MAKE_C_IDENTIFIER:string>
Content of ``...`` converted to a C identifier. The conversion follows the
same behavior as :command:`string(MAKE_C_IDENTIFIER)`.
Content of ``string`` converted to a C identifier. The conversion follows
the same behavior as :command:`string(MAKE_C_IDENTIFIER)`.
List Expressions
----------------

View File

@@ -0,0 +1,8 @@
GenEx-STRING
------------
* :genex:`$<STRING:...>` generator expressions were added for
:ref:`query <String Queries Generator Expressions>`,
:ref:`generation <String Generating Generator Expressions>`, and
:ref:`transformation <String Transforming Generator Expressions>` operations
on strings.

View File

@@ -7,6 +7,7 @@
#include <cerrno>
#include <cstdlib>
#include <cstring>
#include <exception>
#include <functional>
#include <map>
#include <memory>
@@ -782,6 +783,55 @@ bool CheckGenExParameters(cm::GenEx::Evaluation* eval,
return true;
};
template <typename IndexType>
bool GetNumericArgument(std::string const& arg, IndexType& value)
{
try {
std::size_t pos;
if (sizeof(IndexType) == sizeof(long)) {
value = std::stol(arg, &pos);
} else {
value = std::stoll(arg, &pos);
}
if (pos != arg.length()) {
// this is not a number
return false;
}
} catch (std::invalid_argument const&) {
return false;
}
return true;
}
template <typename IndexType>
bool GetNumericArguments(
cm::GenEx::Evaluation* eval, GeneratorExpressionContent const* cnt,
Arguments args, std::vector<IndexType>& indexes,
cmList::ExpandElements expandElements = cmList::ExpandElements::No)
{
using IndexRange = cmRange<Arguments::const_iterator>;
IndexRange arguments(args.begin(), args.end());
cmList list;
if (expandElements == cmList::ExpandElements::Yes) {
list = cmList{ args.begin(), args.end(), expandElements };
arguments = IndexRange{ list.begin(), list.end() };
}
for (auto const& value : arguments) {
IndexType index;
if (!GetNumericArgument(value, index)) {
reportError(eval, cnt->GetOriginalExpression(),
cmStrCat("index: \"", value, "\" is not a valid index"));
return false;
}
indexes.push_back(index);
}
return true;
}
bool CheckPathParametersEx(cm::GenEx::Evaluation* eval,
GeneratorExpressionContent const* cnt,
cm::string_view option, std::size_t count,
@@ -1238,6 +1288,553 @@ static const struct PathEqualNode : public cmGeneratorExpressionNode
}
} pathEqualNode;
namespace {
inline bool CheckStringParametersEx(cm::GenEx::Evaluation* eval,
GeneratorExpressionContent const* cnt,
cm::string_view option, std::size_t count,
int required = 1, bool exactly = true)
{
return CheckGenExParameters(eval, cnt, "STRING"_s, option, count, required,
exactly);
}
inline bool CheckStringParameters(cm::GenEx::Evaluation* eval,
GeneratorExpressionContent const* cnt,
cm::string_view option, Arguments args,
int required = 1)
{
return CheckStringParametersEx(eval, cnt, option, args.size(), required);
};
}
static const struct StringNode : public cmGeneratorExpressionNode
{
StringNode() {} // NOLINT(modernize-use-equals-default)
int NumExpectedParameters() const override { return OneOrMoreParameters; }
bool AcceptsArbitraryContentParameter() const override { return true; }
std::string Evaluate(
std::vector<std::string> const& parameters, cm::GenEx::Evaluation* eval,
GeneratorExpressionContent const* content,
cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
{
static std::unordered_map<
cm::string_view,
std::function<std::string(cm::GenEx::Evaluation*,
GeneratorExpressionContent const*,
Arguments&)>>
stringCommands{
{ "LENGTH"_s,
[](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt,
Arguments& args) -> std::string {
if (CheckStringParameters(ev, cnt, "LENGTH"_s, args)) {
return std::to_string(cm::CMakeString{ args.front() }.Length());
}
return std::string{};
} },
{ "SUBSTRING"_s,
[](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt,
Arguments& args) -> std::string {
if (CheckStringParameters(ev, cnt, "SUBSTRING"_s, args, 3)) {
cm::CMakeString str{ args.front() };
std::vector<long> indexes;
if (GetNumericArguments(ev, cnt, args.advance(1), indexes)) {
try {
return str.Substring(indexes.front(), indexes.back());
} catch (std::out_of_range const& e) {
reportError(ev, cnt->GetOriginalExpression(), e.what());
return std::string{};
}
}
}
return std::string{};
} },
{ "FIND"_s,
[](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt,
Arguments& args) -> std::string {
if (CheckStringParametersEx(ev, cnt, "FIND"_s, args.size(), 2,
false)) {
if (args.size() > 3) {
reportError(ev, cnt->GetOriginalExpression(),
"$<STRING:FIND> expression expects at "
"most three parameters.");
return std::string{};
}
auto const FROM = "FROM:"_s;
cm::CMakeString str{ args.front() };
cm::CMakeString::FindFrom from =
cm::CMakeString::FindFrom::Begin;
cm::string_view substring;
args.advance(1);
if (args.size() == 2) {
cm::CMakeString::FindFrom opt =
static_cast<cm::CMakeString::FindFrom>(-1);
for (auto const& arg : args) {
if (cmHasPrefix(arg, FROM)) {
if (arg != "FROM:BEGIN"_s && arg != "FROM:END"_s) {
reportError(
ev, cnt->GetOriginalExpression(),
cmStrCat("Invalid value for '", FROM,
"' option. 'BEGIN' or 'END' expected."));
return std::string{};
}
opt = arg == "FROM:BEGIN"_s
? cm::CMakeString::FindFrom::Begin
: cm::CMakeString::FindFrom::End;
} else {
substring = arg;
}
}
if (opt == static_cast<cm::CMakeString::FindFrom>(-1)) {
reportError(
ev, cnt->GetOriginalExpression(),
cmStrCat("Expected option '", FROM, "' is missing."));
return std::string{};
}
from = opt;
} else {
substring = args.front();
}
auto pos = str.Find(substring, from);
return pos == cm::CMakeString::npos ? "-1" : std::to_string(pos);
}
return std::string{};
} },
{ "MATCH"_s,
[](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt,
Arguments& args) -> std::string {
if (CheckStringParametersEx(ev, cnt, "MATCH"_s, args.size(), 2,
false)) {
if (args.size() > 3) {
reportError(ev, cnt->GetOriginalExpression(),
"$<STRING:MATCH> expression expects at "
"most three parameters.");
return std::string{};
}
auto const SEEK = "SEEK:"_s;
cm::CMakeString str{ args.front() };
cm::CMakeString::MatchItems seek =
cm::CMakeString::MatchItems::Once;
auto const* regex = &args[1];
args.advance(1);
if (args.size() == 2) {
cm::CMakeString::MatchItems opt =
static_cast<cm::CMakeString::MatchItems>(-1);
for (auto const& arg : args) {
if (cmHasPrefix(arg, SEEK)) {
if (arg != "SEEK:ONCE"_s && arg != "SEEK:ALL"_s) {
reportError(
ev, cnt->GetOriginalExpression(),
cmStrCat("Invalid value for '", SEEK,
"' option. 'ONCE' or 'ALL' expected."));
return std::string{};
}
opt = arg == "SEEK:ONCE"_s
? cm::CMakeString::MatchItems::Once
: cm::CMakeString::MatchItems::All;
} else {
regex = &arg;
}
}
if (opt == static_cast<cm::CMakeString::MatchItems>(-1)) {
reportError(
ev, cnt->GetOriginalExpression(),
cmStrCat("Expected option '", SEEK, "' is missing."));
return std::string{};
}
seek = opt;
}
try {
return str.Match(*regex, seek).to_string();
} catch (std::invalid_argument const& e) {
reportError(ev, cnt->GetOriginalExpression(), e.what());
return std::string{};
}
}
return std::string{};
} },
{ "JOIN"_s,
[](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt,
Arguments& args) -> std::string {
if (CheckStringParametersEx(ev, cnt, "JOIN"_s, args.size(), 2,
false)) {
auto const& glue = args.front();
return cm::CMakeString{ args.advance(1), glue };
}
return std::string{};
} },
{ "ASCII"_s,
[](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt,
Arguments& args) -> std::string {
if (CheckStringParametersEx(ev, cnt, "ASCII"_s, args.size(), 1,
false)) {
try {
return cm::CMakeString{}.FromASCII(args);
} catch (std::invalid_argument const& e) {
reportError(ev, cnt->GetOriginalExpression(), e.what());
return std::string{};
}
}
return std::string{};
} },
{ "TIMESTAMP"_s,
[](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt,
Arguments& args) -> std::string {
cm::string_view format;
cm::CMakeString::UTC utc = cm::CMakeString::UTC::No;
if (args.size() == 2 && args.front() != "UTC"_s &&
args.back() != "UTC"_s) {
reportError(ev, cnt->GetOriginalExpression(),
"'UTC' option is expected.");
return std::string{};
}
if (args.size() > 2) {
reportError(ev, cnt->GetOriginalExpression(),
"$<STRING:TIMESTAMP> expression expects at most two "
"parameters.");
return std::string{};
}
for (auto const& arg : args) {
if (arg == "UTC"_s) {
utc = cm::CMakeString::UTC::Yes;
} else {
format = arg;
}
}
return cm::CMakeString{}.Timestamp(format, utc);
} },
{ "RANDOM"_s,
[](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt,
Arguments& args) -> std::string {
auto const ALPHABET = "ALPHABET:"_s;
auto const LENGTH = "LENGTH:"_s;
auto const RANDOM_SEED = "RANDOM_SEED:"_s;
if (args.size() > 3) {
reportError(ev, cnt->GetOriginalExpression(),
"$<STRING:RANDOM> expression expects at most three "
"parameters.");
return std::string{};
}
cm::string_view alphabet;
std::size_t length = 5;
bool seed_specified = false;
unsigned int seed = 0;
for (auto const& arg : args) {
if (cmHasPrefix(arg, ALPHABET)) {
alphabet = cm::string_view{ arg.c_str() + ALPHABET.length() };
continue;
}
if (cmHasPrefix(arg, LENGTH)) {
try {
length = std::stoul(arg.substr(LENGTH.size()));
} catch (std::exception const&) {
reportError(ev, cnt->GetOriginalExpression(),
cmStrCat(arg, ": invalid numeric value for '",
LENGTH, "' option."));
return std::string{};
}
continue;
}
if (cmHasPrefix(arg, RANDOM_SEED)) {
try {
seed_specified = true;
seed = static_cast<unsigned int>(
std::stoul(arg.substr(RANDOM_SEED.size())));
} catch (std::exception const&) {
reportError(ev, cnt->GetOriginalExpression(),
cmStrCat(arg, ": invalid numeric value for '",
RANDOM_SEED, "' option."));
return std::string{};
}
continue;
}
reportError(ev, cnt->GetOriginalExpression(),
cmStrCat(arg, ": invalid parameter."));
return std::string{};
}
try {
if (seed_specified) {
return cm::CMakeString{}.Random(seed, length, alphabet);
}
return cm::CMakeString{}.Random(length, alphabet);
} catch (std::exception const& e) {
reportError(ev, cnt->GetOriginalExpression(), e.what());
return std::string{};
}
} },
{ "UUID"_s,
[](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt,
Arguments& args) -> std::string {
if (CheckStringParametersEx(ev, cnt, "UUID"_s, args.size(), 2,
false)) {
auto const NAMESPACE = "NAMESPACE:"_s;
auto const NAME = "NAME:"_s;
auto const TYPE = "TYPE:"_s;
auto const CASE = "CASE:"_s;
if (args.size() > 4) {
reportError(ev, cnt->GetOriginalExpression(),
"$<STRING:UUID> expression expects at most four "
"parameters.");
return std::string{};
}
cm::string_view nameSpace;
cm::string_view name;
cm::CMakeString::UUIDType type =
static_cast<cm::CMakeString::UUIDType>(-1);
cm::CMakeString::Case uuidCase = cm::CMakeString::Case::Lower;
for (auto const& arg : args) {
if (cmHasPrefix(arg, NAMESPACE)) {
nameSpace =
cm::string_view{ arg.c_str() + NAMESPACE.length() };
if (nameSpace.empty()) {
reportError(
ev, cnt->GetOriginalExpression(),
cmStrCat("Invalid value for '", NAMESPACE, "' option."));
return std::string{};
}
continue;
}
if (cmHasPrefix(arg, NAME)) {
name = cm::string_view{ arg.c_str() + NAME.length() };
continue;
}
if (cmHasPrefix(arg, TYPE)) {
auto value = cm::string_view{ arg.c_str() + TYPE.length() };
if (value != "MD5"_s && value != "SHA1"_s) {
reportError(
ev, cnt->GetOriginalExpression(),
cmStrCat("Invalid value for '", TYPE,
"' option. 'MD5' or 'SHA1' expected."));
return std::string{};
}
type = value == "MD5"_s ? cm::CMakeString::UUIDType::MD5
: cm::CMakeString::UUIDType::SHA1;
continue;
}
if (cmHasPrefix(arg, CASE)) {
auto value = cm::string_view{ arg.c_str() + CASE.length() };
if (value != "UPPER"_s && value != "LOWER"_s) {
reportError(
ev, cnt->GetOriginalExpression(),
cmStrCat("Invalid value for '", CASE,
"' option. 'UPPER' or 'LOWER' expected."));
return std::string{};
}
uuidCase = value == "UPPER"_s ? cm::CMakeString::Case::Upper
: cm::CMakeString::Case::Lower;
continue;
}
reportError(ev, cnt->GetOriginalExpression(),
cmStrCat(arg, ": invalid parameter."));
return std::string{};
}
if (nameSpace.empty()) {
reportError(
ev, cnt->GetOriginalExpression(),
cmStrCat("Required option '", NAMESPACE, "' is missing."));
return std::string{};
}
if (type == static_cast<cm::CMakeString::UUIDType>(-1)) {
reportError(
ev, cnt->GetOriginalExpression(),
cmStrCat("Required option '", TYPE, "' is missing."));
return std::string{};
}
try {
return cm::CMakeString{}.UUID(nameSpace, name, type, uuidCase);
} catch (std::exception const& e) {
reportError(ev, cnt->GetOriginalExpression(), e.what());
return std::string{};
}
}
return std::string{};
} },
{ "REPLACE"_s,
[](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt,
Arguments& args) -> std::string {
if (CheckStringParametersEx(ev, cnt, "REPLACE"_s, args.size(), 3,
false)) {
if (args.size() > 4) {
reportError(ev, cnt->GetOriginalExpression(),
"$<STRING:REPLACE> expression expects at "
"most four parameters.");
return std::string{};
}
cm::CMakeString::Regex isRegex = cm::CMakeString::Regex::No;
if (args.size() == 4) {
cm::string_view type = args.front();
if (type != "STRING"_s && type != "REGEX"_s) {
reportError(
ev, cnt->GetOriginalExpression(),
cmStrCat(
'\'', type,
"' is unexpected. 'STRING' or 'REGEX' expected."));
return std::string{};
}
isRegex = type == "STRING"_s ? cm::CMakeString::Regex::No
: cm::CMakeString::Regex::Yes;
args.advance(1);
}
try {
return cm::CMakeString{ args.front() }.Replace(
args[1], args[2], isRegex);
} catch (std::invalid_argument const& e) {
reportError(ev, cnt->GetOriginalExpression(), e.what());
return std::string{};
}
}
return std::string{};
} },
{ "APPEND"_s,
[](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt,
Arguments& args) -> std::string {
if (CheckStringParametersEx(ev, cnt, "APPEND"_s, args.size(), 2,
false)) {
cm::CMakeString data{ args.front() };
return data.Append(args.advance(1));
}
return std::string{};
} },
{ "PREPEND"_s,
[](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt,
Arguments& args) -> std::string {
if (CheckStringParametersEx(ev, cnt, "PREPEND "_s, args.size(), 2,
false)) {
cm::CMakeString data{ args.front() };
return data.Prepend(args.advance(1));
}
return std::string{};
} },
{ "TOLOWER"_s,
[](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt,
Arguments& args) -> std::string {
if (CheckStringParameters(ev, cnt, "TOLOWER"_s, args, 1)) {
return cm::CMakeString{}.ToLower(args.front());
}
return std::string{};
} },
{ "TOUPPER"_s,
[](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt,
Arguments& args) -> std::string {
if (CheckStringParameters(ev, cnt, "TOUPPER"_s, args, 1)) {
return cm::CMakeString{}.ToUpper(args.front());
}
return std::string{};
} },
{ "STRIP"_s,
[](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt,
Arguments& args) -> std::string {
if (CheckStringParameters(ev, cnt, "STRIP"_s, args, 2)) {
if (args.front() != "SPACES"_s) {
reportError(ev, cnt->GetOriginalExpression(),
cmStrCat('\'', args.front(),
"' is unexpected. 'SPACES' expected."));
return std::string{};
}
return cm::CMakeString{ args[1] }.Strip();
}
return std::string{};
} },
{ "QUOTE"_s,
[](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt,
Arguments& args) -> std::string {
if (CheckStringParameters(ev, cnt, "QUOTE"_s, args, 2)) {
if (args.front() != "REGEX"_s) {
reportError(ev, cnt->GetOriginalExpression(),
cmStrCat('\'', args.front(),
"' is unexpected. 'REGEX' expected."));
return std::string{};
}
return cm::CMakeString{ args[1] }.Quote();
}
return std::string{};
} },
{ "HEX"_s,
[](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt,
Arguments& args) -> std::string {
if (CheckStringParameters(ev, cnt, "HEX"_s, args, 1)) {
return cm::CMakeString{ args.front() }.ToHexadecimal();
}
return std::string{};
} },
{ "HASH"_s,
[](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt,
Arguments& args) -> std::string {
if (CheckStringParameters(ev, cnt, "HASH"_s, args, 2)) {
auto const ALGORITHM = "ALGORITHM:"_s;
if (cmHasPrefix(args[1], ALGORITHM)) {
try {
auto const algo =
cm::string_view{ args[1].c_str() + ALGORITHM.length() };
if (algo.empty()) {
reportError(
ev, cnt->GetOriginalExpression(),
cmStrCat("Missing value for '", ALGORITHM, "' option."));
return std::string{};
}
return cm::CMakeString{ args.front() }.Hash(algo);
} catch (std::exception const& e) {
reportError(ev, cnt->GetOriginalExpression(), e.what());
return std::string{};
}
}
reportError(ev, cnt->GetOriginalExpression(),
cmStrCat(args[1], ": invalid parameter. Option '",
ALGORITHM, "' expected."));
}
return std::string{};
} },
{ "MAKE_C_IDENTIFIER"_s,
[](cm::GenEx::Evaluation* ev, GeneratorExpressionContent const* cnt,
Arguments& args) -> std::string {
if (CheckStringParameters(ev, cnt, "MAKE_C_IDENTIFIER"_s, args,
1)) {
return cm::CMakeString{ args.front() }.MakeCIdentifier();
}
return std::string{};
} }
};
if (parameters.front().empty()) {
reportError(eval, content->GetOriginalExpression(),
"$<STRING> expression requires at least one parameter.");
return std::string{};
}
if (cm::contains(stringCommands, parameters.front())) {
auto args = Arguments{ parameters }.advance(1);
return stringCommands[parameters.front()](eval, content, args);
}
reportError(eval, content->GetOriginalExpression(),
cmStrCat(parameters.front(), ": invalid option."));
return std::string{};
}
} stringNode;
namespace {
inline bool CheckListParametersEx(cm::GenEx::Evaluation* eval,
GeneratorExpressionContent const* cnt,
@@ -1259,53 +1856,6 @@ inline cmList GetList(std::string const& list)
{
return list.empty() ? cmList{} : cmList{ list, cmList::EmptyElements::Yes };
}
bool GetNumericArgument(std::string const& arg, cmList::index_type& value)
{
try {
std::size_t pos;
if (sizeof(cmList::index_type) == sizeof(long)) {
value = std::stol(arg, &pos);
} else {
value = std::stoll(arg, &pos);
}
if (pos != arg.length()) {
// this is not a number
return false;
}
} catch (std::invalid_argument const&) {
return false;
}
return true;
}
bool GetNumericArguments(
cm::GenEx::Evaluation* eval, GeneratorExpressionContent const* cnt,
Arguments args, std::vector<cmList::index_type>& indexes,
cmList::ExpandElements expandElements = cmList::ExpandElements::No)
{
using IndexRange = cmRange<Arguments::const_iterator>;
IndexRange arguments(args.begin(), args.end());
cmList list;
if (expandElements == cmList::ExpandElements::Yes) {
list = cmList{ args.begin(), args.end(), expandElements };
arguments = IndexRange{ list.begin(), list.end() };
}
for (auto const& value : arguments) {
cmList::index_type index;
if (!GetNumericArgument(value, index)) {
reportError(eval, cnt->GetOriginalExpression(),
cmStrCat("index: \"", value, "\" is not a valid index"));
return false;
}
indexes.push_back(index);
}
return true;
}
}
static const struct ListNode : public cmGeneratorExpressionNode
@@ -4952,6 +5502,7 @@ cmGeneratorExpressionNode const* cmGeneratorExpressionNode::GetNode(
{ "STRLESS_EQUAL", &strLessEqualNode },
{ "STRGREATER", &strGreaterNode },
{ "STRGREATER_EQUAL", &strGreaterEqualNode },
{ "STRING", &stringNode },
{ "EQUAL", &equalNode },
{ "IN_LIST", &inListNode },
{ "FILTER", &filterNode },

View File

@@ -523,6 +523,7 @@ add_RunCMake_test(GenEx-TARGET_IMPORT_FILE)
add_RunCMake_test(GenEx-GENEX_EVAL)
add_RunCMake_test(GenEx-TARGET_PROPERTY)
add_RunCMake_test(GenEx-TARGET_RUNTIME_DLLS)
add_RunCMake_test(GenEx-STRING)
add_RunCMake_test(GenEx-PATH)
add_RunCMake_test(GenEx-PATH_EQUAL)
add_RunCMake_test(GenEx-LIST)

View File

@@ -0,0 +1,22 @@
cmake_minimum_required(VERSION 4.2...4.3)
include ("${RunCMake_SOURCE_DIR}/check_errors.cmake")
unset (errors)
set(reference "abcd")
string(APPEND reference "efg" "ABCD")
set(output "$<STRING:APPEND,abcd,efg,ABCD>")
if (NOT output STREQUAL reference)
list (APPEND errors "<STRING:APPEND,abcd,efg,ABCD> returns bad data: ${output}")
endif()
unset(reference)
string(APPEND reference "efg" "ABCD")
set(output "$<STRING:APPEND,,efg,ABCD>")
if (NOT output STREQUAL reference)
list (APPEND errors "<STRING:APPEND,,efg,ABCD> returns bad data: ${output}")
endif()
check_errors("STRING:APPEND" ${errors})

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,38 @@
CMake Error at ASCII-WrongArguments\.cmake:[0-9]+ \(add_custom_target\):
Error evaluating generator expression:
\$<STRING:ASCII,foo>
Character with code foo does not exist.
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)
CMake Error at ASCII-WrongArguments\.cmake:[0-9]+ \(add_custom_target\):
Error evaluating generator expression:
\$<STRING:ASCII,256>
Character with code 256 does not exist.
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)
CMake Error at ASCII-WrongArguments\.cmake:[0-9]+ \(add_custom_target\):
Error evaluating generator expression:
\$<STRING:ASCII,32,256>
Character with code 256 does not exist.
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)
CMake Error at ASCII-WrongArguments\.cmake:[0-9]+ \(add_custom_target\):
Error evaluating generator expression:
\$<STRING:ASCII,32,-1>
Character with code -1 does not exist.
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)

View File

@@ -0,0 +1,7 @@
add_custom_target(check ALL COMMAND check
$<STRING:ASCII,foo>
$<STRING:ASCII,256>
$<STRING:ASCII,32,256>
$<STRING:ASCII,32,-1>
VERBATIM)

View File

@@ -0,0 +1,20 @@
cmake_minimum_required(VERSION 4.2...4.3)
include ("${RunCMake_SOURCE_DIR}/check_errors.cmake")
unset (errors)
string(ASCII 32 reference)
set(output "$<STRING:ASCII,32>")
if (NOT output STREQUAL reference)
list (APPEND errors "<STRING:ASCII,32> returns bad data: ${output}")
endif()
string(ASCII 32 64 reference)
set(output "$<STRING:ASCII,32,64>")
if (NOT output STREQUAL reference)
list (APPEND errors "<STRING:ASCII,32,64> returns bad data: ${output}")
endif()
check_errors("STRING:ASCII" ${errors})

View File

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

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,18 @@
CMake Error at FIND-WrongArguments\.cmake:[0-9]+ \(add_custom_target\):
Error evaluating generator expression:
\$<STRING:FIND,string,OTHER,foo>
Expected option 'FROM:' is missing.
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)
CMake Error at FIND-WrongArguments\.cmake:[0-9]+ \(add_custom_target\):
Error evaluating generator expression:
\$<STRING:FIND,string,FROM:OTHER,foo>
Invalid value for 'FROM:' option. 'BEGIN' or 'END' expected.
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)

View File

@@ -0,0 +1,5 @@
add_custom_target(check ALL COMMAND check
$<STRING:FIND,string,OTHER,foo>
$<STRING:FIND,string,FROM:OTHER,foo>
VERBATIM)

View File

@@ -0,0 +1,48 @@
cmake_minimum_required(VERSION 4.2...4.3)
include ("${RunCMake_SOURCE_DIR}/check_errors.cmake")
unset (errors)
string(FIND "abcdabcd" "cd" reference)
set(output "$<STRING:FIND,abcdabcd,cd>")
if (NOT output STREQUAL reference)
list (APPEND errors "<STRING:FIND,abcdabcd,cd> returns bad data: ${output}")
endif()
set(output "$<STRING:FIND,abcdabcd,FROM:BEGIN,cd>")
if (NOT output STREQUAL reference)
list (APPEND errors "<STRING:FIND,abcdabcd,FROM:BEGIN,cd> returns bad data: ${output}")
endif()
set(output "$<STRING:FIND,abcdabcd,cd,FROM:BEGIN>")
if (NOT output STREQUAL reference)
list (APPEND errors "<STRING:FIND,abcdabcd,cd,FROM:BEGIN> returns bad data: ${output}")
endif()
string(FIND "abcdabcd" "cd" reference REVERSE)
set(output "$<STRING:FIND,abcdabcd,FROM:END,cd>")
if (NOT output STREQUAL reference)
list (APPEND errors "<STRING:FIND,abcdabcd,FROM:END,cd> returns bad data: ${output}")
endif()
set(output "$<STRING:FIND,abcdabcd,cd,FROM:END>")
if (NOT output STREQUAL reference)
list (APPEND errors "<STRING:FIND,abcdabcd,cd,FROM:END> returns bad data: ${output}")
endif()
string(FIND "abcdabcd" "xy" reference)
set(output "$<STRING:FIND,abcdabcd,xy>")
if (NOT output STREQUAL reference)
list (APPEND errors "<STRING:FIND,abcdabcd,xy> returns bad data: ${output}")
endif()
set(output "$<STRING:FIND,abcdabcd,xy,FROM:BEGIN>")
if (NOT output STREQUAL reference)
list (APPEND errors "<STRING:FIND,abcdabcd,xy,FROM:BEGIN> returns bad data: ${output}")
endif()
string(FIND "abcdabcd" "xy" reference REVERSE)
set(output "$<STRING:FIND,abcdabcd,xy,FROM:END>")
if (NOT output STREQUAL reference)
list (APPEND errors "<STRING:FIND,abcdabcd,xy,FROM:END> returns bad data: ${output}")
endif()
check_errors("STRING:FIND" ${errors})

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,18 @@
CMake Error at HASH-WrongArguments\.cmake:[0-9]+ \(add_custom_target\):
Error evaluating generator expression:
\$<STRING:HASH,string,ALGORITHM:>
Missing value for 'ALGORITHM:' option.
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)
CMake Error at HASH-WrongArguments\.cmake:[0-9]+ \(add_custom_target\):
Error evaluating generator expression:
\$<STRING:HASH,string,ALGORITHM:FOO>
FOO: invalid hash algorithm.
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)

View File

@@ -0,0 +1,5 @@
add_custom_target(check ALL COMMAND check
$<STRING:HASH,string,ALGORITHM:>
$<STRING:HASH,string,ALGORITHM:FOO>
VERBATIM)

View File

@@ -0,0 +1,69 @@
cmake_minimum_required(VERSION 4.2...4.3)
include ("${RunCMake_SOURCE_DIR}/check_errors.cmake")
unset (errors)
# foreach(hash IN ITEMS MD5 SHA1 SHA224 SHA256 SHA384 SHA512 SHA3_224 SHA3_256 SHA3_384 SHA3_512)
string(MD5 reference "sample input string")
set(output "$<STRING:HASH,sample input string,ALGORITHM:MD5>")
if (NOT output STREQUAL reference)
list (APPEND errors "<STRING:HASH,sample input string,MD5> returns bad data: ${output}")
endif()
string(SHA1 reference "sample input string")
set(output "$<STRING:HASH,sample input string,ALGORITHM:SHA1>")
if (NOT output STREQUAL reference)
list (APPEND errors "<STRING:HASH,sample input string,SHA1> returns bad data: ${output}")
endif()
string(SHA224 reference "sample input string")
set(output "$<STRING:HASH,sample input string,ALGORITHM:SHA224>")
if (NOT output STREQUAL reference)
list (APPEND errors "<STRING:HASH,sample input string,SHA224> returns bad data: ${output}")
endif()
string(SHA256 reference "sample input string")
set(output "$<STRING:HASH,sample input string,ALGORITHM:SHA256>")
if (NOT output STREQUAL reference)
list (APPEND errors "<STRING:HASH,sample input string,SHA256> returns bad data: ${output}")
endif()
string(SHA384 reference "sample input string")
set(output "$<STRING:HASH,sample input string,ALGORITHM:SHA384>")
if (NOT output STREQUAL reference)
list (APPEND errors "<STRING:HASH,sample input string,SHA384> returns bad data: ${output}")
endif()
string(SHA512 reference "sample input string")
set(output "$<STRING:HASH,sample input string,ALGORITHM:SHA512>")
if (NOT output STREQUAL reference)
list (APPEND errors "<STRING:HASH,sample input string,SHA512> returns bad data: ${output}")
endif()
string(SHA3_224 reference "sample input string")
set(output "$<STRING:HASH,sample input string,ALGORITHM:SHA3_224>")
if (NOT output STREQUAL reference)
list (APPEND errors "<STRING:HASH,sample input string,SHA3_224> returns bad data: ${output}")
endif()
string(SHA3_256 reference "sample input string")
set(output "$<STRING:HASH,sample input string,ALGORITHM:SHA3_256>")
if (NOT output STREQUAL reference)
list (APPEND errors "<STRING:HASH,sample input string,SHA3_256> returns bad data: ${output}")
endif()
string(SHA3_384 reference "sample input string")
set(output "$<STRING:HASH,sample input string,ALGORITHM:SHA3_384>")
if (NOT output STREQUAL reference)
list (APPEND errors "<STRING:HASH,sample input string,SHA3_384> returns bad data: ${output}")
endif()
string(SHA3_512 reference "sample input string")
set(output "$<STRING:HASH,sample input string,ALGORITHM:SHA3_512>")
if (NOT output STREQUAL reference)
list (APPEND errors "<STRING:HASH,sample input string,SHA3_512> returns bad data: ${output}")
endif()
check_errors("STRING:HASH" ${errors})

View File

@@ -0,0 +1,20 @@
cmake_minimum_required(VERSION 4.2...4.3)
include ("${RunCMake_SOURCE_DIR}/check_errors.cmake")
unset (errors)
string(HEX "The quick brown fox jumps over the lazy dog." reference)
set(output "$<STRING:HEX,The quick brown fox jumps over the lazy dog.>")
if (NOT output STREQUAL reference)
list (APPEND errors "<STRING:HEX,The quick brown fox jumps over the lazy dog.> returns bad data: ${output}")
endif()
string(HEX "Ash nazg durbatulûk. Ash nazg gimbatul. Ash nazg thrakatulûk. Agh burzum-ishi krimpatul" reference)
set(output "$<STRING:HEX,Ash nazg durbatulûk. Ash nazg gimbatul. Ash nazg thrakatulûk. Agh burzum-ishi krimpatul>")
if (NOT output STREQUAL reference)
list (APPEND errors "<STRING:HEX,Ash nazg durbatulûk. Ash nazg gimbatul. Ash nazg thrakatulûk. Agh burzum-ishi krimpatul> returns bad data: ${output}")
endif()
check_errors("STRING:HEX" ${errors})

View File

@@ -0,0 +1,32 @@
cmake_minimum_required(VERSION 4.2...4.3)
include ("${RunCMake_SOURCE_DIR}/check_errors.cmake")
unset (errors)
string(JOIN "|" reference "ab" "cd")
set(output "$<STRING:JOIN,|,ab,cd>")
if (NOT output STREQUAL reference)
list (APPEND errors "<STRING:JOIN,|,ab,cd> returns bad data: ${output}")
endif()
string(JOIN "" reference "ab" "cd")
set(output "$<STRING:JOIN,,ab,cd>")
if (NOT output STREQUAL reference)
list (APPEND errors "<STRING:JOIN,,ab,cd> returns bad data: ${output}")
endif()
string(JOIN "|" reference)
set(output "$<STRING:JOIN,|,>")
if (NOT output STREQUAL reference)
list (APPEND errors "<STRING:JOIN,|,> returns bad data: ${output}")
endif()
string(JOIN "" reference)
set(output "$<STRING:JOIN,,>")
if (NOT output STREQUAL reference)
list (APPEND errors "<STRING:JOIN,,> returns bad data: ${output}")
endif()
check_errors("STRING:JOIN" ${errors})

View File

@@ -0,0 +1,21 @@
cmake_minimum_required(VERSION 4.2...4.3)
include ("${RunCMake_SOURCE_DIR}/check_errors.cmake")
unset (errors)
string(LENGTH "abc" reference)
set(output "$<STRING:LENGTH,abc>")
if (NOT output STREQUAL reference)
list (APPEND errors "<STRING:LENGTH,abc> returns bad data: ${output}")
endif()
string(LENGTH "" reference)
set(output "$<STRING:LENGTH,>")
if (NOT output STREQUAL reference)
list (APPEND errors "<STRING:LENGTH,> returns bad data: ${output}")
endif()
check_errors("STRING:LENGTH" ${errors})

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,28 @@
CMake Error at MATCH-WrongArguments\.cmake:[0-9]+ \(add_custom_target\):
Error evaluating generator expression:
\$<STRING:MATCH,string,\(>
Failed to compile regex "\("
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)
CMake Error at MATCH-WrongArguments\.cmake:[0-9]+ \(add_custom_target\):
Error evaluating generator expression:
\$<STRING:MATCH,string,FOO,\.\+>
Expected option 'SEEK:' is missing.
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)
CMake Error at MATCH-WrongArguments\.cmake:[0-9]+ \(add_custom_target\):
Error evaluating generator expression:
\$<STRING:MATCH,string,SEEK:FOO,\.\+>
Invalid value for 'SEEK:' option. 'ONCE' or 'ALL' expected.
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)

View File

@@ -0,0 +1,8 @@
add_custom_target(check ALL COMMAND check
[[
$<STRING:MATCH,string,(>
$<STRING:MATCH,string,FOO,.+>
$<STRING:MATCH,string,SEEK:FOO,.+>
]]
VERBATIM)

View File

@@ -0,0 +1,40 @@
cmake_minimum_required(VERSION 4.2...4.3)
include ("${RunCMake_SOURCE_DIR}/check_errors.cmake")
unset (errors)
string(REGEX MATCH "cd" reference "abcdabcd")
set(output "$<STRING:MATCH,abcdabcd,cd>")
if (NOT output STREQUAL reference)
list (APPEND errors "<STRING:MATCH,abcdabcd,cd> returns bad data: ${output}")
endif()
set(output "$<STRING:MATCH,abcdabcd,SEEK:ONCE,cd>")
if (NOT output STREQUAL reference)
list (APPEND errors "<STRING:MATCH,abcdabcd,SEEK:ONCE,cd> returns bad data: ${output}")
endif()
string(REGEX MATCH "(b|B)cd" reference "abcdABcd")
set(output "$<STRING:MATCH,abcdABcd,(b|B)cd>")
if (NOT output STREQUAL reference)
list (APPEND errors "<STRING:MATCH,abcdABcd,B(cd)> returns bad data: ${output}")
endif()
set(output "$<STRING:MATCH,abcdABcd,(b|B)cd,SEEK:ONCE>")
if (NOT output STREQUAL reference)
list (APPEND errors "<STRING:MATCH,abcdABcd,B(cd),SEEK:ONCE> returns bad data: ${output}")
endif()
string(REGEX MATCHALL "cd" reference "abcdabcd")
set(output "$<STRING:MATCH,abcdabcd,SEEK:ALL,cd>")
if (NOT output STREQUAL reference)
list (APPEND errors "<STRING:MATCH,abcdabcd,SEEK:ALL,cd> returns bad data: ${output}")
endif()
string(REGEX MATCHALL "(b|B)cd" reference "abcdABcd")
set(output "$<STRING:MATCH,abcdABcd,(b|B)cd,SEEK:ALL>")
if (NOT output STREQUAL reference)
list (APPEND errors "<STRING:MATCH,abcdABcd,B(cd),SEEK:ALL> returns bad data: ${output}")
endif()
check_errors("STRING:MATCH" ${errors})

View File

@@ -0,0 +1,22 @@
cmake_minimum_required(VERSION 4.2...4.3)
include ("${RunCMake_SOURCE_DIR}/check_errors.cmake")
unset (errors)
set(reference "abcd")
string(PREPEND reference "efg" "ABCD")
set(output "$<STRING:PREPEND,abcd,efg,ABCD>")
if (NOT output STREQUAL reference)
list (APPEND errors "<STRING:PREPEND,abcd,efg,ABCD> returns bad data: ${output}")
endif()
unset(reference)
string(PREPEND reference "efg" "ABCD")
set(output "$<STRING:PREPEND,,efg,ABCD>")
if (NOT output STREQUAL reference)
list (APPEND errors "<STRING:PREPEND,,efg,ABCD> returns bad data: ${output}")
endif()
check_errors("STRING:PREPEND" ${errors})

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,8 @@
CMake Error at QUOTE-WrongArguments\.cmake:[0-9]+ \(add_custom_target\):
Error evaluating generator expression:
\$<STRING:QUOTE,FOO,string>
'FOO' is unexpected. 'REGEX' expected.
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)

View File

@@ -0,0 +1,6 @@
add_custom_target(check ALL COMMAND check
[[
$<STRING:QUOTE,FOO,string>
]]
VERBATIM)

View File

@@ -0,0 +1,21 @@
cmake_minimum_required(VERSION 4.2...4.3)
include ("${RunCMake_SOURCE_DIR}/check_errors.cmake")
unset (errors)
string(REGEX QUOTE reference "abcd")
set(output "$<STRING:QUOTE,REGEX,abcd>")
if (NOT output STREQUAL reference)
list (APPEND errors "<STRING:QUOTE,REGEX,abcd> returns bad data: ${output}")
endif()
string(REGEX QUOTE reference "ab|c+12?3[x-z]$(y)\\t\\r\\n.cma*ke^[:alpha:] ")
set(output [[
$<STRING:QUOTE,REGEX,ab|c+12?3[x-z]$(y)\t\r\n.cma*ke^[:alpha:]> ]])
if (NOT output STREQUAL reference)
list (APPEND errors "<STRING:QUOTE,REGEX,ab|c+12?3[x-z]$(y)\\t\\r\\n.cma*ke^[:alpha:]> returns bad data: ${output}")
endif()
check_errors("STRING:QUOTE" ${errors})

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,18 @@
CMake Error at RANDOM-WrongArguments\.cmake:[0-9]+ \(add_custom_target\):
Error evaluating generator expression:
\$<STRING:RANDOM,LENGTH:foo>
LENGTH:foo: invalid numeric value for 'LENGTH:' option.
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)
CMake Error at RANDOM-WrongArguments\.cmake:[0-9]+ \(add_custom_target\):
Error evaluating generator expression:
\$<STRING:RANDOM,RANDOM_SEED:foo>
RANDOM_SEED:foo: invalid numeric value for 'RANDOM_SEED:' option.
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)

View File

@@ -0,0 +1,5 @@
add_custom_target(check ALL COMMAND check
$<STRING:RANDOM,LENGTH:foo>
$<STRING:RANDOM,RANDOM_SEED:foo>
VERBATIM)

View File

@@ -0,0 +1,32 @@
cmake_minimum_required(VERSION 4.2...4.3)
include ("${RunCMake_SOURCE_DIR}/check_errors.cmake")
unset (errors)
string(RANDOM RANDOM_SEED 123 reference)
set(output "$<STRING:RANDOM,RANDOM_SEED:123>")
if (NOT output STREQUAL reference)
list (APPEND errors "<STRING:RANDOM,RANDOM_SEED:123> returns bad data: ${output}")
endif()
string(RANDOM LENGTH 9 RANDOM_SEED 5 reference)
set(output "$<STRING:RANDOM,LENGTH:9,RANDOM_SEED:5>")
if (NOT output STREQUAL reference)
list (APPEND errors "<STRING:RANDOM,LENGTH:9,RANDOM_SEED:5> returns bad data: ${output}")
endif()
string(RANDOM LENGTH 9 RANDOM_SEED 5 ALPHABET "" reference)
set(output "$<STRING:RANDOM,LENGTH:9,RANDOM_SEED:5,ALPHABET:>")
if (NOT output STREQUAL reference)
list (APPEND errors "<STRING:RANDOM,LENGTH:9,RANDOM_SEED:5,ALPHABET:> returns bad data: ${output}")
endif()
string(RANDOM LENGTH 9 RANDOM_SEED 5 ALPHABET "abcdef123456789" reference)
set(output "$<STRING:RANDOM,LENGTH:9,RANDOM_SEED:5,ALPHABET:abcdef123456789>")
if (NOT output STREQUAL reference)
list (APPEND errors "<STRING:RANDOM,LENGTH:9,RANDOM_SEED:5,ALPHABET:abcdef123456789> returns bad data: ${output}")
endif()
check_errors("STRING:RANDOM" ${errors})

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,18 @@
CMake Error at REPLACE-WrongArguments\.cmake:[0-9]+ \(add_custom_target\):
Error evaluating generator expression:
\$<STRING:REPLACE,FOO,string,\.\+,>
'FOO' is unexpected. 'STRING' or 'REGEX' expected.
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)
CMake Error at REPLACE-WrongArguments\.cmake:[0-9]+ \(add_custom_target\):
Error evaluating generator expression:
\$<STRING:REPLACE,REGEX,string,\(,>
Failed to compile regex "\("
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)

View File

@@ -0,0 +1,7 @@
add_custom_target(check ALL COMMAND check
[[
$<STRING:REPLACE,FOO,string,.+,>
$<STRING:REPLACE,REGEX,string,(,>
]]
VERBATIM)

View File

@@ -0,0 +1,44 @@
cmake_minimum_required(VERSION 4.2...4.3)
include ("${RunCMake_SOURCE_DIR}/check_errors.cmake")
unset (errors)
string(REPLACE "bcd" "BCD" reference "abcdabcd")
set(output "$<STRING:REPLACE,abcdabcd,bcd,BCD>")
if (NOT output STREQUAL reference)
list (APPEND errors "<STRING:REPLACE,abcdabcd,bcd,BCD> returns bad data: ${output}")
endif()
set(output "$<STRING:REPLACE,STRING,abcdabcd,bcd,BCD>")
if (NOT output STREQUAL reference)
list (APPEND errors "<STRING:REPLACE,STRING,abcdabcd,bcd,BCD> returns bad data: ${output}")
endif()
string(REPLACE "bcd" "" reference "abcdabcd")
set(output "$<STRING:REPLACE,abcdabcd,bcd,>")
if (NOT output STREQUAL reference)
list (APPEND errors "<STRING:REPLACE,abcdabcd,bcd,> returns bad data: ${output}")
endif()
set(output "$<STRING:REPLACE,STRING,abcdabcd,bcd,>")
if (NOT output STREQUAL reference)
list (APPEND errors "<STRING:REPLACE,STRING,abcdabcd,bcd,> returns bad data: ${output}")
endif()
string(REPLACE "xyz" "BCD" reference "abcdabcd")
set(output "$<STRING:REPLACE,abcdabcd,xyz,BCD>")
if (NOT output STREQUAL reference)
list (APPEND errors "<STRING:REPLACE,abcdabcd,xyz,BCD> returns bad data: ${output}")
endif()
set(output "$<STRING:REPLACE,STRING,abcdabcd,xyz,BCD>")
if (NOT output STREQUAL reference)
list (APPEND errors "<STRING:REPLACE,STRING,abcdabcd,xyz,BCD> returns bad data: ${output}")
endif()
string(REGEX REPLACE "bcd" "BCD" reference "abcdabcd")
set(output "$<STRING:REPLACE,REGEX,abcdabcd,bcd,BCD>")
if (NOT output STREQUAL reference)
list (APPEND errors "<STRING:REPLACE,REGEX,abcdabcd,bcd,BCD> returns bad data: ${output}")
endif()
check_errors("STRING:REPLACE" ${errors})

View File

@@ -0,0 +1,80 @@
include(RunCMake)
run_cmake(no-arguments)
run_cmake(bad-option)
function(check_string_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 LENGTH TOLOWER TOUPPER HEX MAKE_C_IDENTIFIER)
check_string_syntax (${subcommand} unexpected-arg "-DSTRING_ARGUMENTS=${subcommand},ARG1,ARG2")
endforeach()
### sub-commands with two arguments
foreach (subcommand IN ITEMS HASH STRIP QUOTE)
check_string_syntax (${subcommand} unexpected-arg "-DSTRING_ARGUMENTS=${subcommand},ARG1,ARG2,ARG3")
endforeach()
### sub-commands with three arguments
foreach (subcommand IN ITEMS SUBSTRING)
check_string_syntax (${subcommand} unexpected-arg "-DSTRING_ARGUMENTS=${subcommand},ARG1,ARG2,ARG3,ARG4")
endforeach()
foreach (subcommand IN ITEMS FIND MATCH RANDOM)
check_string_syntax (${subcommand} unexpected-arg2 "-DSTRING_ARGUMENTS=${subcommand},ARG1,ARG2,ARG3,ARG4")
endforeach()
### sub-commands with four arguments
foreach (subcommand IN ITEMS REPLACE)
check_string_syntax (${subcommand} unexpected-arg2 "-DSTRING_ARGUMENTS=${subcommand},ARG1,ARG2,ARG3,ARG4,ARG5")
endforeach()
run_cmake(SUBSTRING-WrongArguments)
run_cmake(FIND-WrongArguments)
run_cmake(MATCH-WrongArguments)
run_cmaKE(ASCII-WrongArguments)
run_cmake(TIMESTAMP-WrongArguments)
run_cmake(RANDOM-WrongArguments)
run_cmake(UUID-WrongArguments)
run_cmake(REPLACE-WrongArguments)
run_cmake(STRIP-WrongArguments)
run_cmake(QUOTE-WrongArguments)
run_cmake(HASH-WrongArguments)
function(check_string_execution name)
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${name}-build)
if(RunCMake_GENERATOR_IS_MULTI_CONFIG)
set(CONFIG_OPTION -DCMAKE_CONFIGURATION_TYPES=Debug)
else()
set(CONFIG_OPTION -DCMAKE_BUILD_TYPE=Debug)
endif()
set(RunCMake_TEST_NO_CLEAN 1)
set(RunCMake_TEST_VARIANT_DESCRIPTION " - ${name}")
run_cmake_with_options(generate -DSTRING_TEST=${name} ${CONFIG_OPTION})
run_cmake_command(check "${CMAKE_COMMAND}" "-DRunCMake_SOURCE_DIR=${RunCMake_SOURCE_DIR}" -P "${RunCMake_TEST_BINARY_DIR}/${name}.cmake")
endfunction()
check_string_execution (LENGTH)
check_string_execution (SUBSTRING)
check_string_execution (FIND)
check_string_execution (MATCH)
check_string_execution (JOIN)
check_string_execution (ASCII)
check_string_execution (TIMESTAMP)
check_string_execution (RANDOM)
check_string_execution (UUID)
check_string_execution (REPLACE)
check_string_execution (APPEND)
check_string_execution (PREPEND)
check_string_execution (TOLOWER)
check_string_execution (TOUPPER)
check_string_execution (STRIP)
check_string_execution (QUOTE)
check_string_execution (HEX)
check_string_execution (HASH)

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,8 @@
CMake Error at STRIP-WrongArguments\.cmake:[0-9]+ \(add_custom_target\):
Error evaluating generator expression:
\$<STRING:STRIP,FOO,string>
'FOO' is unexpected. 'SPACES' expected.
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)

View File

@@ -0,0 +1,6 @@
add_custom_target(check ALL COMMAND check
[[
$<STRING:STRIP,FOO,string>
]]
VERBATIM)

View File

@@ -0,0 +1,14 @@
cmake_minimum_required(VERSION 4.2...4.3)
include ("${RunCMake_SOURCE_DIR}/check_errors.cmake")
unset (errors)
string(STRIP " ABCD " reference)
set(output "$<STRING:STRIP,SPACES, ABCD >")
if (NOT output STREQUAL reference)
list (APPEND errors "<STRING:STRIP,SPACES, ABCD > returns bad data: ${output}")
endif()
check_errors("STRING:STRIP" ${errors})

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,38 @@
CMake Error at SUBSTRING-WrongArguments\.cmake:[0-9]+ \(add_custom_target\):
Error evaluating generator expression:
\$<STRING:SUBSTRING,string,foo,1>
index: "foo" is not a valid index
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)
CMake Error at SUBSTRING-WrongArguments\.cmake:[0-9]+ \(add_custom_target\):
Error evaluating generator expression:
\$<STRING:SUBSTRING,string,1,foo>
index: "foo" is not a valid index
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)
CMake Error at SUBSTRING-WrongArguments\.cmake:[0-9]+ \(add_custom_target\):
Error evaluating generator expression:
\$<STRING:SUBSTRING,string,-1,2>
begin index: -1 is out of range 0 - 6
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)
CMake Error at SUBSTRING-WrongArguments\.cmake:[0-9]+ \(add_custom_target\):
Error evaluating generator expression:
\$<STRING:SUBSTRING,string,1,-2>
end index: -2 should be -1 or greater
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)

View File

@@ -0,0 +1,7 @@
add_custom_target(check ALL COMMAND check
$<STRING:SUBSTRING,string,foo,1>
$<STRING:SUBSTRING,string,1,foo>
$<STRING:SUBSTRING,string,-1,2>
$<STRING:SUBSTRING,string,1,-2>
VERBATIM)

View File

@@ -0,0 +1,26 @@
cmake_minimum_required(VERSION 4.2...4.3)
include ("${RunCMake_SOURCE_DIR}/check_errors.cmake")
unset (errors)
string(SUBSTRING "abcd" 1 2 reference)
set(output "$<STRING:SUBSTRING,abcd,1,2>")
if (NOT output STREQUAL reference)
list (APPEND errors "<STRING:SUBSTRING,abcd,1,2> returns bad data: ${output}")
endif()
string(SUBSTRING "abcd" 1 -1 reference)
set(output "$<STRING:SUBSTRING,abcd,1,-1>")
if (NOT output STREQUAL reference)
list (APPEND errors "<STRING:SUBSTRING,abcd,1,-1> returns bad data: ${output}")
endif()
string(SUBSTRING "abcd" 1 5 reference)
set(output "$<STRING:SUBSTRING,abcd,1,5>")
if (NOT output STREQUAL reference)
list (APPEND errors "<STRING:SUBSTRING,abcd,1,5> returns bad data: ${output}")
endif()
check_errors("STRING:SUBSTRING" ${errors})

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,8 @@
CMake Error at TIMESTAMP-WrongArguments\.cmake:[0-9]+ \(add_custom_target\):
Error evaluating generator expression:
\$<STRING:TIMESTAMP,%s,%s>
'UTC' option is expected.
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)

View File

@@ -0,0 +1,4 @@
add_custom_target(check ALL COMMAND check
$<STRING:TIMESTAMP,%s,%s>
VERBATIM)

View File

@@ -0,0 +1,27 @@
cmake_minimum_required(VERSION 4.2...4.3)
include ("${RunCMake_SOURCE_DIR}/check_errors.cmake")
unset (errors)
set(ENV{SOURCE_DATE_EPOCH} "1123456789")
string(TIMESTAMP reference "%Y-%m-%d %H:%M:%S.%f %A=%a %B=%b %y day=%j wd=%w week=%U w_iso=%V %%I=%I epoch=%s TZ=%Z tz=%z")
set(output "$<STRING:TIMESTAMP,%Y-%m-%d %H:%M:%S.%f %A=%a %B=%b %y day=%j wd=%w week=%U w_iso=%V %%I=%I epoch=%s TZ=%Z tz=%z>")
if (NOT output STREQUAL reference)
list (APPEND errors "<STRING:TIMESTAMP,%Y-%m-%d %H:%M:%S.%f %A=%a %B=%b %y day=%j wd=%w week=%U w_iso=%V %%I=%I epoch=%s TZ=%Z tz=%z> returns bad data: ${output}")
endif()
string(TIMESTAMP reference "%Y-%m-%d %H:%M:%S.%f %A=%a %B=%b %y day=%j wd=%w week=%U w_iso=%V %%I=%I epoch=%s TZ=%Z tz=%z" UTC)
set(output "$<STRING:TIMESTAMP,%Y-%m-%d %H:%M:%S.%f %A=%a %B=%b %y day=%j wd=%w week=%U w_iso=%V %%I=%I epoch=%s TZ=%Z tz=%z,UTC>")
if (NOT output STREQUAL reference)
list (APPEND errors "<STRING:TIMESTAMP,%Y-%m-%d %H:%M:%S.%f %A=%a %B=%b %y day=%j wd=%w week=%U w_iso=%V %%I=%I epoch=%s TZ=%Z tz=%z,UTC> returns bad data: ${output}")
endif()
set(output "$<STRING:TIMESTAMP,UTC,%Y-%m-%d %H:%M:%S.%f %A=%a %B=%b %y day=%j wd=%w week=%U w_iso=%V %%I=%I epoch=%s TZ=%Z tz=%z>")
if (NOT output STREQUAL reference)
list (APPEND errors "<STRING:TIMESTAMP,UTC,%Y-%m-%d %H:%M:%S.%f %A=%a %B=%b %y day=%j wd=%w week=%U w_iso=%V %%I=%I epoch=%s TZ=%Z tz=%z> returns bad data: ${output}")
endif()
check_errors("STRING:TIMESTAMP" ${errors})

View File

@@ -0,0 +1,26 @@
cmake_minimum_required(VERSION 4.2...4.3)
include ("${RunCMake_SOURCE_DIR}/check_errors.cmake")
unset (errors)
string(TOLOWER "ABCD" reference)
set(output "$<STRING:TOLOWER,ABCD>")
if (NOT output STREQUAL reference)
list (APPEND errors "<STRING:TOLOWER,ABCD> returns bad data: ${output}")
endif()
string(TOLOWER "abcd" reference)
set(output "$<STRING:TOLOWER,abcd>")
if (NOT output STREQUAL reference)
list (APPEND errors "<STRING:TOLOWER,abcd> returns bad data: ${output}")
endif()
string(TOLOWER "abcdABCD12@!" reference)
set(output "$<STRING:TOLOWER,abcdABCD12@!>")
if (NOT output STREQUAL reference)
list (APPEND errors "<STRING:TOLOWER,abcdABCD12@!> returns bad data: ${output}")
endif()
check_errors("STRING:TOLOWER" ${errors})

View File

@@ -0,0 +1,26 @@
cmake_minimum_required(VERSION 4.2...4.3)
include ("${RunCMake_SOURCE_DIR}/check_errors.cmake")
unset (errors)
string(TOUPPER "abcd" reference)
set(output "$<STRING:TOUPPER,abcd>")
if (NOT output STREQUAL reference)
list (APPEND errors "<STRING:TOUPPER,abcd> returns bad data: ${output}")
endif()
string(TOUPPER "ABCD" reference)
set(output "$<STRING:TOUPPER,ABCD>")
if (NOT output STREQUAL reference)
list (APPEND errors "<STRING:TOUPPER,ABCD> returns bad data: ${output}")
endif()
string(TOUPPER "abcdABCD12@!" reference)
set(output "$<STRING:TOUPPER,abcdABCD12@!>")
if (NOT output STREQUAL reference)
list (APPEND errors "<STRING:TOUPPER,abcdABCD12@!> returns bad data: ${output}")
endif()
check_errors("STRING:TOUPPER" ${errors})

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,28 @@
CMake Error at UUID-WrongArguments\.cmake:[0-9]+ \(add_custom_target\):
Error evaluating generator expression:
\$<STRING:UUID,TYPE:MD5,NAMESPACE:>
Invalid value for 'NAMESPACE:' option.
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)
CMake Error at UUID-WrongArguments\.cmake:[0-9]+ \(add_custom_target\):
Error evaluating generator expression:
\$<STRING:UUID,NAMESPACE:foo,TYPE:FOO>
Invalid value for 'TYPE:' option. 'MD5' or 'SHA1' expected.
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)
CMake Error at UUID-WrongArguments\.cmake:[0-9]+ \(add_custom_target\):
Error evaluating generator expression:
\$<STRING:UUID,NAMESPACE:foo,TYPE:MD5,CASE:FOO>
Invalid value for 'CASE:' option. 'UPPER' or 'LOWER' expected.
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)

View File

@@ -0,0 +1,6 @@
add_custom_target(check ALL COMMAND check
$<STRING:UUID,TYPE:MD5,NAMESPACE:>
$<STRING:UUID,NAMESPACE:foo,TYPE:FOO>
$<STRING:UUID,NAMESPACE:foo,TYPE:MD5,CASE:FOO>
VERBATIM)

View File

@@ -0,0 +1,32 @@
cmake_minimum_required(VERSION 4.2...4.3)
include ("${RunCMake_SOURCE_DIR}/check_errors.cmake")
unset (errors)
set(UUID_DNS_NAMESPACE 6ba7b810-9dad-11d1-80b4-00c04fd430c8)
string(UUID reference NAMESPACE ${UUID_DNS_NAMESPACE} NAME www.example.com TYPE MD5)
set(output "$<STRING:UUID,NAMESPACE:6ba7b810-9dad-11d1-80b4-00c04fd430c8,NAME:www.example.com,TYPE:MD5>")
if (NOT output STREQUAL reference)
list (APPEND errors "<STRING:UUID,NAMESPACE:6ba7b810-9dad-11d1-80b4-00c04fd430c8,NAME:www.example.com,TYPE:MD5> returns bad data: ${output}")
endif()
set(output "$<STRING:UUID,NAMESPACE:6ba7b810-9dad-11d1-80b4-00c04fd430c8,NAME:www.example.com,TYPE:MD5,CASE:LOWER>")
if (NOT output STREQUAL reference)
list (APPEND errors "<STRING:UUID,NAMESPACE:6ba7b810-9dad-11d1-80b4-00c04fd430c8,NAME:www.example.com,TYPE:MD5,CASE:LOWER> returns bad data: ${output}")
endif()
string(UUID reference NAMESPACE ${UUID_DNS_NAMESPACE} NAME www.example.com TYPE MD5 UPPER)
set(output "$<STRING:UUID,NAMESPACE:6ba7b810-9dad-11d1-80b4-00c04fd430c8,NAME:www.example.com,TYPE:MD5,CASE:UPPER>")
if (NOT output STREQUAL reference)
list (APPEND errors "<STRING:UUID,NAMESPACE:6ba7b810-9dad-11d1-80b4-00c04fd430c8,NAME:www.example.com,TYPE:MD5,CASE:UPPER> returns bad data: ${output}")
endif()
string(UUID reference NAMESPACE ${UUID_DNS_NAMESPACE} NAME www.example.com TYPE SHA1)
set(output "$<STRING:UUID,NAMESPACE:6ba7b810-9dad-11d1-80b4-00c04fd430c8,NAME:www.example.com,TYPE:SHA1>")
if (NOT output STREQUAL reference)
list (APPEND errors "<STRING:UUID,NAMESPACE:6ba7b810-9dad-11d1-80b4-00c04fd430c8,NAME:www.example.com,TYPE:SHA1> returns bad data: ${output}")
endif()
check_errors("STRING:UUID" ${errors})

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:
\$<STRING:BAD_OPTION>
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 "$<STRING:BAD_OPTION>")

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,6 @@
if (STRING_TEST STREQUAL "TIMESTAMP")
set(ENV{SOURCE_DATE_EPOCH} "1123456789")
endif()
file(GENERATE OUTPUT "${STRING_TEST}.cmake" INPUT "${STRING_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:
\$<STRING:>
\$<STRING> expression requires at least one parameter.
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)

View File

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

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:
\$<STRING:[A-Z_]+,.+>
\$<STRING:[A-Z_:]+(,[A-Z_:]+(,[A-Z_:]+)?)?> expression requires exactly (one|two|three) parameters?.
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)

View File

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

View File

@@ -0,0 +1 @@
1

View File

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

View File

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