Merge topic 'GenEx-STRING'

fd7e305097 GenEx: Add $<STRING> generator expression
7564cbae12 GenEx: add new expressions for string comparisons
dab5e6ebb1 introduce cm::CMakeString class as helper for string() command
34c0c7754f cmString: add methods append and insert
ab60f1a010 Extend usage for cm::string_view for more flexibility

Acked-by: Kitware Robot <kwrobot@kitware.com>
Merge-request: !11382
This commit is contained in:
Brad King
2025-11-15 14:10:26 +00:00
committed by Kitware Robot
109 changed files with 3159 additions and 455 deletions

View File

@@ -284,20 +284,8 @@ This section covers the primary and most widely used comparison types.
Other more specific comparison types are documented in their own separate
sections further below.
String Comparisons
^^^^^^^^^^^^^^^^^^
.. genex:: $<STREQUAL:string1,string2>
``1`` if ``string1`` and ``string2`` are equal, else ``0``.
The comparison is case-sensitive. For a case-insensitive comparison,
combine with a :ref:`string transforming generator expression
<String Transforming Generator Expressions>`. For example, the following
evaluates to ``1`` if ``${foo}`` is any of ``BAR``, ``Bar``, ``bar``, etc.
.. code-block:: cmake
$<STREQUAL:$<UPPER_CASE:${foo}>,BAR>
Numeric Comparisons
^^^^^^^^^^^^^^^^^^^
.. genex:: $<EQUAL:value1,value2>
@@ -330,10 +318,399 @@ Version Comparisons
``1`` if ``v1`` is a version greater than or equal to ``v2``, else ``0``.
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
^^^^^^^^^^^^^^^^^^
The comparisons are case-sensitive. For a case-insensitive comparison,
combine with a :ref:`string transforming generator expression
<String Transforming Generator Expressions>`. For example, the following
evaluates to ``1`` if ``${foo}`` is any of ``BAR``, ``Bar``, ``bar``, etc.
.. code-block:: cmake
$<STREQUAL:$<STRING:TOUPPER,${foo}>,BAR>
.. genex:: $<STREQUAL:string1,string2>
``1`` if ``string1`` and ``string2`` are lexicographically equal, else ``0``.
.. genex:: $<STRLESS:string1,string2>
.. versionadded:: 4.3
``1`` if ``string1`` is lexicographically less than ``string2``, else ``0``.
.. genex:: $<STRGREATER:string1,string2>
.. versionadded:: 4.3
``1`` if ``string1`` is lexicographically greater than ``string2``, else
``0``.
.. genex:: $<STRLESS_EQUAL:string1,string2>
.. versionadded:: 4.3
``1`` if ``string1`` is lexicographically less than or equal to ``string2``,
else ``0``.
.. genex:: $<STRGREATER_EQUAL:string1,string2>
.. versionadded:: 4.3
``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>
@@ -343,10 +720,10 @@ String Transformations
Content of ``string`` converted to upper case.
.. genex:: $<MAKE_C_IDENTIFIER:...>
.. 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

@@ -0,0 +1,5 @@
GenEx-string-comparisons
------------------------
* CMake gains new :ref:`generator expressions
<String Comparisons Generator Expressions>` for string comparisons.

View File

@@ -135,6 +135,8 @@ add_library(
cmCMakePresetsGraphReadJSONPackagePresets.cxx
cmCMakePresetsGraphReadJSONTestPresets.cxx
cmCMakePresetsGraphReadJSONWorkflowPresets.cxx
cmCMakeString.hxx
cmCMakeString.cxx
cmCommandLineArgument.h
cmCommonTargetGenerator.cxx
cmCommonTargetGenerator.h

326
Source/cmCMakeString.cxx Normal file
View File

@@ -0,0 +1,326 @@
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file LICENSE.rst or https://cmake.org/licensing for details. */
#include "cmConfigure.h" // IWYU pragma: keep
#include "cmCMakeString.hxx"
#include <cstdio>
#include <cstdlib>
#include <memory>
#include <stdexcept>
#include <vector>
#include "cmsys/RegularExpression.hxx"
#include "cmCryptoHash.h"
#include "cmGeneratorExpression.h"
#include "cmMakefile.h"
#include "cmPolicies.h"
#include "cmStringAlgorithms.h"
#include "cmStringReplaceHelper.h"
#include "cmSystemTools.h"
#include "cmTimestamp.h"
#include "cmUuid.h"
namespace cm {
bool CMakeString::Compare(CompOperator op, cm::string_view other)
{
switch (op) {
case CompOperator::EQUAL:
return this->String_ == other;
case CompOperator::LESS:
return this->String_ < other;
case CompOperator::LESS_EQUAL:
return this->String_ <= other;
case CompOperator::GREATER:
return this->String_ > other;
case CompOperator::GREATER_EQUAL:
return this->String_ >= other;
default:
return false;
}
}
CMakeString& CMakeString::Replace(std::string const& matchExpression,
std::string const& replaceExpression,
Regex regex, cmMakefile* makefile)
{
if (regex == Regex::Yes) {
if (makefile) {
makefile->ClearMatches();
}
cmStringReplaceHelper replaceHelper(matchExpression, replaceExpression,
makefile);
if (!replaceHelper.IsReplaceExpressionValid()) {
throw std::invalid_argument(replaceHelper.GetError());
}
if (!replaceHelper.IsRegularExpressionValid()) {
throw std::invalid_argument(
cmStrCat("Failed to compile regex \"", matchExpression, '"'));
}
std ::string output;
if (!replaceHelper.Replace(this->String_, output)) {
throw std::runtime_error(replaceHelper.GetError());
}
this->String_ = std::move(output);
} else {
std::string output = this->String_.str();
cmsys::SystemTools::ReplaceString(output, matchExpression,
replaceExpression);
this->String_ = std::move(output);
}
return *this;
};
cmList CMakeString::Match(std::string const& matchExpression,
MatchItems matchItems, cmMakefile* makefile) const
{
if (makefile) {
makefile->ClearMatches();
}
// Compile the regular expression.
cmsys::RegularExpression re;
if (!re.compile(matchExpression)) {
throw std::invalid_argument(
cmStrCat("Failed to compile regex \"", matchExpression, '"'));
}
cmList output;
if (matchItems == MatchItems::Once) {
if (re.find(this->String_.data())) {
if (makefile) {
makefile->StoreMatches(re);
}
output = re.match();
}
} else {
unsigned optAnchor = 0;
if (makefile &&
makefile->GetPolicyStatus(cmPolicies::CMP0186) != cmPolicies::NEW) {
optAnchor = cmsys::RegularExpression::BOL_AT_OFFSET;
}
// Scan through the input for all matches.
std::string::size_type base = 0;
unsigned optNonEmpty = 0;
while (re.find(this->String_.data(), base, optAnchor | optNonEmpty)) {
if (makefile) {
makefile->ClearMatches();
makefile->StoreMatches(re);
}
output.push_back(re.match());
base = re.end();
if (re.start() == this->String_.length()) {
break;
}
if (re.start() == re.end()) {
optNonEmpty = cmsys::RegularExpression::NONEMPTY_AT_OFFSET;
} else {
optNonEmpty = 0;
}
}
}
return output;
}
CMakeString CMakeString::Substring(long begin, long count) const
{
if (begin < 0 || static_cast<size_type>(begin) > this->String_.size()) {
throw std::out_of_range(cmStrCat(
"begin index: ", begin, " is out of range 0 - ", this->String_.size()));
}
if (count < -1) {
throw std::out_of_range(
cmStrCat("end index: ", count, " should be -1 or greater"));
}
return this->String_.substr(static_cast<size_type>(begin),
count == -1 ? npos
: static_cast<size_type>(count));
}
CMakeString& CMakeString::Strip(StripItems stripItems)
{
if (stripItems == StripItems::Space) {
this->String_ = cmTrimWhitespace(this->String_);
} else {
this->String_ = cmGeneratorExpression::Preprocess(
this->String_, cmGeneratorExpression::StripAllGeneratorExpressions);
}
return *this;
}
CMakeString& CMakeString::Repeat(size_type count)
{
switch (this->Size()) {
case 0u:
// Nothing to do for zero length input strings
break;
case 1u:
// NOTE If the string to repeat consists of the only character,
// use the appropriate constructor.
this->String_ = std::string(count, this->String_[0]);
break;
default:
std::string result;
auto size = this->Size();
result.reserve(size * count);
for (auto i = 0u; i < count; ++i) {
result.insert(i * size, this->String_.data(), size);
}
this->String_ = std::move(result);
break;
}
return *this;
}
CMakeString& CMakeString::Quote(QuoteItems)
{
std ::string output;
// Escape all regex special characters
cmStringReplaceHelper replaceHelper("([][()+*^.$?|\\\\])", R"(\\\1)");
if (!replaceHelper.Replace(this->String_, output)) {
throw std::runtime_error(replaceHelper.GetError());
}
this->String_ = std::move(output);
return *this;
}
CMakeString& CMakeString::Hash(cm::string_view hashAlgorithm)
{
std::unique_ptr<cmCryptoHash> hash(cmCryptoHash::New(hashAlgorithm));
if (hash) {
this->String_ = hash->HashString(this->String_);
return *this;
}
throw std::invalid_argument(
cmStrCat(hashAlgorithm, ": invalid hash algorithm."));
}
CMakeString& CMakeString::FromASCII(string_range codes)
{
std::string output;
output.reserve(codes.size());
for (auto const& code : codes) {
try {
auto ch = std::stoi(code);
if (ch > 0 && ch < 256) {
output += static_cast<char>(ch);
} else {
throw std::invalid_argument(
cmStrCat("Character with code ", code, " does not exist."));
}
} catch (...) {
throw std::invalid_argument(
cmStrCat("Character with code ", code, " does not exist."));
}
}
this->String_ = std::move(output);
return *this;
}
CMakeString& CMakeString::ToHexadecimal(cm::string_view str)
{
std::string output(str.size() * 2, ' ');
std::string::size_type hexIndex = 0;
for (auto const& c : str) {
std::snprintf(&output[hexIndex], 3, "%.2x", c & 0xFFu);
hexIndex += 2;
}
this->String_ = output;
return *this;
}
cm::string_view const CMakeString::RandomDefaultAlphabet{
"qwertyuiopasdfghjklzxcvbnm"
"QWERTYUIOPASDFGHJKLZXCVBNM"
"0123456789"
};
bool CMakeString::Seeded = false;
CMakeString& CMakeString::Random(unsigned int seed, std::size_t length,
cm::string_view alphabet)
{
if (alphabet.empty()) {
alphabet = RandomDefaultAlphabet;
}
if (length < 1) {
throw std::out_of_range("Invoked with bad length.");
}
if (!this->Seeded) {
this->Seeded = true;
std::srand(seed);
}
double alphabetSize = static_cast<double>(alphabet.size());
std::vector<char> result;
result.reserve(length + 1);
for (std::size_t i = 0; i < length; i++) {
auto index = static_cast<std::string::size_type>(
alphabetSize * std::rand() / (RAND_MAX + 1.0));
result.push_back(alphabet[index]);
}
result.push_back(0);
this->String_ = result.data();
return *this;
}
CMakeString& CMakeString::Timestamp(cm::string_view format, UTC utc)
{
cmTimestamp timestamp;
this->String_ = timestamp.CurrentTime(format, utc == UTC::Yes);
return *this;
}
CMakeString& CMakeString::UUID(cm::string_view nameSpace, cm::string_view name,
UUIDType type, Case uuidCase)
{
#if !defined(CMAKE_BOOTSTRAP)
cmUuid uuidGenerator;
std::vector<unsigned char> uuidNamespace;
std::string uuid;
if (!uuidGenerator.StringToBinary(nameSpace, uuidNamespace)) {
throw std::invalid_argument("malformed NAMESPACE UUID");
}
if (type == UUIDType::MD5) {
uuid = uuidGenerator.FromMd5(uuidNamespace, name);
} else if (type == UUIDType::SHA1) {
uuid = uuidGenerator.FromSha1(uuidNamespace, name);
}
if (uuid.empty()) {
throw std::runtime_error("generation failed");
}
if (uuidCase == Case::Upper) {
uuid = cmSystemTools::UpperCase(uuid);
}
this->String_ = std::move(uuid);
return *this;
#else
throw std::runtime_error("not available during bootstrap");
#endif
}
}

259
Source/cmCMakeString.hxx Normal file
View File

@@ -0,0 +1,259 @@
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file LICENSE.rst or https://cmake.org/licensing for details. */
#pragma once
#include "cmConfigure.h" // IWYU pragma: keep
#include <cstddef>
#include <string>
#include <utility>
#include <vector>
#include <cm/string_view>
#include "cmList.h"
#include "cmRange.h"
#include "cmString.hxx"
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
#include "cmValue.h"
class cmMakefile;
//
// Class offering various string operations which is used by
// * CMake string() command
// * $<STRING> generator expression
//
namespace cm {
class CMakeString
{
public:
using size_type = cm::String::size_type;
static auto const npos = cm::String::npos;
using string_range = cmRange<std::vector<std::string>::const_iterator>;
CMakeString(std::string const& str)
: String_(cm::String::borrow(str))
{
}
CMakeString(cm::string_view str)
: String_(cm::String::borrow(str))
{
}
CMakeString(cm::String const& str)
: String_(str)
{
}
CMakeString(cm::String&& str)
: String_(std::move(str))
{
}
CMakeString(cmValue value)
: String_(cm::String::borrow(*value))
{
}
CMakeString(string_range range,
cm::string_view separator = cm::string_view{})
: String_(cmJoin(range, separator))
{
}
CMakeString() = default;
CMakeString(CMakeString const&) = default;
CMakeString(CMakeString&&) = default;
CMakeString& operator=(CMakeString const& string)
{
if (this != &string) {
this->String_ = string.String_;
}
return *this;
}
CMakeString& operator=(CMakeString&& string) noexcept
{
if (this != &string) {
this->String_ = std::move(string.String_);
}
return *this;
}
CMakeString& operator=(cm::string_view string)
{
this->String_ = string;
return *this;
}
// conversions
string_view view() const noexcept { return this->String_.view(); }
operator cm::string_view() noexcept { return this->String_.view(); }
operator std::string const&() { return this->String_.str(); }
size_type Size() const { return this->String_.size(); }
size_type Length() const { return this->String_.size(); }
enum class CompOperator
{
EQUAL,
LESS,
LESS_EQUAL,
GREATER,
GREATER_EQUAL
};
bool Compare(CompOperator op, cm::string_view other);
enum class FindFrom
{
Begin,
End
};
size_type Find(cm::string_view substring,
FindFrom from = FindFrom::Begin) const
{
return from == FindFrom::Begin ? this->String_.find(substring)
: this->String_.rfind(substring);
}
enum class Regex
{
No,
Yes
};
// Throw std::invalid_argument if regular expression is invalid
// std::runtime_error if replacement failed
CMakeString& Replace(std::string const& matchExpression,
std::string const& replaceExpression,
Regex regex = Regex::No,
cmMakefile* makefile = nullptr);
enum class MatchItems
{
Once,
All
};
// Throw std::invalid_argument if regular expression is invalid
cmList Match(std::string const& matchExpression,
MatchItems matchItems = MatchItems::Once,
cmMakefile* makefile = nullptr) const;
CMakeString& Append(cm::string_view str)
{
this->String_.append(str);
return *this;
}
CMakeString& Append(string_range range)
{
this->Append(cmJoin(range, {}));
return *this;
}
CMakeString& Prepend(cm::string_view str)
{
this->String_.insert(0, str);
return *this;
}
CMakeString& Prepend(string_range range)
{
this->Prepend(cmJoin(range, {}));
return *this;
}
CMakeString& ToLower()
{
this->String_ = cmSystemTools::LowerCase(this->String_);
return *this;
}
CMakeString& ToLower(cm::string_view str)
{
this->String_ = cmSystemTools::LowerCase(str);
return *this;
}
CMakeString& ToUpper()
{
this->String_ = cmSystemTools::UpperCase(this->String_);
return *this;
}
CMakeString& ToUpper(cm::string_view str)
{
this->String_ = cmSystemTools::UpperCase(str);
return *this;
}
// Throw std::out_of_range if pos or count are outside of the expected range
CMakeString Substring(long pos = 0, long count = -1) const;
enum class StripItems
{
Space,
Genex
};
CMakeString& Strip(StripItems stripItems = StripItems::Space);
// Throw std::runtime_error if quoting string failed
enum class QuoteItems
{
Regex
};
CMakeString& Quote(QuoteItems quoteItems = QuoteItems::Regex);
CMakeString& Repeat(size_type count);
// Throw std::invalid_argument if the hash algorithm is invalid
CMakeString& Hash(cm::string_view hashAlgorithm);
// Throw std::invalid_argument if one of the codes is invalid
CMakeString& FromASCII(string_range codes);
CMakeString& ToHexadecimal() { return this->ToHexadecimal(this->String_); }
CMakeString& ToHexadecimal(cm::string_view str);
CMakeString& MakeCIdentifier()
{
this->String_ = cmSystemTools::MakeCidentifier(this->String_.str());
return *this;
}
CMakeString& MakeCIdentifier(std::string const& str)
{
this->String_ = cmSystemTools::MakeCidentifier(str);
return *this;
}
static cm::string_view const RandomDefaultAlphabet;
// Throw std::invalid_argument if the alphabet is invalid
// std::out_of_range if length is outside valid range
CMakeString& Random(std::size_t length = 5,
cm::string_view alphabet = RandomDefaultAlphabet)
{
return this->Random(cmSystemTools::RandomSeed(), length, alphabet);
}
CMakeString& Random(unsigned int seed, std::size_t length = 5,
cm::string_view alphabet = RandomDefaultAlphabet);
enum class UTC
{
No,
Yes
};
CMakeString& Timestamp(cm::string_view format, UTC utc = UTC::No);
enum class UUIDType
{
MD5,
SHA1
};
enum class Case
{
Lower,
Upper
};
// Throw std::invalid_argument if namespace or the type are invalid
// std::runtime_error if the UUID cannot be generated
CMakeString& UUID(cm::string_view nameSpace, cm::string_view name,
UUIDType type, Case uuidCase = Case::Lower);
private:
static bool Seeded;
cm::String String_;
};
}

View File

@@ -166,7 +166,7 @@ std::string cmGeneratorExpression::StripEmptyListElements(
}
static std::string extractAllGeneratorExpressions(
std::string const& input,
cm::string_view input,
std::map<std::string, std::vector<std::string>>* collected)
{
std::string result;
@@ -176,9 +176,9 @@ static std::string extractAllGeneratorExpressions(
std::stack<char const*> colons; // indices of ":"
while ((pos = input.find("$<", lastPos)) != std::string::npos) {
result += input.substr(lastPos, pos - lastPos);
starts.push(input.c_str() + pos);
starts.push(input.data() + pos);
pos += 2;
char const* c = input.c_str() + pos;
char const* c = input.data() + pos;
char const* const cStart = c;
for (; *c; ++c) {
if (cmGeneratorExpression::StartsWithGeneratorExpression(c)) {
@@ -209,7 +209,7 @@ static std::string extractAllGeneratorExpressions(
}
std::string::size_type const traversed = (c - cStart) + 1;
if (!*c) {
result += "$<" + input.substr(pos, traversed);
result += cmStrCat("$<", input.substr(pos, traversed));
}
pos += traversed;
lastPos = pos;
@@ -220,7 +220,7 @@ static std::string extractAllGeneratorExpressions(
return cmGeneratorExpression::StripEmptyListElements(result);
}
static std::string stripAllGeneratorExpressions(std::string const& input)
static std::string stripAllGeneratorExpressions(cm::string_view input)
{
return extractAllGeneratorExpressions(input, nullptr);
}
@@ -243,7 +243,7 @@ static void prefixItems(std::string const& content, std::string& result,
}
static std::string stripExportInterface(
std::string const& input, cmGeneratorExpression::PreprocessContext context,
cm::string_view input, cmGeneratorExpression::PreprocessContext context,
cm::string_view importPrefix)
{
std::string result;
@@ -282,7 +282,7 @@ static std::string stripExportInterface(
assert(false && "Invalid position found");
}
nestingLevel = 1;
char const* c = input.c_str() + pos;
char const* c = input.data() + pos;
char const* const cStart = c;
for (; *c; ++c) {
if (cmGeneratorExpression::StartsWithGeneratorExpression(c)) {
@@ -300,7 +300,8 @@ static std::string stripExportInterface(
result += input.substr(pos, c - cStart);
} else if (context == cmGeneratorExpression::InstallInterface &&
foundGenex == FoundGenex::InstallInterface) {
std::string const content = input.substr(pos, c - cStart);
std::string const content =
static_cast<std::string>(input.substr(pos, c - cStart));
if (!importPrefix.empty()) {
prefixItems(content, result, importPrefix);
} else {
@@ -390,7 +391,7 @@ void cmGeneratorExpression::Split(std::string const& input,
}
}
std::string cmGeneratorExpression::Preprocess(std::string const& input,
std::string cmGeneratorExpression::Preprocess(cm::string_view input,
PreprocessContext context,
cm::string_view importPrefix)
{

View File

@@ -65,7 +65,7 @@ public:
InstallInterface
};
static std::string Preprocess(std::string const& input,
static std::string Preprocess(cm::string_view input,
PreprocessContext context,
cm::string_view importPrefix = {});

View File

@@ -7,6 +7,7 @@
#include <cerrno>
#include <cstdlib>
#include <cstring>
#include <exception>
#include <functional>
#include <map>
#include <memory>
@@ -26,6 +27,7 @@
#include "cmsys/String.h"
#include "cmCMakePath.h"
#include "cmCMakeString.hxx"
#include "cmComputeLinkInformation.h"
#include "cmGenExContext.h"
#include "cmGenExEvaluation.h"
@@ -265,9 +267,84 @@ static const struct StrEqualNode : public cmGeneratorExpressionNode
GeneratorExpressionContent const* /*content*/,
cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
{
return parameters.front() == parameters[1] ? "1" : "0";
return cm::CMakeString{ parameters.front() }.Compare(
cm::CMakeString::CompOperator::EQUAL, parameters[1])
? "1"
: "0";
}
} strEqualNode;
static const struct StrLessNode : public cmGeneratorExpressionNode
{
StrLessNode() {} // NOLINT(modernize-use-equals-default)
int NumExpectedParameters() const override { return 2; }
std::string Evaluate(
std::vector<std::string> const& parameters,
cm::GenEx::Evaluation* /*eval*/,
GeneratorExpressionContent const* /*content*/,
cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
{
return cm::CMakeString{ parameters.front() }.Compare(
cm::CMakeString::CompOperator::LESS, parameters[1])
? "1"
: "0";
}
} strLessNode;
static const struct StrLessEqualNode : public cmGeneratorExpressionNode
{
StrLessEqualNode() {} // NOLINT(modernize-use-equals-default)
int NumExpectedParameters() const override { return 2; }
std::string Evaluate(
std::vector<std::string> const& parameters,
cm::GenEx::Evaluation* /*eval*/,
GeneratorExpressionContent const* /*content*/,
cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
{
return cm::CMakeString{ parameters.front() }.Compare(
cm::CMakeString::CompOperator::LESS_EQUAL, parameters[1])
? "1"
: "0";
}
} strLessEqualNode;
static const struct StrGreaterNode : public cmGeneratorExpressionNode
{
StrGreaterNode() {} // NOLINT(modernize-use-equals-default)
int NumExpectedParameters() const override { return 2; }
std::string Evaluate(
std::vector<std::string> const& parameters,
cm::GenEx::Evaluation* /*eval*/,
GeneratorExpressionContent const* /*content*/,
cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
{
return cm::CMakeString{ parameters.front() }.Compare(
cm::CMakeString::CompOperator::GREATER, parameters[1])
? "1"
: "0";
}
} strGreaterNode;
static const struct StrGreaterEqualNode : public cmGeneratorExpressionNode
{
StrGreaterEqualNode() {} // NOLINT(modernize-use-equals-default)
int NumExpectedParameters() const override { return 2; }
std::string Evaluate(
std::vector<std::string> const& parameters,
cm::GenEx::Evaluation* /*eval*/,
GeneratorExpressionContent const* /*content*/,
cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override
{
return cm::CMakeString{ parameters.front() }.Compare(
cm::CMakeString::CompOperator::GREATER_EQUAL, parameters[1])
? "1"
: "0";
}
} strGreaterEqualNode;
static const struct EqualNode : public cmGeneratorExpressionNode
{
@@ -706,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,
@@ -1162,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,
@@ -1183,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
@@ -4872,6 +5498,11 @@ cmGeneratorExpressionNode const* cmGeneratorExpressionNode::GetNode(
{ "TARGET_BUNDLE_DIR_NAME", &targetBundleDirNameNode },
{ "TARGET_BUNDLE_CONTENT_DIR", &targetBundleContentDirNode },
{ "STREQUAL", &strEqualNode },
{ "STRLESS", &strLessNode },
{ "STRLESS_EQUAL", &strLessEqualNode },
{ "STRGREATER", &strGreaterNode },
{ "STRGREATER_EQUAL", &strGreaterEqualNode },
{ "STRING", &stringNode },
{ "EQUAL", &equalNode },
{ "IN_LIST", &inListNode },
{ "FILTER", &filterNode },

View File

@@ -425,6 +425,16 @@ public:
r.append(v.data(), v.size());
return *this = std::move(r);
}
template <typename T>
typename std::enable_if<AsStringView<T>::value, String&>::type append(T&& s)
{
string_view v = AsStringView<T>::view(std::forward<T>(s));
std::string r;
r.reserve(this->size() + v.size());
r.assign(this->data(), this->size());
r.append(v.data(), v.size());
return *this = std::move(r);
}
/** Assign to an empty string. */
void clear() { *this = ""_s; }
@@ -432,6 +442,20 @@ public:
/** Insert 'count' copies of 'ch' at position 'index'. */
String& insert(size_type index, size_type count, char ch);
/** Insert into the string using any type that implements the
AsStringView trait. */
template <typename T>
typename std::enable_if<AsStringView<T>::value, String&>::type insert(
size_type index, T&& s)
{
string_view v = AsStringView<T>::view(std::forward<T>(s));
std::string r;
r.reserve(this->size() + v.size());
r.assign(this->data(), this->size());
r.insert(index, v.data(), v.size());
return *this = std::move(r);
}
/** Erase 'count' characters starting at position 'index'. */
String& erase(size_type index = 0, size_type count = npos);

View File

@@ -5,9 +5,9 @@
#include "cmStringCommand.h"
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <exception>
#include <limits>
#include <memory>
#include <stdexcept>
@@ -22,22 +22,14 @@
#include <cm3p/json/value.h>
#include <cm3p/json/writer.h>
#include "cmsys/RegularExpression.hxx"
#include "cmCryptoHash.h"
#include "cmCMakeString.hxx"
#include "cmExecutionStatus.h"
#include "cmGeneratorExpression.h"
#include "cmList.h"
#include "cmMakefile.h"
#include "cmMessageType.h"
#include "cmPolicies.h"
#include "cmRange.h"
#include "cmStringAlgorithms.h"
#include "cmStringReplaceHelper.h"
#include "cmSubcommandTable.h"
#include "cmSystemTools.h"
#include "cmTimestamp.h"
#include "cmUuid.h"
#include "cmValue.h"
namespace {
@@ -62,13 +54,16 @@ bool HandleHashCommand(std::vector<std::string> const& args,
return false;
}
std::unique_ptr<cmCryptoHash> hash(cmCryptoHash::New(args[0]));
if (hash) {
std::string out = hash->HashString(args[2]);
status.GetMakefile().AddDefinition(args[1], out);
cm::CMakeString data{ args[2] };
try {
data.Hash(args[0]);
status.GetMakefile().AddDefinition(args[1], data);
return true;
} catch (std::exception const& e) {
status.SetError(e.what());
return false;
}
return false;
}
bool HandleToUpperLowerCommand(std::vector<std::string> const& args,
@@ -80,16 +75,16 @@ bool HandleToUpperLowerCommand(std::vector<std::string> const& args,
}
std::string const& outvar = args[2];
std::string output;
cm::CMakeString data{ args[1] };
if (toUpper) {
output = cmSystemTools::UpperCase(args[1]);
data.ToUpper();
} else {
output = cmSystemTools::LowerCase(args[1]);
data.ToLower();
}
// Store the output in the provided variable.
status.GetMakefile().AddDefinition(outvar, output);
status.GetMakefile().AddDefinition(outvar, data);
return true;
}
@@ -112,23 +107,17 @@ bool HandleAsciiCommand(std::vector<std::string> const& args,
status.SetError("No output variable specified");
return false;
}
std::string::size_type cc;
std::string const& outvar = args.back();
std::string output;
for (cc = 1; cc < args.size() - 1; cc++) {
int ch = atoi(args[cc].c_str());
if (ch > 0 && ch < 256) {
output += static_cast<char>(ch);
} else {
std::string error =
cmStrCat("Character with code ", args[cc], " does not exist.");
status.SetError(error);
return false;
}
try {
std::string const& outvar = args.back();
cm::CMakeString data;
data.FromASCII(cmMakeRange(args).advance(1).retreat(1));
status.GetMakefile().AddDefinition(outvar, data);
return true;
} catch (std::exception const& e) {
status.SetError(e.what());
return false;
}
// Store the output in the provided variable.
status.GetMakefile().AddDefinition(outvar, output);
return true;
}
bool HandleHexCommand(std::vector<std::string> const& args,
@@ -138,17 +127,13 @@ bool HandleHexCommand(std::vector<std::string> const& args,
status.SetError("Incorrect number of arguments");
return false;
}
auto const& instr = args[1];
auto const& outvar = args[2];
std::string output(instr.size() * 2, ' ');
cm::CMakeString data{ args[1] };
std::string::size_type hexIndex = 0;
for (auto const& c : instr) {
snprintf(&output[hexIndex], 3, "%.2x", c & 0xFFu);
hexIndex += 2;
}
data.ToHexadecimal();
status.GetMakefile().AddDefinition(outvar, output);
status.GetMakefile().AddDefinition(outvar, data);
return true;
}
@@ -239,33 +224,21 @@ bool RegexMatch(std::vector<std::string> const& args,
{
//"STRING(REGEX MATCH <regular_expression> <output variable>
// <input> [<input>...])\n";
std::string const& regex = args[2];
std::string const& outvar = args[3];
try {
std::string const& regex = args[2];
std::string const& outvar = args[3];
cm::CMakeString data{ cmMakeRange(args).advance(4) };
status.GetMakefile().ClearMatches();
// Compile the regular expression.
cmsys::RegularExpression re;
if (!re.compile(regex)) {
std::string e =
"sub-command REGEX, mode MATCH failed to compile regex \"" + regex +
"\".";
status.SetError(e);
auto result = data.Match(regex, cm::CMakeString::MatchItems::Once,
&status.GetMakefile());
// Store the result in the provided variable.
status.GetMakefile().AddDefinition(outvar, result.to_string());
return true;
} catch (std::exception const& e) {
status.SetError(
cmStrCat("sub-command REGEX, mode MATCH: ", e.what(), '.'));
return false;
}
// Concatenate all the last arguments together.
std::string input = cmJoin(cmMakeRange(args).advance(4), std::string());
// Scan through the input for all matches.
std::string output;
if (re.find(input)) {
status.GetMakefile().StoreMatches(re);
output = re.match();
}
// Store the output in the provided variable.
status.GetMakefile().AddDefinition(outvar, output);
return true;
}
bool RegexMatchAll(std::vector<std::string> const& args,
@@ -273,55 +246,21 @@ bool RegexMatchAll(std::vector<std::string> const& args,
{
//"STRING(REGEX MATCHALL <regular_expression> <output variable> <input>
// [<input>...])\n";
std::string const& regex = args[2];
std::string const& outvar = args[3];
try {
std::string const& regex = args[2];
std::string const& outvar = args[3];
cm::CMakeString data{ cmMakeRange(args).advance(4) };
status.GetMakefile().ClearMatches();
// Compile the regular expression.
cmsys::RegularExpression re;
if (!re.compile(regex)) {
std::string e =
"sub-command REGEX, mode MATCHALL failed to compile regex \"" + regex +
"\".";
status.SetError(e);
auto result = data.Match(regex, cm::CMakeString::MatchItems::All,
&status.GetMakefile());
// Store the result in the provided variable.
status.GetMakefile().AddDefinition(outvar, result.to_string());
return true;
} catch (std::exception const& e) {
status.SetError(
cmStrCat("sub-command REGEX, mode MATCHALL: ", e.what(), '.'));
return false;
}
// Concatenate all the last arguments together.
std::string input = cmJoin(cmMakeRange(args).advance(4), std::string());
unsigned optAnchor = 0;
if (status.GetMakefile().GetPolicyStatus(cmPolicies::CMP0186) !=
cmPolicies::NEW) {
optAnchor = cmsys::RegularExpression::BOL_AT_OFFSET;
}
// Scan through the input for all matches.
std::string output;
std::string::size_type base = 0;
unsigned optNonEmpty = 0;
while (re.find(input, base, optAnchor | optNonEmpty)) {
status.GetMakefile().ClearMatches();
status.GetMakefile().StoreMatches(re);
if (!output.empty() || optNonEmpty) {
output += ";";
}
output += re.match();
base = re.end();
if (re.start() == input.length()) {
break;
}
if (re.start() == re.end()) {
optNonEmpty = cmsys::RegularExpression::NONEMPTY_AT_OFFSET;
} else {
optNonEmpty = 0;
}
}
// Store the output in the provided variable.
status.GetMakefile().AddDefinition(outvar, output);
return true;
}
bool RegexReplace(std::vector<std::string> const& args,
@@ -332,38 +271,20 @@ bool RegexReplace(std::vector<std::string> const& args,
std::string const& regex = args[2];
std::string const& replace = args[3];
std::string const& outvar = args[4];
cmStringReplaceHelper replaceHelper(regex, replace, &status.GetMakefile());
if (!replaceHelper.IsReplaceExpressionValid()) {
try {
cm::CMakeString data{ cmMakeRange(args).advance(5) };
data.Replace(regex, replace, cm::CMakeString::Regex::Yes,
&status.GetMakefile());
// Store the result in the provided variable.
status.GetMakefile().AddDefinition(outvar, data);
return true;
} catch (std::exception const& e) {
status.SetError(
"sub-command REGEX, mode REPLACE: " + replaceHelper.GetError() + ".");
cmStrCat("sub-command REGEX, mode REPLACE: ", e.what(), '.'));
return false;
}
status.GetMakefile().ClearMatches();
if (!replaceHelper.IsRegularExpressionValid()) {
std::string e =
"sub-command REGEX, mode REPLACE failed to compile regex \"" + regex +
"\".";
status.SetError(e);
return false;
}
// Concatenate all the last arguments together.
std::string const input =
cmJoin(cmMakeRange(args).advance(5), std::string());
std::string output;
if (!replaceHelper.Replace(input, output)) {
status.SetError(
"sub-command REGEX, mode REPLACE: " + replaceHelper.GetError() + ".");
return false;
}
// Store the output in the provided variable.
status.GetMakefile().AddDefinition(outvar, output);
return true;
}
bool RegexQuote(std::vector<std::string> const& args,
@@ -371,21 +292,19 @@ bool RegexQuote(std::vector<std::string> const& args,
{
//"STRING(REGEX QUOTE <output variable> <input> [<input>...]\n"
std::string const& outvar = args[2];
std::string const input =
cmJoin(cmMakeRange(args).advance(3), std::string());
std::string output;
cm::CMakeString data{ cmMakeRange(args).advance(3) };
// Escape all regex special characters
cmStringReplaceHelper replaceHelper("([][()+*^.$?|\\\\])", R"(\\\1)");
if (!replaceHelper.Replace(input, output)) {
try {
// Escape all regex special characters
data.Quote();
// Store the output in the provided variable.
status.GetMakefile().AddDefinition(outvar, data);
return true;
} catch (std::exception const& e) {
status.SetError(
"sub-command REGEX, mode QUOTE: " + replaceHelper.GetError() + ".");
cmStrCat("sub-command REGEX, mode QUOTE: ", e.what(), '.'));
return false;
}
// Store the output in the provided variable.
status.GetMakefile().AddDefinition(outvar, output);
return true;
}
bool HandleFindCommand(std::vector<std::string> const& args,
@@ -423,19 +342,14 @@ bool HandleFindCommand(std::vector<std::string> const& args,
}
// try to find the character and return its position
size_t pos;
if (!reverseMode) {
pos = sstring.find(schar);
} else {
pos = sstring.rfind(schar);
}
if (std::string::npos != pos) {
status.GetMakefile().AddDefinition(outvar, std::to_string(pos));
return true;
}
auto pos = cm::CMakeString{ sstring }.Find(
schar,
reverseMode ? cm::CMakeString::FindFrom::End
: cm::CMakeString::FindFrom::Begin);
status.GetMakefile().AddDefinition(
outvar, pos != cm::CMakeString::npos ? std::to_string(pos) : "-1");
// the character was not found, but this is not really an error
status.GetMakefile().AddDefinition(outvar, "-1");
return true;
}
@@ -462,25 +376,22 @@ bool HandleCompareCommand(std::vector<std::string> const& args,
std::string const& right = args[3];
std::string const& outvar = args[4];
bool result;
cm::CMakeString::CompOperator op = cm::CMakeString::CompOperator::EQUAL;
if (mode == "LESS") {
result = (left < right);
op = cm::CMakeString::CompOperator::LESS;
} else if (mode == "LESS_EQUAL") {
result = (left <= right);
op = cm::CMakeString::CompOperator::LESS_EQUAL;
} else if (mode == "GREATER") {
result = (left > right);
op = cm::CMakeString::CompOperator::GREATER;
} else if (mode == "GREATER_EQUAL") {
result = (left >= right);
} else if (mode == "EQUAL") {
result = (left == right);
} else // if(mode == "NOTEQUAL")
{
result = !(left == right);
op = cm::CMakeString::CompOperator::GREATER_EQUAL;
}
if (result) {
status.GetMakefile().AddDefinition(outvar, "1");
} else {
status.GetMakefile().AddDefinition(outvar, "0");
result = cm::CMakeString{ left }.Compare(op, right);
if (mode == "NOTEQUAL") {
result = !result;
}
status.GetMakefile().AddDefinition(outvar, result ? "1" : "0");
return true;
}
std::string e = "sub-command COMPARE does not recognize mode " + mode;
@@ -496,17 +407,19 @@ bool HandleReplaceCommand(std::vector<std::string> const& args,
return false;
}
std::string const& matchExpression = args[1];
std::string const& replaceExpression = args[2];
std::string const& variableName = args[3];
try {
std::string const& matchExpression = args[1];
std::string const& replaceExpression = args[2];
std::string const& variableName = args[3];
cm::CMakeString data{ cmMakeRange(args).advance(4) };
std::string input = cmJoin(cmMakeRange(args).advance(4), std::string());
cmsys::SystemTools::ReplaceString(input, matchExpression.c_str(),
replaceExpression.c_str());
status.GetMakefile().AddDefinition(variableName, input);
return true;
data.Replace(matchExpression, replaceExpression);
status.GetMakefile().AddDefinition(variableName, data);
return true;
} catch (std::exception const& e) {
status.SetError(cmStrCat("sub-command REPLACE: ", e.what(), '.'));
return false;
}
}
bool HandleSubstringCommand(std::vector<std::string> const& args,
@@ -517,25 +430,19 @@ bool HandleSubstringCommand(std::vector<std::string> const& args,
return false;
}
std::string const& stringValue = args[1];
int begin = atoi(args[2].c_str());
int end = atoi(args[3].c_str());
std::string const& variableName = args[4];
try {
std::string const& stringValue = args[1];
int begin = atoi(args[2].c_str());
int end = atoi(args[3].c_str());
std::string const& variableName = args[4];
size_t stringLength = stringValue.size();
int intStringLength = static_cast<int>(stringLength);
if (begin < 0 || begin > intStringLength) {
status.SetError(
cmStrCat("begin index: ", begin, " is out of range 0 - ", stringLength));
cm::CMakeString data{ stringValue };
status.GetMakefile().AddDefinition(variableName,
data.Substring(begin, end));
} catch (std::exception const& e) {
status.SetError(e.what());
return false;
}
if (end < -1) {
status.SetError(cmStrCat("end index: ", end, " should be -1 or greater"));
return false;
}
status.GetMakefile().AddDefinition(variableName,
stringValue.substr(begin, end));
return true;
}
@@ -550,11 +457,8 @@ bool HandleLengthCommand(std::vector<std::string> const& args,
std::string const& stringValue = args[1];
std::string const& variableName = args[2];
size_t length = stringValue.size();
char buffer[1024];
snprintf(buffer, sizeof(buffer), "%d", static_cast<int>(length));
status.GetMakefile().AddDefinition(variableName, buffer);
status.GetMakefile().AddDefinition(
variableName, std::to_string(cm::CMakeString{ stringValue }.Length()));
return true;
}
@@ -572,12 +476,10 @@ bool HandleAppendCommand(std::vector<std::string> const& args,
}
auto const& variableName = args[1];
cm::CMakeString data{ status.GetMakefile().GetDefinition(variableName) };
cm::string_view oldView{ status.GetMakefile().GetSafeDefinition(
variableName) };
auto const newValue = cmJoin(cmMakeRange(args).advance(2), {}, oldView);
status.GetMakefile().AddDefinition(variableName, newValue);
data.Append(cmMakeRange(args).advance(2));
status.GetMakefile().AddDefinition(variableName, data);
return true;
}
@@ -596,13 +498,10 @@ bool HandlePrependCommand(std::vector<std::string> const& args,
}
std::string const& variable = args[1];
cm::CMakeString data{ status.GetMakefile().GetDefinition(variable) };
std::string value = cmJoin(cmMakeRange(args).advance(2), std::string());
cmValue oldValue = status.GetMakefile().GetDefinition(variable);
if (oldValue) {
value += *oldValue;
}
status.GetMakefile().AddDefinition(variable, value);
data.Prepend(cmMakeRange(args).advance(2));
status.GetMakefile().AddDefinition(variable, data);
return true;
}
@@ -634,9 +533,9 @@ bool joinImpl(std::vector<std::string> const& args, std::string const& glue,
std::string const& variableName = args[varIdx];
// NOTE Items to concat/join placed right after the variable for
// both `CONCAT` and `JOIN` sub-commands.
std::string value = cmJoin(cmMakeRange(args).advance(varIdx + 1), glue);
cm::CMakeString data{ cmMakeRange(args).advance(varIdx + 1), glue };
makefile.AddDefinition(variableName, value);
makefile.AddDefinition(variableName, data);
return true;
}
@@ -652,7 +551,7 @@ bool HandleMakeCIdentifierCommand(std::vector<std::string> const& args,
std::string const& variableName = args[2];
status.GetMakefile().AddDefinition(variableName,
cmSystemTools::MakeCidentifier(input));
cm::CMakeString{}.MakeCIdentifier(input));
return true;
}
@@ -664,14 +563,11 @@ bool HandleGenexStripCommand(std::vector<std::string> const& args,
return false;
}
std::string const& input = args[1];
std::string result = cmGeneratorExpression::Preprocess(
input, cmGeneratorExpression::StripAllGeneratorExpressions);
cm::CMakeString data{ args[1] };
std::string const& variableName = args[2];
status.GetMakefile().AddDefinition(variableName, result);
status.GetMakefile().AddDefinition(
variableName, data.Strip(cm::CMakeString::StripItems::Genex));
return true;
}
@@ -683,36 +579,10 @@ bool HandleStripCommand(std::vector<std::string> const& args,
return false;
}
std::string const& stringValue = args[1];
cm::CMakeString data{ args[1] };
std::string const& variableName = args[2];
size_t inStringLength = stringValue.size();
size_t startPos = inStringLength + 1;
size_t endPos = 0;
char const* ptr = stringValue.c_str();
size_t cc;
for (cc = 0; cc < inStringLength; ++cc) {
if (!cmIsSpace(*ptr)) {
if (startPos > inStringLength) {
startPos = cc;
}
endPos = cc;
}
++ptr;
}
size_t outLength = 0;
// if the input string didn't contain any non-space characters, return
// an empty string
if (startPos > inStringLength) {
outLength = 0;
startPos = 0;
} else {
outLength = endPos - startPos + 1;
}
status.GetMakefile().AddDefinition(variableName,
stringValue.substr(startPos, outLength));
status.GetMakefile().AddDefinition(variableName, data.Strip());
return true;
}
@@ -744,30 +614,12 @@ bool HandleRepeatCommand(std::vector<std::string> const& args,
return true;
}
auto const& stringValue = args[ArgPos::VALUE];
cm::CMakeString data{ args[ArgPos::VALUE] };
data.Repeat(times);
auto const& variableName = args[ArgPos::OUTPUT_VARIABLE];
auto const inStringLength = stringValue.size();
std::string result;
switch (inStringLength) {
case 0u:
// Nothing to do for zero length input strings
break;
case 1u:
// NOTE If the string to repeat consists of the only character,
// use the appropriate constructor.
result = std::string(times, stringValue[0]);
break;
default:
result = std::string(inStringLength * times, char{});
for (auto i = 0u; i < times; ++i) {
std::copy(cm::cbegin(stringValue), cm::cend(stringValue),
&result[i * inStringLength]);
}
break;
}
makefile.AddDefinition(variableName, result);
makefile.AddDefinition(variableName, data);
return true;
}
@@ -779,14 +631,10 @@ bool HandleRandomCommand(std::vector<std::string> const& args,
return false;
}
static bool seeded = false;
int length = 5;
cm::string_view alphabet;
bool force_seed = false;
unsigned int seed = 0;
int length = 5;
char const cmStringCommandDefaultAlphabet[] = "qwertyuiopasdfghjklzxcvbnm"
"QWERTYUIOPASDFGHJKLZXCVBNM"
"0123456789";
std::string alphabet;
if (args.size() > 3) {
size_t i = 1;
@@ -806,37 +654,22 @@ bool HandleRandomCommand(std::vector<std::string> const& args,
}
}
}
if (alphabet.empty()) {
alphabet = cmStringCommandDefaultAlphabet;
}
double sizeofAlphabet = static_cast<double>(alphabet.size());
if (sizeofAlphabet < 1) {
status.SetError("sub-command RANDOM invoked with bad alphabet.");
try {
cm::CMakeString data;
std::string const& variableName = args.back();
if (force_seed) {
data.Random(seed, length, alphabet);
} else {
data.Random(length, alphabet);
}
status.GetMakefile().AddDefinition(variableName, data);
return true;
} catch (std::exception const& e) {
status.SetError(cmStrCat("sub-command RANDOM: ", e.what(), '.'));
return false;
}
if (length < 1) {
status.SetError("sub-command RANDOM invoked with bad length.");
return false;
}
std::string const& variableName = args.back();
std::vector<char> result;
if (!seeded || force_seed) {
seeded = true;
srand(force_seed ? seed : cmSystemTools::RandomSeed());
}
char const* alphaPtr = alphabet.c_str();
for (int cc = 0; cc < length; cc++) {
int idx = static_cast<int>(sizeofAlphabet * rand() / (RAND_MAX + 1.0));
result.push_back(*(alphaPtr + idx));
}
result.push_back(0);
status.GetMakefile().AddDefinition(variableName, result.data());
return true;
}
bool HandleTimestampCommand(std::vector<std::string> const& args,
@@ -855,26 +688,28 @@ bool HandleTimestampCommand(std::vector<std::string> const& args,
std::string const& outputVariable = args[argsIndex++];
std::string formatString;
cm::string_view formatString;
if (args.size() > argsIndex && args[argsIndex] != "UTC") {
formatString = args[argsIndex++];
}
bool utcFlag = false;
cm::CMakeString::UTC utcFlag = cm::CMakeString::UTC::No;
if (args.size() > argsIndex) {
if (args[argsIndex] == "UTC") {
utcFlag = true;
utcFlag = cm::CMakeString::UTC::Yes;
} else {
std::string e = " TIMESTAMP sub-command does not recognize option " +
args[argsIndex] + ".";
std::string e =
cmStrCat(" TIMESTAMP sub-command does not recognize option ",
args[argsIndex], '.');
status.SetError(e);
return false;
}
}
cmTimestamp timestamp;
std::string result = timestamp.CurrentTime(formatString, utcFlag);
status.GetMakefile().AddDefinition(outputVariable, result);
cm::CMakeString data;
status.GetMakefile().AddDefinition(outputVariable,
data.Timestamp(formatString, utcFlag));
return true;
}
@@ -892,10 +727,10 @@ bool HandleUuidCommand(std::vector<std::string> const& args,
std::string const& outputVariable = args[argsIndex++];
std::string uuidNamespaceString;
std::string uuidName;
std::string uuidType;
bool uuidUpperCase = false;
cm::string_view uuidNamespaceString;
cm::string_view uuidName;
cm::CMakeString::UUIDType uuidType = cm::CMakeString::UUIDType::MD5;
cm::CMakeString::Case uuidCase = cm::CMakeString::Case::Lower;
while (args.size() > argsIndex) {
if (args[argsIndex] == "NAMESPACE") {
@@ -918,50 +753,39 @@ bool HandleUuidCommand(std::vector<std::string> const& args,
status.SetError("UUID sub-command, TYPE requires a value.");
return false;
}
uuidType = args[argsIndex++];
if (args[argsIndex] == "MD5") {
uuidType = cm::CMakeString::UUIDType::MD5;
} else if (args[argsIndex] == "SHA1") {
uuidType = cm::CMakeString::UUIDType::SHA1;
} else {
status.SetError(
cmStrCat("UUID sub-command, unknown TYPE '", args[argsIndex], "'."));
return false;
}
argsIndex++;
} else if (args[argsIndex] == "UPPER") {
++argsIndex;
uuidUpperCase = true;
uuidCase = cm::CMakeString::Case::Upper;
} else {
std::string e =
"UUID sub-command does not recognize option " + args[argsIndex] + ".";
std::string e = cmStrCat("UUID sub-command does not recognize option ",
args[argsIndex], '.');
status.SetError(e);
return false;
}
}
std::string uuid;
cmUuid uuidGenerator;
try {
cm::CMakeString data;
std::vector<unsigned char> uuidNamespace;
if (!uuidGenerator.StringToBinary(uuidNamespaceString, uuidNamespace)) {
status.SetError("UUID sub-command, malformed NAMESPACE UUID.");
data.UUID(uuidNamespaceString, uuidName, uuidType, uuidCase);
status.GetMakefile().AddDefinition(outputVariable, data);
return true;
} catch (std::exception const& e) {
status.SetError(cmStrCat("UUID sub-command, ", e.what(), '.'));
return false;
}
if (uuidType == "MD5") {
uuid = uuidGenerator.FromMd5(uuidNamespace, uuidName);
} else if (uuidType == "SHA1") {
uuid = uuidGenerator.FromSha1(uuidNamespace, uuidName);
} else {
std::string e = "UUID sub-command, unknown TYPE '" + uuidType + "'.";
status.SetError(e);
return false;
}
if (uuid.empty()) {
status.SetError("UUID sub-command, generation failed.");
return false;
}
if (uuidUpperCase) {
uuid = cmSystemTools::UpperCase(uuid);
}
status.GetMakefile().AddDefinition(outputVariable, uuid);
return true;
#else
status.SetError(cmStrCat(args[0], " not available during bootstrap"));
status.SetError("UUID sub-command not available during bootstrap.");
return false;
#endif
}

View File

@@ -20,8 +20,7 @@ cmStringReplaceHelper::cmStringReplaceHelper(std::string const& regex,
this->ParseReplaceExpression();
}
bool cmStringReplaceHelper::Replace(std::string const& input,
std::string& output)
bool cmStringReplaceHelper::Replace(cm::string_view input, std::string& output)
{
output.clear();
@@ -36,7 +35,7 @@ bool cmStringReplaceHelper::Replace(std::string const& input,
auto& re = this->RegularExpression;
std::string::size_type base = 0;
unsigned optNonEmpty = 0;
while (re.find(input, base, optAnchor | optNonEmpty)) {
while (re.find(input.data(), base, optAnchor | optNonEmpty)) {
if (this->Makefile) {
this->Makefile->ClearMatches();
this->Makefile->StoreMatches(re);

View File

@@ -6,6 +6,8 @@
#include <utility>
#include <vector>
#include <cm/string_view>
#include "cmsys/RegularExpression.hxx"
class cmMakefile;
@@ -25,7 +27,7 @@ public:
return this->ValidReplaceExpression;
}
bool Replace(std::string const& input, std::string& output);
bool Replace(cm::string_view input, std::string& output);
std::string const& GetError() { return this->ErrorString; }

View File

@@ -36,7 +36,7 @@
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
std::string cmTimestamp::CurrentTime(std::string const& formatString,
std::string cmTimestamp::CurrentTime(cm::string_view formatString,
bool utcFlag) const
{
// get current time with microsecond resolution
@@ -63,11 +63,12 @@ std::string cmTimestamp::CurrentTime(std::string const& formatString,
}
return this->CreateTimestampFromTimeT(currentTimeT, microseconds,
formatString, utcFlag);
static_cast<std::string>(formatString),
utcFlag);
}
std::string cmTimestamp::FileModificationTime(char const* path,
std::string const& formatString,
cm::string_view formatString,
bool utcFlag) const
{
std::string real_path =
@@ -89,8 +90,8 @@ std::string cmTimestamp::FileModificationTime(char const* path,
}
uv_fs_req_cleanup(&req);
return this->CreateTimestampFromTimeT(mtime, microseconds, formatString,
utcFlag);
return this->CreateTimestampFromTimeT(
mtime, microseconds, static_cast<std::string>(formatString), utcFlag);
}
std::string cmTimestamp::CreateTimestampFromTimeT(time_t timeT,

View File

@@ -8,6 +8,8 @@
#include <ctime>
#include <string>
#include <cm/string_view>
/** \class cmTimestamp
* \brief Utility class to generate string representation of a timestamp
*
@@ -15,10 +17,10 @@
class cmTimestamp
{
public:
std::string CurrentTime(std::string const& formatString, bool utcFlag) const;
std::string CurrentTime(cm::string_view formatString, bool utcFlag) const;
std::string FileModificationTime(char const* path,
std::string const& formatString,
cm::string_view formatString,
bool utcFlag) const;
std::string CreateTimestampFromTimeT(time_t timeT, std::string formatString,

View File

@@ -10,7 +10,7 @@
static std::array<int, 5> const kUuidGroups = { { 4, 2, 2, 2, 6 } };
std::string cmUuid::FromMd5(std::vector<unsigned char> const& uuidNamespace,
std::string const& name) const
cm::string_view name) const
{
std::vector<unsigned char> hashInput;
this->CreateHashInput(uuidNamespace, name, hashInput);
@@ -24,7 +24,7 @@ std::string cmUuid::FromMd5(std::vector<unsigned char> const& uuidNamespace,
}
std::string cmUuid::FromSha1(std::vector<unsigned char> const& uuidNamespace,
std::string const& name) const
cm::string_view name) const
{
std::vector<unsigned char> hashInput;
this->CreateHashInput(uuidNamespace, name, hashInput);
@@ -38,7 +38,7 @@ std::string cmUuid::FromSha1(std::vector<unsigned char> const& uuidNamespace,
}
void cmUuid::CreateHashInput(std::vector<unsigned char> const& uuidNamespace,
std::string const& name,
cm::string_view name,
std::vector<unsigned char>& output) const
{
output = uuidNamespace;
@@ -46,7 +46,7 @@ void cmUuid::CreateHashInput(std::vector<unsigned char> const& uuidNamespace,
if (!name.empty()) {
output.resize(output.size() + name.size());
memcpy(output.data() + uuidNamespace.size(), name.c_str(), name.size());
memcpy(output.data() + uuidNamespace.size(), name.data(), name.size());
}
}
@@ -67,7 +67,7 @@ std::string cmUuid::FromDigest(unsigned char const* digest,
return this->BinaryToString(uuid);
}
bool cmUuid::StringToBinary(std::string const& input,
bool cmUuid::StringToBinary(cm::string_view input,
std::vector<unsigned char>& output) const
{
output.clear();
@@ -126,7 +126,7 @@ std::string cmUuid::ByteToHex(unsigned char inputByte) const
return result;
}
bool cmUuid::StringToBinaryImpl(std::string const& input,
bool cmUuid::StringToBinaryImpl(cm::string_view input,
std::vector<unsigned char>& output) const
{
if (input.size() % 2) {

View File

@@ -7,6 +7,8 @@
#include <string>
#include <vector>
#include <cm/string_view>
/** \class cmUuid
* \brief Utility class to generate UUIDs as defined by RFC4122
*
@@ -15,25 +17,25 @@ class cmUuid
{
public:
std::string FromMd5(std::vector<unsigned char> const& uuidNamespace,
std::string const& name) const;
cm::string_view name) const;
std::string FromSha1(std::vector<unsigned char> const& uuidNamespace,
std::string const& name) const;
cm::string_view name) const;
bool StringToBinary(std::string const& input,
bool StringToBinary(cm::string_view input,
std::vector<unsigned char>& output) const;
private:
std::string ByteToHex(unsigned char byte) const;
void CreateHashInput(std::vector<unsigned char> const& uuidNamespace,
std::string const& name,
cm::string_view name,
std::vector<unsigned char>& output) const;
std::string FromDigest(unsigned char const* digest,
unsigned char version) const;
bool StringToBinaryImpl(std::string const& input,
bool StringToBinaryImpl(cm::string_view input,
std::vector<unsigned char>& output) const;
std::string BinaryToString(unsigned char const* input) const;

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}>")

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,40 @@
CMake Error at BadStrGreater.cmake:1 \(add_custom_target\):
Error evaluating generator expression:
\$<STRGREATER>
\$<STRGREATER> expression requires 2 comma separated parameters, but got 0
instead.
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)
+CMake Error at BadStrGreater.cmake:1 \(add_custom_target\):
Error evaluating generator expression:
\$<STRGREATER:>
\$<STRGREATER> expression requires 2 comma separated parameters, but got 1
instead.
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)
+
CMake Error at BadStrGreater.cmake:1 \(add_custom_target\):
Error evaluating generator expression:
\$<STRGREATER:,,>
\$<STRGREATER> expression requires 2 comma separated parameters, but got 3
instead.
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)
+
CMake Error at BadStrGreater.cmake:1 \(add_custom_target\):
Error evaluating generator expression:
\$<STRGREATER:something,,>
\$<STRGREATER> expression requires 2 comma separated parameters, but got 3
instead.
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)
+
CMake Generate step failed\. Build files cannot be regenerated correctly\.$

View File

@@ -0,0 +1,6 @@
add_custom_target(check ALL COMMAND check
$<STRGREATER>
$<STRGREATER:>
$<STRGREATER:,,>
$<STRGREATER:something,,>
VERBATIM)

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,40 @@
CMake Error at BadStrGreaterEqual.cmake:1 \(add_custom_target\):
Error evaluating generator expression:
\$<STRGREATER_EQUAL>
\$<STRGREATER_EQUAL> expression requires 2 comma separated parameters, but
got 0 instead.
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)
+CMake Error at BadStrGreaterEqual.cmake:1 \(add_custom_target\):
Error evaluating generator expression:
\$<STRGREATER_EQUAL:>
\$<STRGREATER_EQUAL> expression requires 2 comma separated parameters, but
got 1 instead.
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)
+
CMake Error at BadStrGreaterEqual.cmake:1 \(add_custom_target\):
Error evaluating generator expression:
\$<STRGREATER_EQUAL:,,>
\$<STRGREATER_EQUAL> expression requires 2 comma separated parameters, but
got 3 instead.
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)
+
CMake Error at BadStrGreaterEqual.cmake:1 \(add_custom_target\):
Error evaluating generator expression:
\$<STRGREATER_EQUAL:something,,>
\$<STRGREATER_EQUAL> expression requires 2 comma separated parameters, but
got 3 instead.
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)
+
CMake Generate step failed\. Build files cannot be regenerated correctly\.$

View File

@@ -0,0 +1,6 @@
add_custom_target(check ALL COMMAND check
$<STRGREATER_EQUAL>
$<STRGREATER_EQUAL:>
$<STRGREATER_EQUAL:,,>
$<STRGREATER_EQUAL:something,,>
VERBATIM)

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,40 @@
CMake Error at BadStrLess.cmake:1 \(add_custom_target\):
Error evaluating generator expression:
\$<STRLESS>
\$<STRLESS> expression requires 2 comma separated parameters, but got 0
instead.
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)
+CMake Error at BadStrLess.cmake:1 \(add_custom_target\):
Error evaluating generator expression:
\$<STRLESS:>
\$<STRLESS> expression requires 2 comma separated parameters, but got 1
instead.
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)
+
CMake Error at BadStrLess.cmake:1 \(add_custom_target\):
Error evaluating generator expression:
\$<STRLESS:,,>
\$<STRLESS> expression requires 2 comma separated parameters, but got 3
instead.
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)
+
CMake Error at BadStrLess.cmake:1 \(add_custom_target\):
Error evaluating generator expression:
\$<STRLESS:something,,>
\$<STRLESS> expression requires 2 comma separated parameters, but got 3
instead.
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)
+
CMake Generate step failed\. Build files cannot be regenerated correctly\.$

View File

@@ -0,0 +1,6 @@
add_custom_target(check ALL COMMAND check
$<STRLESS>
$<STRLESS:>
$<STRLESS:,,>
$<STRLESS:something,,>
VERBATIM)

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,40 @@
CMake Error at BadStrLessEqual.cmake:1 \(add_custom_target\):
Error evaluating generator expression:
\$<STRLESS_EQUAL>
\$<STRLESS_EQUAL> expression requires 2 comma separated parameters, but got
0 instead.
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)
+CMake Error at BadStrLessEqual.cmake:1 \(add_custom_target\):
Error evaluating generator expression:
\$<STRLESS_EQUAL:>
\$<STRLESS_EQUAL> expression requires 2 comma separated parameters, but got
1 instead.
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)
+
CMake Error at BadStrLessEqual.cmake:1 \(add_custom_target\):
Error evaluating generator expression:
\$<STRLESS_EQUAL:,,>
\$<STRLESS_EQUAL> expression requires 2 comma separated parameters, but got
3 instead.
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)
+
CMake Error at BadStrLessEqual.cmake:1 \(add_custom_target\):
Error evaluating generator expression:
\$<STRLESS_EQUAL:something,,>
\$<STRLESS_EQUAL> expression requires 2 comma separated parameters, but got
3 instead.
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)
+
CMake Generate step failed\. Build files cannot be regenerated correctly\.$

View File

@@ -0,0 +1,6 @@
add_custom_target(check ALL COMMAND check
$<STRLESS_EQUAL>
$<STRLESS_EQUAL:>
$<STRLESS_EQUAL:,,>
$<STRLESS_EQUAL:something,,>
VERBATIM)

View File

@@ -7,6 +7,15 @@ run_cmake(BadOR)
run_cmake(BadAND)
run_cmake(BadNOT)
run_cmake(BadStrEqual)
run_cmake(BadStrLess)
run_cmake(BadStrLessEqual)
run_cmake(BadStrGreater)
run_cmake(BadStrGreaterEqual)
run_cmake(STREQUAL)
run_cmake(STRLESS)
run_cmake(STRLESS_EQUAL)
run_cmake(STRGREATER)
run_cmake(STRGREATER_EQUAL)
run_cmake(BadZero)
run_cmake(BadTargetName)
run_cmake(BadTargetTypeInterface)

View File

@@ -0,0 +1,6 @@
file(READ "${RunCMake_TEST_BINARY_DIR}/STREQUAL-generated.txt" content)
set(expected "1:0")
if(NOT content STREQUAL expected)
set(RunCMake_TEST_FAILED "$<STREQUAL>: actual content:\n [[${content}]]\nbut expected:\n [[${expected}]]")
endif()

View File

@@ -0,0 +1,3 @@
cmake_policy(VERSION 4.2)
file(GENERATE OUTPUT "STREQUAL-generated.txt" CONTENT "$<STREQUAL:AA,AA>:$<STREQUAL:AA,BB>")

Some files were not shown because too many files have changed in this diff Show More