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
cmCMakePresetsGraphReadJSONTestPresets.cxx
cmCMakePresetsGraphReadJSONWorkflowPresets.cxx
cmCMakeString.hxx
cmCMakeString.cxx
cmCommandLineArgument.h
cmCommonTargetGenerator.cxx
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 <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
}
+1
View File
@@ -314,6 +314,7 @@ CMAKE_CXX_SOURCES="\
cmCMakePath \
cmCMakePathCommand \
cmCMakePolicyCommand \
cmCMakeString \
cmCPackPropertiesGenerator \
cmCacheManager \
cmCommands \