introduce cm::CMakeString class as helper for string() command

This class will be used, as helper for:
* string() command
* future $<STRING> generator expression
This commit is contained in:
Marc Chevrier
2025-10-06 19:18:38 +02:00
parent 34c0c7754f
commit dab5e6ebb1
5 changed files with 769 additions and 357 deletions
+2
View File
@@ -135,6 +135,8 @@ add_library(
cmCMakePresetsGraphReadJSONPackagePresets.cxx cmCMakePresetsGraphReadJSONPackagePresets.cxx
cmCMakePresetsGraphReadJSONTestPresets.cxx cmCMakePresetsGraphReadJSONTestPresets.cxx
cmCMakePresetsGraphReadJSONWorkflowPresets.cxx cmCMakePresetsGraphReadJSONWorkflowPresets.cxx
cmCMakeString.hxx
cmCMakeString.cxx
cmCommandLineArgument.h cmCommandLineArgument.h
cmCommonTargetGenerator.cxx cmCommonTargetGenerator.cxx
cmCommonTargetGenerator.h cmCommonTargetGenerator.h
+326
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
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_;
};
}
+181 -357
View File
@@ -5,9 +5,9 @@
#include "cmStringCommand.h" #include "cmStringCommand.h"
#include <algorithm>
#include <cstdio> #include <cstdio>
#include <cstdlib> #include <cstdlib>
#include <exception>
#include <limits> #include <limits>
#include <memory> #include <memory>
#include <stdexcept> #include <stdexcept>
@@ -22,22 +22,14 @@
#include <cm3p/json/value.h> #include <cm3p/json/value.h>
#include <cm3p/json/writer.h> #include <cm3p/json/writer.h>
#include "cmsys/RegularExpression.hxx" #include "cmCMakeString.hxx"
#include "cmCryptoHash.h"
#include "cmExecutionStatus.h" #include "cmExecutionStatus.h"
#include "cmGeneratorExpression.h" #include "cmList.h"
#include "cmMakefile.h" #include "cmMakefile.h"
#include "cmMessageType.h" #include "cmMessageType.h"
#include "cmPolicies.h"
#include "cmRange.h" #include "cmRange.h"
#include "cmStringAlgorithms.h" #include "cmStringAlgorithms.h"
#include "cmStringReplaceHelper.h"
#include "cmSubcommandTable.h" #include "cmSubcommandTable.h"
#include "cmSystemTools.h"
#include "cmTimestamp.h"
#include "cmUuid.h"
#include "cmValue.h"
namespace { namespace {
@@ -62,13 +54,16 @@ bool HandleHashCommand(std::vector<std::string> const& args,
return false; return false;
} }
std::unique_ptr<cmCryptoHash> hash(cmCryptoHash::New(args[0])); cm::CMakeString data{ args[2] };
if (hash) {
std::string out = hash->HashString(args[2]); try {
status.GetMakefile().AddDefinition(args[1], out); data.Hash(args[0]);
status.GetMakefile().AddDefinition(args[1], data);
return true; return true;
} catch (std::exception const& e) {
status.SetError(e.what());
return false;
} }
return false;
} }
bool HandleToUpperLowerCommand(std::vector<std::string> const& args, 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 const& outvar = args[2];
std::string output; cm::CMakeString data{ args[1] };
if (toUpper) { if (toUpper) {
output = cmSystemTools::UpperCase(args[1]); data.ToUpper();
} else { } else {
output = cmSystemTools::LowerCase(args[1]); data.ToLower();
} }
// Store the output in the provided variable. // Store the output in the provided variable.
status.GetMakefile().AddDefinition(outvar, output); status.GetMakefile().AddDefinition(outvar, data);
return true; return true;
} }
@@ -112,23 +107,17 @@ bool HandleAsciiCommand(std::vector<std::string> const& args,
status.SetError("No output variable specified"); status.SetError("No output variable specified");
return false; return false;
} }
std::string::size_type cc;
std::string const& outvar = args.back(); try {
std::string output; std::string const& outvar = args.back();
for (cc = 1; cc < args.size() - 1; cc++) { cm::CMakeString data;
int ch = atoi(args[cc].c_str()); data.FromASCII(cmMakeRange(args).advance(1).retreat(1));
if (ch > 0 && ch < 256) { status.GetMakefile().AddDefinition(outvar, data);
output += static_cast<char>(ch); return true;
} else { } catch (std::exception const& e) {
std::string error = status.SetError(e.what());
cmStrCat("Character with code ", args[cc], " does not exist."); return false;
status.SetError(error);
return false;
}
} }
// Store the output in the provided variable.
status.GetMakefile().AddDefinition(outvar, output);
return true;
} }
bool HandleHexCommand(std::vector<std::string> const& args, 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"); status.SetError("Incorrect number of arguments");
return false; return false;
} }
auto const& instr = args[1];
auto const& outvar = args[2]; auto const& outvar = args[2];
std::string output(instr.size() * 2, ' '); cm::CMakeString data{ args[1] };
std::string::size_type hexIndex = 0; data.ToHexadecimal();
for (auto const& c : instr) {
snprintf(&output[hexIndex], 3, "%.2x", c & 0xFFu);
hexIndex += 2;
}
status.GetMakefile().AddDefinition(outvar, output); status.GetMakefile().AddDefinition(outvar, data);
return true; return true;
} }
@@ -239,33 +224,21 @@ bool RegexMatch(std::vector<std::string> const& args,
{ {
//"STRING(REGEX MATCH <regular_expression> <output variable> //"STRING(REGEX MATCH <regular_expression> <output variable>
// <input> [<input>...])\n"; // <input> [<input>...])\n";
std::string const& regex = args[2]; try {
std::string const& outvar = args[3]; std::string const& regex = args[2];
std::string const& outvar = args[3];
cm::CMakeString data{ cmMakeRange(args).advance(4) };
status.GetMakefile().ClearMatches(); auto result = data.Match(regex, cm::CMakeString::MatchItems::Once,
// Compile the regular expression. &status.GetMakefile());
cmsys::RegularExpression re; // Store the result in the provided variable.
if (!re.compile(regex)) { status.GetMakefile().AddDefinition(outvar, result.to_string());
std::string e = return true;
"sub-command REGEX, mode MATCH failed to compile regex \"" + regex + } catch (std::exception const& e) {
"\"."; status.SetError(
status.SetError(e); cmStrCat("sub-command REGEX, mode MATCH: ", e.what(), '.'));
return false; 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, 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> //"STRING(REGEX MATCHALL <regular_expression> <output variable> <input>
// [<input>...])\n"; // [<input>...])\n";
std::string const& regex = args[2]; try {
std::string const& outvar = args[3]; std::string const& regex = args[2];
std::string const& outvar = args[3];
cm::CMakeString data{ cmMakeRange(args).advance(4) };
status.GetMakefile().ClearMatches(); auto result = data.Match(regex, cm::CMakeString::MatchItems::All,
// Compile the regular expression. &status.GetMakefile());
cmsys::RegularExpression re; // Store the result in the provided variable.
if (!re.compile(regex)) { status.GetMakefile().AddDefinition(outvar, result.to_string());
std::string e = return true;
"sub-command REGEX, mode MATCHALL failed to compile regex \"" + regex + } catch (std::exception const& e) {
"\"."; status.SetError(
status.SetError(e); cmStrCat("sub-command REGEX, mode MATCHALL: ", e.what(), '.'));
return false; 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, 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& regex = args[2];
std::string const& replace = args[3]; std::string const& replace = args[3];
std::string const& outvar = args[4]; 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( status.SetError(
"sub-command REGEX, mode REPLACE: " + replaceHelper.GetError() + "."); cmStrCat("sub-command REGEX, mode REPLACE: ", e.what(), '.'));
return false; 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, 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" //"STRING(REGEX QUOTE <output variable> <input> [<input>...]\n"
std::string const& outvar = args[2]; std::string const& outvar = args[2];
std::string const input = cm::CMakeString data{ cmMakeRange(args).advance(3) };
cmJoin(cmMakeRange(args).advance(3), std::string());
std::string output;
// Escape all regex special characters try {
cmStringReplaceHelper replaceHelper("([][()+*^.$?|\\\\])", R"(\\\1)"); // Escape all regex special characters
if (!replaceHelper.Replace(input, output)) { data.Quote();
// Store the output in the provided variable.
status.GetMakefile().AddDefinition(outvar, data);
return true;
} catch (std::exception const& e) {
status.SetError( status.SetError(
"sub-command REGEX, mode QUOTE: " + replaceHelper.GetError() + "."); cmStrCat("sub-command REGEX, mode QUOTE: ", e.what(), '.'));
return false; return false;
} }
// Store the output in the provided variable.
status.GetMakefile().AddDefinition(outvar, output);
return true;
} }
bool HandleFindCommand(std::vector<std::string> const& args, 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 // try to find the character and return its position
size_t pos; auto pos = cm::CMakeString{ sstring }.Find(
if (!reverseMode) { schar,
pos = sstring.find(schar); reverseMode ? cm::CMakeString::FindFrom::End
} else { : cm::CMakeString::FindFrom::Begin);
pos = sstring.rfind(schar);
} status.GetMakefile().AddDefinition(
if (std::string::npos != pos) { outvar, pos != cm::CMakeString::npos ? std::to_string(pos) : "-1");
status.GetMakefile().AddDefinition(outvar, std::to_string(pos));
return true;
}
// the character was not found, but this is not really an error
status.GetMakefile().AddDefinition(outvar, "-1");
return true; return true;
} }
@@ -462,25 +376,22 @@ bool HandleCompareCommand(std::vector<std::string> const& args,
std::string const& right = args[3]; std::string const& right = args[3];
std::string const& outvar = args[4]; std::string const& outvar = args[4];
bool result; bool result;
cm::CMakeString::CompOperator op = cm::CMakeString::CompOperator::EQUAL;
if (mode == "LESS") { if (mode == "LESS") {
result = (left < right); op = cm::CMakeString::CompOperator::LESS;
} else if (mode == "LESS_EQUAL") { } else if (mode == "LESS_EQUAL") {
result = (left <= right); op = cm::CMakeString::CompOperator::LESS_EQUAL;
} else if (mode == "GREATER") { } else if (mode == "GREATER") {
result = (left > right); op = cm::CMakeString::CompOperator::GREATER;
} else if (mode == "GREATER_EQUAL") { } else if (mode == "GREATER_EQUAL") {
result = (left >= right); op = cm::CMakeString::CompOperator::GREATER_EQUAL;
} else if (mode == "EQUAL") {
result = (left == right);
} else // if(mode == "NOTEQUAL")
{
result = !(left == right);
} }
if (result) { result = cm::CMakeString{ left }.Compare(op, right);
status.GetMakefile().AddDefinition(outvar, "1"); if (mode == "NOTEQUAL") {
} else { result = !result;
status.GetMakefile().AddDefinition(outvar, "0");
} }
status.GetMakefile().AddDefinition(outvar, result ? "1" : "0");
return true; return true;
} }
std::string e = "sub-command COMPARE does not recognize mode " + mode; 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; return false;
} }
std::string const& matchExpression = args[1]; try {
std::string const& replaceExpression = args[2]; std::string const& matchExpression = args[1];
std::string const& variableName = args[3]; 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()); data.Replace(matchExpression, replaceExpression);
status.GetMakefile().AddDefinition(variableName, data);
cmsys::SystemTools::ReplaceString(input, matchExpression.c_str(), return true;
replaceExpression.c_str()); } catch (std::exception const& e) {
status.SetError(cmStrCat("sub-command REPLACE: ", e.what(), '.'));
status.GetMakefile().AddDefinition(variableName, input); return false;
return true; }
} }
bool HandleSubstringCommand(std::vector<std::string> const& args, bool HandleSubstringCommand(std::vector<std::string> const& args,
@@ -517,25 +430,19 @@ bool HandleSubstringCommand(std::vector<std::string> const& args,
return false; return false;
} }
std::string const& stringValue = args[1]; try {
int begin = atoi(args[2].c_str()); std::string const& stringValue = args[1];
int end = atoi(args[3].c_str()); int begin = atoi(args[2].c_str());
std::string const& variableName = args[4]; int end = atoi(args[3].c_str());
std::string const& variableName = args[4];
size_t stringLength = stringValue.size(); cm::CMakeString data{ stringValue };
int intStringLength = static_cast<int>(stringLength); status.GetMakefile().AddDefinition(variableName,
if (begin < 0 || begin > intStringLength) { data.Substring(begin, end));
status.SetError( } catch (std::exception const& e) {
cmStrCat("begin index: ", begin, " is out of range 0 - ", stringLength)); status.SetError(e.what());
return false; 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; return true;
} }
@@ -550,11 +457,8 @@ bool HandleLengthCommand(std::vector<std::string> const& args,
std::string const& stringValue = args[1]; std::string const& stringValue = args[1];
std::string const& variableName = args[2]; std::string const& variableName = args[2];
size_t length = stringValue.size(); status.GetMakefile().AddDefinition(
char buffer[1024]; variableName, std::to_string(cm::CMakeString{ stringValue }.Length()));
snprintf(buffer, sizeof(buffer), "%d", static_cast<int>(length));
status.GetMakefile().AddDefinition(variableName, buffer);
return true; return true;
} }
@@ -572,12 +476,10 @@ bool HandleAppendCommand(std::vector<std::string> const& args,
} }
auto const& variableName = args[1]; auto const& variableName = args[1];
cm::CMakeString data{ status.GetMakefile().GetDefinition(variableName) };
cm::string_view oldView{ status.GetMakefile().GetSafeDefinition( data.Append(cmMakeRange(args).advance(2));
variableName) }; status.GetMakefile().AddDefinition(variableName, data);
auto const newValue = cmJoin(cmMakeRange(args).advance(2), {}, oldView);
status.GetMakefile().AddDefinition(variableName, newValue);
return true; return true;
} }
@@ -596,13 +498,10 @@ bool HandlePrependCommand(std::vector<std::string> const& args,
} }
std::string const& variable = args[1]; std::string const& variable = args[1];
cm::CMakeString data{ status.GetMakefile().GetDefinition(variable) };
std::string value = cmJoin(cmMakeRange(args).advance(2), std::string()); data.Prepend(cmMakeRange(args).advance(2));
cmValue oldValue = status.GetMakefile().GetDefinition(variable); status.GetMakefile().AddDefinition(variable, data);
if (oldValue) {
value += *oldValue;
}
status.GetMakefile().AddDefinition(variable, value);
return true; return true;
} }
@@ -634,9 +533,9 @@ bool joinImpl(std::vector<std::string> const& args, std::string const& glue,
std::string const& variableName = args[varIdx]; std::string const& variableName = args[varIdx];
// NOTE Items to concat/join placed right after the variable for // NOTE Items to concat/join placed right after the variable for
// both `CONCAT` and `JOIN` sub-commands. // 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; return true;
} }
@@ -652,7 +551,7 @@ bool HandleMakeCIdentifierCommand(std::vector<std::string> const& args,
std::string const& variableName = args[2]; std::string const& variableName = args[2];
status.GetMakefile().AddDefinition(variableName, status.GetMakefile().AddDefinition(variableName,
cmSystemTools::MakeCidentifier(input)); cm::CMakeString{}.MakeCIdentifier(input));
return true; return true;
} }
@@ -664,14 +563,11 @@ bool HandleGenexStripCommand(std::vector<std::string> const& args,
return false; return false;
} }
std::string const& input = args[1]; cm::CMakeString data{ args[1] };
std::string result = cmGeneratorExpression::Preprocess(
input, cmGeneratorExpression::StripAllGeneratorExpressions);
std::string const& variableName = args[2]; std::string const& variableName = args[2];
status.GetMakefile().AddDefinition(variableName, result); status.GetMakefile().AddDefinition(
variableName, data.Strip(cm::CMakeString::StripItems::Genex));
return true; return true;
} }
@@ -683,36 +579,10 @@ bool HandleStripCommand(std::vector<std::string> const& args,
return false; return false;
} }
std::string const& stringValue = args[1]; cm::CMakeString data{ args[1] };
std::string const& variableName = args[2]; 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; status.GetMakefile().AddDefinition(variableName, data.Strip());
// 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));
return true; return true;
} }
@@ -744,30 +614,12 @@ bool HandleRepeatCommand(std::vector<std::string> const& args,
return true; 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& variableName = args[ArgPos::OUTPUT_VARIABLE];
auto const inStringLength = stringValue.size();
std::string result; makefile.AddDefinition(variableName, data);
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);
return true; return true;
} }
@@ -779,14 +631,10 @@ bool HandleRandomCommand(std::vector<std::string> const& args,
return false; return false;
} }
static bool seeded = false; int length = 5;
cm::string_view alphabet;
bool force_seed = false; bool force_seed = false;
unsigned int seed = 0; unsigned int seed = 0;
int length = 5;
char const cmStringCommandDefaultAlphabet[] = "qwertyuiopasdfghjklzxcvbnm"
"QWERTYUIOPASDFGHJKLZXCVBNM"
"0123456789";
std::string alphabet;
if (args.size() > 3) { if (args.size() > 3) {
size_t i = 1; 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()); try {
if (sizeofAlphabet < 1) { cm::CMakeString data;
status.SetError("sub-command RANDOM invoked with bad alphabet."); 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; 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, 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 const& outputVariable = args[argsIndex++];
std::string formatString; cm::string_view formatString;
if (args.size() > argsIndex && args[argsIndex] != "UTC") { if (args.size() > argsIndex && args[argsIndex] != "UTC") {
formatString = args[argsIndex++]; formatString = args[argsIndex++];
} }
bool utcFlag = false; cm::CMakeString::UTC utcFlag = cm::CMakeString::UTC::No;
if (args.size() > argsIndex) { if (args.size() > argsIndex) {
if (args[argsIndex] == "UTC") { if (args[argsIndex] == "UTC") {
utcFlag = true; utcFlag = cm::CMakeString::UTC::Yes;
} else { } else {
std::string e = " TIMESTAMP sub-command does not recognize option " + std::string e =
args[argsIndex] + "."; cmStrCat(" TIMESTAMP sub-command does not recognize option ",
args[argsIndex], '.');
status.SetError(e); status.SetError(e);
return false; return false;
} }
} }
cmTimestamp timestamp; cm::CMakeString data;
std::string result = timestamp.CurrentTime(formatString, utcFlag);
status.GetMakefile().AddDefinition(outputVariable, result); status.GetMakefile().AddDefinition(outputVariable,
data.Timestamp(formatString, utcFlag));
return true; return true;
} }
@@ -892,10 +727,10 @@ bool HandleUuidCommand(std::vector<std::string> const& args,
std::string const& outputVariable = args[argsIndex++]; std::string const& outputVariable = args[argsIndex++];
std::string uuidNamespaceString; cm::string_view uuidNamespaceString;
std::string uuidName; cm::string_view uuidName;
std::string uuidType; cm::CMakeString::UUIDType uuidType = cm::CMakeString::UUIDType::MD5;
bool uuidUpperCase = false; cm::CMakeString::Case uuidCase = cm::CMakeString::Case::Lower;
while (args.size() > argsIndex) { while (args.size() > argsIndex) {
if (args[argsIndex] == "NAMESPACE") { 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."); status.SetError("UUID sub-command, TYPE requires a value.");
return false; 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") { } else if (args[argsIndex] == "UPPER") {
++argsIndex; ++argsIndex;
uuidUpperCase = true; uuidCase = cm::CMakeString::Case::Upper;
} else { } else {
std::string e = std::string e = cmStrCat("UUID sub-command does not recognize option ",
"UUID sub-command does not recognize option " + args[argsIndex] + "."; args[argsIndex], '.');
status.SetError(e); status.SetError(e);
return false; return false;
} }
} }
std::string uuid; try {
cmUuid uuidGenerator; cm::CMakeString data;
std::vector<unsigned char> uuidNamespace; data.UUID(uuidNamespaceString, uuidName, uuidType, uuidCase);
if (!uuidGenerator.StringToBinary(uuidNamespaceString, uuidNamespace)) { status.GetMakefile().AddDefinition(outputVariable, data);
status.SetError("UUID sub-command, malformed NAMESPACE UUID."); return true;
} catch (std::exception const& e) {
status.SetError(cmStrCat("UUID sub-command, ", e.what(), '.'));
return false; 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 #else
status.SetError(cmStrCat(args[0], " not available during bootstrap")); status.SetError("UUID sub-command not available during bootstrap.");
return false; return false;
#endif #endif
} }
+1
View File
@@ -314,6 +314,7 @@ CMAKE_CXX_SOURCES="\
cmCMakePath \ cmCMakePath \
cmCMakePathCommand \ cmCMakePathCommand \
cmCMakePolicyCommand \ cmCMakePolicyCommand \
cmCMakeString \
cmCPackPropertiesGenerator \ cmCPackPropertiesGenerator \
cmCacheManager \ cmCacheManager \
cmCommands \ cmCommands \