Compile with C++20 (#3965)

This commit is contained in:
Antoine Prouvost 2025-06-04 11:28:11 +02:00 committed by GitHub
parent 835cbe8fcc
commit 7ce51b3de9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
31 changed files with 329 additions and 439 deletions

View File

@ -1,6 +1,6 @@
BasedOnStyle: Mozilla BasedOnStyle: Mozilla
Language: Cpp Language: Cpp
Standard: c++17 Standard: c++20
AccessModifierOffset: "-4" AccessModifierOffset: "-4"
AlignAfterOpenBracket: BlockIndent AlignAfterOpenBracket: BlockIndent

View File

@ -48,7 +48,7 @@
"CMAKE_COLOR_MAKEFILE": "ON", "CMAKE_COLOR_MAKEFILE": "ON",
"CMAKE_CXX_COMPILER_LAUNCHER": "ccache", "CMAKE_CXX_COMPILER_LAUNCHER": "ccache",
"CMAKE_CXX_EXTENSIONS": "OFF", "CMAKE_CXX_EXTENSIONS": "OFF",
"CMAKE_CXX_STANDARD": "17", "CMAKE_CXX_STANDARD": "20",
"CMAKE_CXX_STANDARD_REQUIRED": "ON", "CMAKE_CXX_STANDARD_REQUIRED": "ON",
"CMAKE_C_COMPILER_LAUNCHER": "ccache", "CMAKE_C_COMPILER_LAUNCHER": "ccache",
"CMAKE_EXPORT_COMPILE_COMMANDS": "ON" "CMAKE_EXPORT_COMPILE_COMMANDS": "ON"

View File

@ -294,7 +294,6 @@ set(
${LIBMAMBA_INCLUDE_DIR}/mamba/util/build.hpp ${LIBMAMBA_INCLUDE_DIR}/mamba/util/build.hpp
${LIBMAMBA_INCLUDE_DIR}/mamba/util/cast.hpp ${LIBMAMBA_INCLUDE_DIR}/mamba/util/cast.hpp
${LIBMAMBA_INCLUDE_DIR}/mamba/util/cfile.hpp ${LIBMAMBA_INCLUDE_DIR}/mamba/util/cfile.hpp
${LIBMAMBA_INCLUDE_DIR}/mamba/util/compare.hpp
${LIBMAMBA_INCLUDE_DIR}/mamba/util/conditional.hpp ${LIBMAMBA_INCLUDE_DIR}/mamba/util/conditional.hpp
${LIBMAMBA_INCLUDE_DIR}/mamba/util/cryptography.hpp ${LIBMAMBA_INCLUDE_DIR}/mamba/util/cryptography.hpp
${LIBMAMBA_INCLUDE_DIR}/mamba/util/deprecation.hpp ${LIBMAMBA_INCLUDE_DIR}/mamba/util/deprecation.hpp
@ -303,7 +302,6 @@ set(
${LIBMAMBA_INCLUDE_DIR}/mamba/util/flat_binary_tree.hpp ${LIBMAMBA_INCLUDE_DIR}/mamba/util/flat_binary_tree.hpp
${LIBMAMBA_INCLUDE_DIR}/mamba/util/flat_bool_expr_tree.hpp ${LIBMAMBA_INCLUDE_DIR}/mamba/util/flat_bool_expr_tree.hpp
${LIBMAMBA_INCLUDE_DIR}/mamba/util/flat_set.hpp ${LIBMAMBA_INCLUDE_DIR}/mamba/util/flat_set.hpp
${LIBMAMBA_INCLUDE_DIR}/mamba/util/functional.hpp
${LIBMAMBA_INCLUDE_DIR}/mamba/util/graph.hpp ${LIBMAMBA_INCLUDE_DIR}/mamba/util/graph.hpp
${LIBMAMBA_INCLUDE_DIR}/mamba/util/heap_optional.hpp ${LIBMAMBA_INCLUDE_DIR}/mamba/util/heap_optional.hpp
${LIBMAMBA_INCLUDE_DIR}/mamba/util/iterator.hpp ${LIBMAMBA_INCLUDE_DIR}/mamba/util/iterator.hpp
@ -454,7 +452,14 @@ macro(libmamba_create_target target_name linkage output_name)
# Header only libraries are always linked the same way # Header only libraries are always linked the same way
target_link_libraries(${target_name} PUBLIC tl::expected nlohmann_json::nlohmann_json) target_link_libraries(${target_name} PUBLIC tl::expected nlohmann_json::nlohmann_json)
target_compile_features(${target_name} PUBLIC cxx_std_17) target_compile_features(${target_name} PUBLIC cxx_std_20)
set_target_properties(
${target_name}
PROPERTIES
CXX_STANDARD 20
CXX_STANDARD_REQUIRED YES
CXX_EXTENSIONS NO
)
mamba_target_add_compile_warnings(${target_name} WARNING_AS_ERROR ${MAMBA_WARNING_AS_ERROR}) mamba_target_add_compile_warnings(${target_name} WARNING_AS_ERROR ${MAMBA_WARNING_AS_ERROR})
mamba_target_set_lto(${target_name} MODE ${MAMBA_LTO}) mamba_target_set_lto(${target_name} MODE ${MAMBA_LTO})

View File

@ -35,7 +35,14 @@ else()
endif() endif()
target_link_libraries(solv-cpp PUBLIC tl::expected ${LIBSOLV_DEPS}) target_link_libraries(solv-cpp PUBLIC tl::expected ${LIBSOLV_DEPS})
target_compile_features(solv-cpp PUBLIC cxx_std_17) target_compile_features(solv-cpp PUBLIC cxx_std_20)
set_target_properties(
solv-cpp
PROPERTIES
CXX_STANDARD 20
CXX_STANDARD_REQUIRED YES
CXX_EXTENSIONS NO
)
mamba_target_add_compile_warnings(solv-cpp WARNING_AS_ERROR ${MAMBA_WARNING_AS_ERROR}) mamba_target_add_compile_warnings(solv-cpp WARNING_AS_ERROR ${MAMBA_WARNING_AS_ERROR})
mamba_target_set_lto(solv-cpp MODE ${MAMBA_LTO}) mamba_target_set_lto(solv-cpp MODE ${MAMBA_LTO})

View File

@ -431,35 +431,14 @@ namespace mamba::fs
} }
//---- Operators ---- //---- Operators ----
friend bool operator==(const u8path& left, const u8path& right) noexcept friend bool operator==(const u8path& left, const u8path& right) noexcept
{ {
return left.m_path == right.m_path; return left.m_path == right.m_path;
} }
friend bool operator!=(const u8path& left, const u8path& right) noexcept friend std::strong_ordering operator<=>(const u8path& left, const u8path& right) noexcept
{ {
return left.m_path != right.m_path; return left.m_path <=> right.m_path;
}
friend bool operator<(const u8path& left, const u8path& right) noexcept
{
return left.m_path < right.m_path;
}
friend bool operator<=(const u8path& left, const u8path& right) noexcept
{
return left.m_path <= right.m_path;
}
friend bool operator>(const u8path& left, const u8path& right) noexcept
{
return left.m_path > right.m_path;
}
friend bool operator>=(const u8path& left, const u8path& right) noexcept
{
return left.m_path >= right.m_path;
} }
friend bool operator==(const u8path& left, const std::filesystem::path& right) noexcept friend bool operator==(const u8path& left, const std::filesystem::path& right) noexcept
@ -467,29 +446,10 @@ namespace mamba::fs
return left.m_path == right; return left.m_path == right;
} }
friend bool operator!=(const u8path& left, const std::filesystem::path& right) noexcept friend std::strong_ordering
operator<=>(const u8path& left, const std::filesystem::path& right) noexcept
{ {
return left.m_path != right; return left.m_path <=> right;
}
friend bool operator<(const u8path& left, const std::filesystem::path& right) noexcept
{
return left.m_path < right;
}
friend bool operator<=(const u8path& left, const std::filesystem::path& right) noexcept
{
return left.m_path <= right;
}
friend bool operator>(const u8path& left, const std::filesystem::path& right) noexcept
{
return left.m_path > right;
}
friend bool operator>=(const u8path& left, const std::filesystem::path& right) noexcept
{
return left.m_path >= right;
} }
friend bool operator==(const u8path& left, const std::string& right) noexcept friend bool operator==(const u8path& left, const std::string& right) noexcept
@ -497,9 +457,9 @@ namespace mamba::fs
return left.m_path == from_utf8(right); return left.m_path == from_utf8(right);
} }
friend bool operator!=(const u8path& left, const std::string& right) noexcept friend std::strong_ordering operator<=>(const u8path& left, const std::string& right) noexcept
{ {
return left.m_path != from_utf8(right); return left.m_path <=> from_utf8(right);
} }
friend bool operator==(const u8path& left, const char* right) noexcept friend bool operator==(const u8path& left, const char* right) noexcept
@ -507,29 +467,29 @@ namespace mamba::fs
return left.m_path == from_utf8(right); return left.m_path == from_utf8(right);
} }
friend bool operator!=(const u8path& left, const char* right) noexcept friend std::strong_ordering operator<=>(const u8path& left, const char* right) noexcept
{ {
return left.m_path != from_utf8(right); return left.m_path <=> from_utf8(right);
} }
friend bool operator==(const u8path& left, const std::wstring& right) noexcept friend bool operator==(const u8path& left, const std::wstring& right) noexcept
{ {
return left.m_path == right; return left.m_path == std::filesystem::path(right);
} }
friend bool operator!=(const u8path& left, const std::wstring& right) noexcept friend std::strong_ordering operator<=>(const u8path& left, const std::wstring& right) noexcept
{ {
return left.m_path != right; return left.m_path <=> std::filesystem::path(right);
} }
friend bool operator==(const u8path& left, const wchar_t* right) noexcept friend bool operator==(const u8path& left, const wchar_t* right) noexcept
{ {
return left.m_path == right; return left.m_path == std::filesystem::path(right);
} }
friend bool operator!=(const u8path& left, const wchar_t* right) noexcept friend std::strong_ordering operator<=>(const u8path& left, const wchar_t* right) noexcept
{ {
return left.m_path != right; return left.m_path <=> std::filesystem::path(right);
} }
//---- State ---- //---- State ----
@ -680,14 +640,9 @@ namespace mamba::fs
return *this; return *this;
} }
bool operator==(const directory_entry& other) const noexcept std::strong_ordering operator<=>(const directory_entry& other) const noexcept
{ {
return std::filesystem::directory_entry::operator==(other); return std::filesystem::directory_entry::operator<=>(other);
}
bool operator!=(const directory_entry& other) const noexcept
{
return std::filesystem::directory_entry::operator!=(other);
} }
u8path path() const u8path path() const

View File

@ -10,11 +10,10 @@
#include <limits> #include <limits>
#include <stdexcept> #include <stdexcept>
#include <type_traits> #include <type_traits>
#include <utility>
#include <fmt/format.h> #include <fmt/format.h>
#include "mamba/util/compare.hpp"
namespace mamba::util namespace mamba::util
{ {
/** /**
@ -56,23 +55,37 @@ namespace mamba::util
constexpr auto from_lowest = std::numeric_limits<From>::lowest(); constexpr auto from_lowest = std::numeric_limits<From>::lowest();
constexpr auto from_max = std::numeric_limits<From>::max(); constexpr auto from_max = std::numeric_limits<From>::max();
// Handle cases of char which std::cmp_xxx does not accept.
// WARNING: Only use during comparison
constexpr auto with_char_as_int = [](auto x)
{
if constexpr (std::is_same_v<decltype(x), char>)
{
return static_cast<int>(x);
}
else
{
return x;
}
};
if constexpr (std::is_same_v<From, To>) if constexpr (std::is_same_v<From, To>)
{ {
return val; return val;
} }
else if constexpr (std::is_integral_v<From> && std::is_integral_v<To>) else if constexpr (std::is_integral_v<From> && std::is_integral_v<To>)
{ {
if constexpr (cmp_less(from_lowest, to_lowest)) if constexpr (std::cmp_less(with_char_as_int(from_lowest), with_char_as_int(to_lowest)))
{ {
if (cmp_less(val, to_lowest)) if (std::cmp_less(with_char_as_int(val), with_char_as_int(to_lowest)))
{ {
throw detail::make_overflow_error<To>(val); throw detail::make_overflow_error<To>(val);
} }
} }
if constexpr (cmp_greater(from_max, to_max)) if constexpr (std::cmp_greater(with_char_as_int(from_max), with_char_as_int(to_max)))
{ {
if (cmp_greater(val, to_max)) if (std::cmp_greater(with_char_as_int(val), with_char_as_int(to_max)))
{ {
throw detail::make_overflow_error<To>(val); throw detail::make_overflow_error<To>(val);
} }

View File

@ -1,83 +0,0 @@
// Copyright (c) 2022, Cppreference.com
//
// Distributed under the terms of the Copyright/CC-BY-SA License.
//
// The full license can be found at the address
// https://en.cppreference.com/w/Cppreference:Copyright/CC-BY-SA
/**
* Backport of C++20 ``std::cmp_*`` functions for signed comparison.
*/
#ifndef MAMBA_CORE_UTIL_COMPARE_HPP
#define MAMBA_CORE_UTIL_COMPARE_HPP
#include <type_traits>
#include "mamba/util/deprecation.hpp"
namespace mamba::util
{
template <class T, class U>
MAMBA_DEPRECATED_CXX20 constexpr bool cmp_equal(T t, U u) noexcept
{
using UT = std::make_unsigned_t<T>;
using UU = std::make_unsigned_t<U>;
if constexpr (std::is_signed_v<T> == std::is_signed_v<U>)
{
return t == u;
}
else if constexpr (std::is_signed_v<T>)
{
return t < 0 ? false : UT(t) == u;
}
else
{
return u < 0 ? false : t == UU(u);
}
}
template <class T, class U>
MAMBA_DEPRECATED_CXX20 constexpr bool cmp_not_equal(T t, U u) noexcept
{
return !cmp_equal(t, u);
}
template <class T, class U>
MAMBA_DEPRECATED_CXX20 constexpr bool cmp_less(T t, U u) noexcept
{
using UT = std::make_unsigned_t<T>;
using UU = std::make_unsigned_t<U>;
if constexpr (std::is_signed_v<T> == std::is_signed_v<U>)
{
return t < u;
}
else if constexpr (std::is_signed_v<T>)
{
return t < 0 ? true : UT(t) < u;
}
else
{
return u < 0 ? false : t < UU(u);
}
}
template <class T, class U>
MAMBA_DEPRECATED_CXX20 constexpr bool cmp_greater(T t, U u) noexcept
{
return cmp_less(u, t);
}
template <class T, class U>
MAMBA_DEPRECATED_CXX20 constexpr bool cmp_less_equal(T t, U u) noexcept
{
return !cmp_greater(t, u);
}
template <class T, class U>
MAMBA_DEPRECATED_CXX20 constexpr bool cmp_greater_equal(T t, U u) noexcept
{
return !cmp_less(t, u);
}
}
#endif

View File

@ -7,12 +7,6 @@
#ifndef MAMBA_UTIL_DEPRECATION_HPP #ifndef MAMBA_UTIL_DEPRECATION_HPP
#define MAMBA_UTIL_DEPRECATION_HPP #define MAMBA_UTIL_DEPRECATION_HPP
#if __cplusplus >= 202002L
#define MAMBA_DEPRECATED_CXX20 [[deprecated("Use C++20 functions with the same name")]]
#else
#define MAMBA_DEPRECATED_CXX20 [[]]
#endif
#if __cplusplus >= 202302L #if __cplusplus >= 202302L
#define MAMBA_DEPRECATED_CXX23 [[deprecated("Use C++23 functions with the same name")]] #define MAMBA_DEPRECATED_CXX23 [[deprecated("Use C++23 functions with the same name")]]
#else #else

View File

@ -108,5 +108,20 @@ namespace mamba::util
*/ */
[[nodiscard]] auto decode_base64(std::string_view input) [[nodiscard]] auto decode_base64(std::string_view input)
-> tl::expected<std::string, EncodingError>; -> tl::expected<std::string, EncodingError>;
/**
* Convert a ``std::u8string`` to a UTF-8 encoded ``std::string``.
*
* We assume here that ``char`` and ``char8_t`` are containing the same Unicode data.
*/
[[nodiscard]] auto to_utf8_std_string(std::u8string_view text) -> std::string;
/**
* Convert a UTF-8 encoded ``std::string`` to a ``std::u8string` .
*
* We assume here that ``char`` and ``char8_t`` are containing the same Unicode data.
* No checks are made.
*/
[[nodiscard]] auto to_u8string(std::string_view text) -> std::u8string;
} }
#endif #endif

View File

@ -14,7 +14,6 @@
#include <vector> #include <vector>
#include "mamba/util/flat_binary_tree.hpp" #include "mamba/util/flat_binary_tree.hpp"
#include "mamba/util/functional.hpp"
namespace mamba::util namespace mamba::util
{ {
@ -166,7 +165,7 @@ namespace mamba::util
void clear(); void clear();
void reserve(size_type size); void reserve(size_type size);
template <typename UnaryFunc = identity> template <typename UnaryFunc = std::identity>
[[nodiscard]] auto evaluate(UnaryFunc&& var_evaluator = {}, bool empty_val = true) const [[nodiscard]] auto evaluate(UnaryFunc&& var_evaluator = {}, bool empty_val = true) const
-> bool; -> bool;

View File

@ -9,15 +9,16 @@
#define MAMBA_UTILFLAT_SET_HPP #define MAMBA_UTILFLAT_SET_HPP
#include <algorithm> #include <algorithm>
#include <iterator>
#include <utility> #include <utility>
#include <vector> #include <vector>
#include "mamba/util/deprecation.hpp"
#include "mamba/util/tuple_hash.hpp" #include "mamba/util/tuple_hash.hpp"
namespace mamba::util namespace mamba::util
{ {
struct sorted_unique_t struct sorted_unique_t
{ {
explicit sorted_unique_t() = default; explicit sorted_unique_t() = default;
@ -31,11 +32,9 @@ namespace mamba::util
* Like, ``std::set``, uniqueness is determined by using the equivalence relation. * Like, ``std::set``, uniqueness is determined by using the equivalence relation.
* In imprecise terms, two objects ``a`` and ``b`` are considered equivalent if neither * In imprecise terms, two objects ``a`` and ``b`` are considered equivalent if neither
* compares less than the other: ``!comp(a, b) && !comp(b, a)`` * compares less than the other: ``!comp(a, b) && !comp(b, a)``
*
* @todo C++23 This is implemented in <flat_set>
*/ */
template <typename Key, typename Compare = std::less<Key>, typename Allocator = std::allocator<Key>> template <typename Key, typename Compare = std::less<Key>, typename Allocator = std::allocator<Key>>
class flat_set : private std::vector<Key, Allocator> class MAMBA_DEPRECATED_CXX23 flat_set : private std::vector<Key, Allocator>
{ {
public: public:

View File

@ -1,32 +0,0 @@
// Copyright (c) 2023, QuantStack and Mamba Contributors
//
// Distributed under the terms of the BSD 3-Clause License.
//
// The full license is in the file LICENSE, distributed with this software.
#ifndef MAMBA_UTIL_FUNCTIONAL_HPP
#define MAMBA_UTIL_FUNCTIONAL_HPP
#include <utility>
#include "mamba/util/deprecation.hpp"
namespace mamba::util
{
struct MAMBA_DEPRECATED_CXX20 identity
{
template <typename T>
constexpr auto operator()(T&& t) const noexcept -> T&&;
};
/********************************
* Implementation of identity *
********************************/
template <typename T>
constexpr auto identity::operator()(T&& t) const noexcept -> T&&
{
return std::forward<T>(t);
}
}
#endif

View File

@ -19,8 +19,6 @@
#include <utility> #include <utility>
#include <vector> #include <vector>
#include "mamba/util/compare.hpp"
namespace mamba::util namespace mamba::util
{ {
/** /**
@ -662,7 +660,7 @@ namespace mamba::util
std::pair<std::size_t, std::size_t> show std::pair<std::size_t, std::size_t> show
) -> UnaryFunction ) -> UnaryFunction
{ {
if (util::cmp_less_equal(last - first, threshold)) if (std::cmp_less_equal(last - first, threshold))
{ {
return join_for_each(first, last, std::move(func), sep); return join_for_each(first, last, std::move(func), sep);
} }

View File

@ -272,7 +272,7 @@ namespace mamba
{ {
m_extract_bar.push_back(Console::instance().add_progress_bar(extract_tasks[i].name(), 1)); m_extract_bar.push_back(Console::instance().add_progress_bar(extract_tasks[i].name(), 1));
init_extract_bar(m_extract_bar.back()); init_extract_bar(m_extract_bar.back());
extract_tasks[i].set_progress_callback([=](PackageExtractEvent e) extract_tasks[i].set_progress_callback([=, this](PackageExtractEvent e)
{ update_extract_bar(i, e); }); { update_extract_bar(i, e); });
if (i < dl_requests.size()) if (i < dl_requests.size())

View File

@ -9,7 +9,6 @@
#include "mamba/core/context.hpp" #include "mamba/core/context.hpp"
#include "mamba/core/execution.hpp" #include "mamba/core/execution.hpp"
#include "mamba/util/compare.hpp"
#include "progress_bar_impl.hpp" #include "progress_bar_impl.hpp"
@ -974,10 +973,10 @@ namespace mamba
std::size_t spinner_width = 8; std::size_t spinner_width = 8;
std::size_t spinner_start = util::cmp_greater(pos, spinner_width) std::size_t spinner_start = std::cmp_greater(pos, spinner_width)
? pos - spinner_width ? pos - spinner_width
: 0; : 0;
std::size_t spinner_length = (util::cmp_less(pos + spinner_width, width) std::size_t spinner_length = (std::cmp_less(pos + spinner_width, width)
? pos + spinner_width ? pos + spinner_width
: width) : width)
- spinner_start; - spinner_start;

View File

@ -8,6 +8,7 @@
#include <memory> #include <memory>
#include <regex> #include <regex>
#include <stdexcept> #include <stdexcept>
#include <utility>
#include "mamba/core/channel_context.hpp" #include "mamba/core/channel_context.hpp"
#include "mamba/core/output.hpp" #include "mamba/core/output.hpp"
@ -687,7 +688,7 @@ namespace mamba
const auto cache_age_seconds = std::chrono::duration_cast<std::chrono::seconds>(cache_age) const auto cache_age_seconds = std::chrono::duration_cast<std::chrono::seconds>(cache_age)
.count(); .count();
if (util::cmp_less(cache_age_seconds, max_age) || params.offline) if (std::cmp_less(cache_age_seconds, max_age) || params.offline)
{ {
// valid json cache found // valid json cache found
if (!m_valid_cache_found) if (!m_valid_cache_found)

View File

@ -17,8 +17,6 @@
#include <sstream> #include <sstream>
#include <string> #include <string>
#include <string_view> #include <string_view>
#include <unordered_map>
#include <vector>
#include <time.h> #include <time.h>
@ -45,6 +43,8 @@ extern "C"
#include <process.h> #include <process.h>
#include <sys/locking.h> #include <sys/locking.h>
} }
#include "mamba/core/shell_init.hpp"
#endif #endif
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
@ -54,13 +54,11 @@ extern "C"
#include "mamba/core/execution.hpp" #include "mamba/core/execution.hpp"
#include "mamba/core/invoke.hpp" #include "mamba/core/invoke.hpp"
#include "mamba/core/output.hpp" #include "mamba/core/output.hpp"
#include "mamba/core/shell_init.hpp"
#include "mamba/core/thread_utils.hpp" #include "mamba/core/thread_utils.hpp"
#include "mamba/core/util.hpp" #include "mamba/core/util.hpp"
#include "mamba/core/util_os.hpp" #include "mamba/core/util_os.hpp"
#include "mamba/fs/filesystem.hpp" #include "mamba/fs/filesystem.hpp"
#include "mamba/util/build.hpp" #include "mamba/util/build.hpp"
#include "mamba/util/compare.hpp"
#include "mamba/util/environment.hpp" #include "mamba/util/environment.hpp"
#include "mamba/util/random.hpp" #include "mamba/util/random.hpp"
#include "mamba/util/string.hpp" #include "mamba/util/string.hpp"

View File

@ -21,6 +21,7 @@
#endif #endif
#include "mamba/fs/filesystem.hpp" #include "mamba/fs/filesystem.hpp"
#include "mamba/util/encoding.hpp"
namespace mamba::fs namespace mamba::fs
{ {
@ -44,26 +45,27 @@ namespace mamba::fs
} }
#endif #endif
#if __cplusplus == 201703L
std::string to_utf8(const std::filesystem::path& path, Utf8Options utf8_options) std::string to_utf8(const std::filesystem::path& path, Utf8Options utf8_options)
{ {
if (utf8_options.normalize_sep) const auto u8str = [&]
{ {
return normalized_separators(path).u8string(); if (utf8_options.normalize_sep)
} {
else return normalized_separators(path).u8string();
{ }
return path.u8string(); else
} {
return path.u8string();
}
}();
return util::to_utf8_std_string(u8str);
} }
std::filesystem::path from_utf8(std::string_view u8string) std::filesystem::path from_utf8(std::string_view u8string)
{ {
return normalized_separators(std::filesystem::u8path(u8string)); return normalized_separators(util::to_u8string(u8string));
} }
#else
#error UTF8 functions implementation is specific to C++17, using another version requires a different implementation.
#endif
void last_write_time(const u8path& path, now, std::error_code& ec) noexcept void last_write_time(const u8path& path, now, std::error_code& ec) noexcept
{ {

View File

@ -7,10 +7,11 @@
#include <array> #include <array>
#include <cstdint> #include <cstdint>
#include <cstring> #include <cstring>
#include <ranges>
#include <utility>
#include <openssl/evp.h> #include <openssl/evp.h>
#include "mamba/util/compare.hpp"
#include "mamba/util/conditional.hpp" #include "mamba/util/conditional.hpp"
#include "mamba/util/encoding.hpp" #include "mamba/util/encoding.hpp"
#include "mamba/util/string.hpp" #include "mamba/util/string.hpp"
@ -268,7 +269,7 @@ namespace mamba::util
static_cast<int>(input.size()) static_cast<int>(input.size())
); );
if (util::cmp_not_equal(expected_size, written_size)) if (std::cmp_not_equal(expected_size, written_size))
{ {
return tl::make_unexpected(EncodingError()); return tl::make_unexpected(EncodingError());
} }
@ -285,7 +286,7 @@ namespace mamba::util
reinterpret_cast<const unsigned char*>(input.data()), reinterpret_cast<const unsigned char*>(input.data()),
static_cast<int>(input.size()) static_cast<int>(input.size())
); );
if (util::cmp_not_equal(max_expected_size, max_possible_written_size)) if (std::cmp_not_equal(max_expected_size, max_possible_written_size))
{ {
return tl::make_unexpected(EncodingError()); return tl::make_unexpected(EncodingError());
} }
@ -296,4 +297,20 @@ namespace mamba::util
out.resize(min_expected_size + extra); out.resize(min_expected_size + extra);
return { std::move(out) }; return { std::move(out) };
} }
auto to_utf8_std_string(std::u8string_view text) -> std::string
{
static constexpr auto to_char = [](char8_t c) { return static_cast<char>(c); };
auto bytes = text | std::ranges::views::transform(to_char);
// TODO(C++23): Use std::ranges::to<std::string>
return { bytes.begin(), bytes.end() };
}
auto to_u8string(std::string_view text) -> std::u8string
{
static constexpr auto to_char8_t = [](char c) { return static_cast<char8_t>(c); };
auto bytes = text | std::ranges::views::transform(to_char8_t);
// TODO(C++23): Use std::ranges::to<std::string>
return { bytes.begin(), bytes.end() };
}
} }

View File

@ -4,7 +4,14 @@ add_executable(testing_libmamba_lock libmamba_lock/lock.cpp)
target_link_libraries(testing_libmamba_lock PUBLIC mamba::libmamba) target_link_libraries(testing_libmamba_lock PUBLIC mamba::libmamba)
target_compile_features(testing_libmamba_lock PUBLIC cxx_std_17) target_compile_features(testing_libmamba_lock PUBLIC cxx_std_20)
set_target_properties(
testing_libmamba_lock
PROPERTIES
CXX_STANDARD 20
CXX_STANDARD_REQUIRED YES
CXX_EXTENSIONS NO
)
mamba_target_add_compile_warnings(testing_libmamba_lock WARNING_AS_ERROR ${MAMBA_WARNING_AS_ERROR}) mamba_target_add_compile_warnings(testing_libmamba_lock WARNING_AS_ERROR ${MAMBA_WARNING_AS_ERROR})
@ -18,7 +25,6 @@ set(
src/catch-utils/msvc_catch_string_view.cpp src/catch-utils/msvc_catch_string_view.cpp
# Utility library # Utility library
src/util/test_cast.cpp src/util/test_cast.cpp
src/util/test_compare.cpp
src/util/test_charconv.cpp src/util/test_charconv.cpp
src/util/test_cryptography.cpp src/util/test_cryptography.cpp
src/util/test_encoding.cpp src/util/test_encoding.cpp
@ -143,7 +149,14 @@ target_compile_definitions(
MAMBA_TEST_LOCK_EXE="$<TARGET_FILE:testing_libmamba_lock>" MAMBA_TEST_LOCK_EXE="$<TARGET_FILE:testing_libmamba_lock>"
) )
target_compile_features(test_libmamba PUBLIC cxx_std_17) target_compile_features(test_libmamba PUBLIC cxx_std_20)
set_target_properties(
test_libmamba
PROPERTIES
CXX_STANDARD 20
CXX_STANDARD_REQUIRED YES
CXX_EXTENSIONS NO
)
add_custom_target( add_custom_target(
test test

View File

@ -13,6 +13,7 @@
#include "mamba/core/util_scope.hpp" #include "mamba/core/util_scope.hpp"
#include "mamba/fs/filesystem.hpp" #include "mamba/fs/filesystem.hpp"
#include "mamba/util/build.hpp" #include "mamba/util/build.hpp"
#include "mamba/util/encoding.hpp"
namespace mamba namespace mamba
{ {
@ -23,17 +24,20 @@ namespace mamba
static constexpr auto value = u8"a/b/c"; static constexpr auto value = u8"a/b/c";
std::filesystem::path x{ value }; std::filesystem::path x{ value };
const auto y = fs::normalized_separators(x); const auto y = fs::normalized_separators(x);
#if defined(_WIN32) if (util::on_win)
REQUIRE(y.u8string() == u8R"(a\b\c)"); {
#else REQUIRE(y.u8string() == u8R"(a\b\c)");
REQUIRE(y.u8string() == value); }
#endif else
{
REQUIRE(y.u8string() == value);
}
} }
TEST_CASE("normalized_separators_unicode") TEST_CASE("normalized_separators_unicode")
{ {
static constexpr auto value = u8"日本語"; static constexpr auto value = u8"日本語";
const std::filesystem::path x = fs::from_utf8(value); const std::filesystem::path x = fs::from_utf8(util::to_utf8_std_string(value));
REQUIRE(x.u8string() == u8"日本語"); // check assumption REQUIRE(x.u8string() == u8"日本語"); // check assumption
const auto y = fs::normalized_separators(x); const auto y = fs::normalized_separators(x);
REQUIRE(y.u8string() == u8"日本語"); REQUIRE(y.u8string() == u8"日本語");
@ -42,63 +46,108 @@ namespace mamba
TEST_CASE("to_utf8_check_separators") TEST_CASE("to_utf8_check_separators")
{ {
static constexpr auto some_path_str = u8"a/b/c"; static constexpr auto some_path_str = u8"a/b/c";
std::filesystem::path some_path = std::filesystem::u8path(some_path_str); std::filesystem::path some_path = some_path_str;
REQUIRE(fs::to_utf8(some_path, { /*normalize_sep=*/false }) == some_path_str); REQUIRE(
#if defined(_WIN32) fs::to_utf8(some_path, { .normalize_sep = false })
REQUIRE(fs::to_utf8(some_path, { /*normalize_sep=*/true }) == u8"a\\b\\c"); == util::to_utf8_std_string(some_path_str)
#else );
REQUIRE(fs::to_utf8(some_path, { /*normalize_sep=*/true }) == some_path_str); if (util::on_win)
#endif {
REQUIRE(
fs::to_utf8(some_path, { .normalize_sep = true })
== util::to_utf8_std_string(u8"a\\b\\c")
);
}
else
{
REQUIRE(
fs::to_utf8(some_path, { .normalize_sep = true })
== util::to_utf8_std_string(some_path_str)
);
}
} }
TEST_CASE("to_utf8_check_separators_unicode") TEST_CASE("to_utf8_check_separators_unicode")
{ {
static constexpr auto some_path_str = u8"日/本/語"; static constexpr auto some_path_str = u8"日/本/語";
std::filesystem::path some_path = std::filesystem::u8path(some_path_str); std::filesystem::path some_path = some_path_str;
REQUIRE(fs::to_utf8(some_path, { /*normalize_sep=*/false }) == some_path_str); REQUIRE(
#if defined(_WIN32) fs::to_utf8(some_path, { .normalize_sep = false })
REQUIRE(fs::to_utf8(some_path, { /*normalize_sep=*/true }) == u8"\\\\"); == util::to_utf8_std_string(some_path_str)
#else );
REQUIRE(fs::to_utf8(some_path, { /*normalize_sep=*/true }) == some_path_str); if (util::on_win)
#endif {
REQUIRE(
fs::to_utf8(some_path, { .normalize_sep = true })
== util::to_utf8_std_string(u8"\\\\")
);
}
else
{
REQUIRE(
fs::to_utf8(some_path, { .normalize_sep = true })
== util::to_utf8_std_string(some_path_str)
);
}
} }
TEST_CASE("from_utf8_check_separators") TEST_CASE("from_utf8_check_separators")
{ {
static constexpr auto some_path_str = u8"a/b/c"; static constexpr auto some_path_str = u8"a/b/c";
#if defined(_WIN32) if (util::on_win)
REQUIRE(fs::from_utf8(some_path_str) == std::filesystem::u8path(u8"a\\b\\c")); {
#else REQUIRE(
REQUIRE(fs::from_utf8(some_path_str) == std::filesystem::u8path(u8"a/b/c")); fs::from_utf8(util::to_utf8_std_string(some_path_str))
#endif == std::filesystem::path(u8"a\\b\\c")
);
}
else
{
REQUIRE(
fs::from_utf8(util::to_utf8_std_string(some_path_str))
== std::filesystem::path(u8"a/b/c")
);
}
} }
TEST_CASE("from_utf8_check_separators_unicode") TEST_CASE("from_utf8_check_separators_unicode")
{ {
static constexpr auto some_path_str = u8"日/本/語"; static constexpr auto some_path_str = u8"日/本/語";
#if defined(_WIN32) if (util::on_win)
REQUIRE(fs::from_utf8(some_path_str) == std::filesystem::u8path(u8"\\\\")); {
#else REQUIRE(
REQUIRE(fs::from_utf8(some_path_str) == std::filesystem::u8path(u8"日/本/語")); fs::from_utf8(util::to_utf8_std_string(some_path_str))
#endif == std::filesystem::path(u8"\\\\")
);
}
else
{
REQUIRE(
fs::from_utf8(util::to_utf8_std_string(some_path_str))
== std::filesystem::path(u8"日/本/語")
);
}
} }
TEST_CASE("u8path_separators_formatting") TEST_CASE("u8path_separators_formatting")
{ {
static constexpr auto some_path_str = u8"a/b/c"; static constexpr auto some_path_str = u8"a/b/c";
std::filesystem::path some_path = std::filesystem::u8path(some_path_str); std::filesystem::path some_path = std::filesystem::path(some_path_str);
const fs::u8path u8_path(some_path); const fs::u8path u8_path(some_path);
#if defined(_WIN32) if (util::on_win)
REQUIRE(u8_path.string() == u8"a\\b\\c"); {
#else REQUIRE(u8_path.string() == util::to_utf8_std_string(u8"a\\b\\c"));
REQUIRE(u8_path.string() == some_path_str); }
#endif else
REQUIRE(u8_path.generic_string() == some_path_str); {
REQUIRE(u8_path.string() == util::to_utf8_std_string(some_path_str));
}
REQUIRE(u8_path.generic_string() == util::to_utf8_std_string(some_path_str));
} }
TEST_CASE("consistent_encoding") TEST_CASE("consistent_encoding")
@ -106,10 +155,10 @@ namespace mamba
const auto utf8_string = u8"日本語"; const auto utf8_string = u8"日本語";
const fs::u8path filename(utf8_string); const fs::u8path filename(utf8_string);
const auto str = filename.string(); const auto str = filename.string();
REQUIRE(str == utf8_string); REQUIRE(str == util::to_utf8_std_string(utf8_string));
const fs::u8path file_path = fs::temp_directory_path() / filename; const fs::u8path file_path = fs::temp_directory_path() / filename;
REQUIRE(file_path.filename().string() == utf8_string); REQUIRE(file_path.filename().string() == util::to_utf8_std_string(utf8_string));
const auto std_path = file_path.std_path(); const auto std_path = file_path.std_path();
REQUIRE(std_path.filename().u8string() == utf8_string); REQUIRE(std_path.filename().u8string() == utf8_string);
@ -118,7 +167,8 @@ namespace mamba
TEST_CASE("string_stream_encoding") TEST_CASE("string_stream_encoding")
{ {
const auto utf8_string = u8"日本語"; const auto utf8_string = u8"日本語";
const std::string quoted_utf8_string = std::string("\"") + utf8_string const std::string quoted_utf8_string = std::string("\"")
+ util::to_utf8_std_string(utf8_string)
+ std::string("\""); + std::string("\"");
const fs::u8path filename(utf8_string); const fs::u8path filename(utf8_string);
std::stringstream stream; std::stringstream stream;
@ -128,7 +178,7 @@ namespace mamba
fs::u8path path_read; fs::u8path path_read;
stream.seekg(0); stream.seekg(0);
stream >> path_read; stream >> path_read;
REQUIRE(path_read.string() == utf8_string); REQUIRE(path_read.string() == util::to_utf8_std_string(utf8_string));
} }
TEST_CASE("directory_iteration") TEST_CASE("directory_iteration")
@ -143,7 +193,7 @@ namespace mamba
{ {
std::ofstream file(file_path.std_path(), std::ios::binary | std::ios::trunc); std::ofstream file(file_path.std_path(), std::ios::binary | std::ios::trunc);
file << u8"日本語"; file << util::to_utf8_std_string(u8"日本語");
} }
{ {
@ -232,14 +282,15 @@ namespace mamba
fs::create_directories(long_path); fs::create_directories(long_path);
} }
#if defined(_WIN32)
TEST_CASE("append_maintains_slash_type") TEST_CASE("append_maintains_slash_type")
{ {
const fs::u8path path = u8R"(a/b/c/d)"; if (util::on_win)
const auto path_1 = path / u8R"(e\f\g)"; {
REQUIRE(path_1.string() == u8R"(a\b\c\d\e\f\g)"); const fs::u8path path = util::to_utf8_std_string(u8R"(a/b/c/d)");
const auto path_1 = path / u8R"(e\f\g)";
REQUIRE(path_1.string() == util::to_utf8_std_string(u8R"(a\b\c\d\e\f\g)"));
}
} }
#endif
} }
namespace namespace
@ -294,7 +345,7 @@ namespace mamba
const auto create_readonly_files = [](const fs::u8path& dir_path) const auto create_readonly_files = [](const fs::u8path& dir_path)
{ {
assert(fs::is_directory(dir_path)); REQUIRE(fs::is_directory(dir_path));
for (int file_idx = 0; file_idx < file_count_per_directory; ++file_idx) for (int file_idx = 0; file_idx < file_count_per_directory; ++file_idx)
{ {
const auto readonly_file_path = dir_path const auto readonly_file_path = dir_path

View File

@ -7,6 +7,7 @@
#include <array> #include <array>
#include <string> #include <string>
#include <utility> #include <utility>
#include <variant>
#include <vector> #include <vector>
#include <catch2/catch_all.hpp> #include <catch2/catch_all.hpp>
@ -635,9 +636,6 @@ TEST_CASE("NamedList", "[mamba::solver]")
TEST_CASE("Create problem graph", "[mamba::solver]") TEST_CASE("Create problem graph", "[mamba::solver]")
{ {
using PbGr = ProblemsGraph;
using CpPbGr = CompressedProblemsGraph;
const auto [name, factory] = GENERATE( const auto [name, factory] = GENERATE(
std::pair{ "Basic conflict", &create_basic_conflict }, std::pair{ "Basic conflict", &create_basic_conflict },
std::pair{ "PubGrub example", &create_pubgrub }, std::pair{ "PubGrub example", &create_pubgrub },
@ -672,7 +670,7 @@ TEST_CASE("Create problem graph", "[mamba::solver]")
graph_init.for_each_node_id( graph_init.for_each_node_id(
[&](auto id) [&](auto id)
{ {
const auto& node = graph_init.node(id); const ProblemsGraph::node_t& node = graph_init.node(id);
// Currently we do not make assumption about virtual package since // Currently we do not make assumption about virtual package since
// we are not sure we are including them the same way than they would be in // we are not sure we are including them the same way than they would be in
// practice // practice
@ -682,15 +680,15 @@ TEST_CASE("Create problem graph", "[mamba::solver]")
{ {
// Only one root node // Only one root node
REQUIRE(id == pbs_init.root_node()); REQUIRE(id == pbs_init.root_node());
REQUIRE(std::holds_alternative<PbGr::RootNode>(node)); REQUIRE(std::holds_alternative<ProblemsGraph::RootNode>(node));
} }
else if (graph_init.out_degree(id) == 0) else if (graph_init.out_degree(id) == 0)
{ {
REQUIRE_FALSE(std::holds_alternative<PbGr::RootNode>(node)); REQUIRE_FALSE(std::holds_alternative<ProblemsGraph::RootNode>(node));
} }
else else
{ {
REQUIRE(std::holds_alternative<PbGr::PackageNode>(node)); REQUIRE(std::holds_alternative<ProblemsGraph::PackageNode>(node));
} }
// All nodes reachable from the root // All nodes reachable from the root
REQUIRE(is_reachable(pbs_init.graph(), pbs_init.root_node(), id)); REQUIRE(is_reachable(pbs_init.graph(), pbs_init.root_node(), id));
@ -701,8 +699,8 @@ TEST_CASE("Create problem graph", "[mamba::solver]")
const auto& conflicts_init = pbs_init.conflicts(); const auto& conflicts_init = pbs_init.conflicts();
for (const auto& [n, _] : conflicts_init) for (const auto& [n, _] : conflicts_init)
{ {
bool tmp = std::holds_alternative<PbGr::PackageNode>(graph_init.node(n)) bool tmp = std::holds_alternative<ProblemsGraph::PackageNode>(graph_init.node(n))
|| std::holds_alternative<PbGr::ConstraintNode>(graph_init.node(n)); || std::holds_alternative<ProblemsGraph::ConstraintNode>(graph_init.node(n));
REQUIRE(tmp); REQUIRE(tmp);
} }
@ -731,7 +729,7 @@ TEST_CASE("Create problem graph", "[mamba::solver]")
SECTION("Compress graph") SECTION("Compress graph")
{ {
const auto pbs_comp = CpPbGr::from_problems_graph(pbs_simplified); const auto pbs_comp = CompressedProblemsGraph::from_problems_graph(pbs_simplified);
const auto& graph_comp = pbs_comp.graph(); const auto& graph_comp = pbs_comp.graph();
REQUIRE(pbs_init.graph().number_of_nodes() >= graph_comp.number_of_nodes()); REQUIRE(pbs_init.graph().number_of_nodes() >= graph_comp.number_of_nodes());
@ -739,7 +737,7 @@ TEST_CASE("Create problem graph", "[mamba::solver]")
graph_comp.for_each_node_id( graph_comp.for_each_node_id(
[&](auto id) [&](auto id)
{ {
const auto& node = graph_comp.node(id); const CompressedProblemsGraph::node_t& node = graph_comp.node(id);
// Currently we do not make assumption about virtual package since // Currently we do not make assumption about virtual package since
// we are not sure we are including them the same way than they // we are not sure we are including them the same way than they
// would be in // would be in
@ -749,15 +747,18 @@ TEST_CASE("Create problem graph", "[mamba::solver]")
{ {
// Only one root node // Only one root node
REQUIRE(id == pbs_init.root_node()); REQUIRE(id == pbs_init.root_node());
REQUIRE(std::holds_alternative<CpPbGr::RootNode>(node)); REQUIRE(std::holds_alternative<CompressedProblemsGraph::RootNode>(node));
} }
else if (graph_comp.out_degree(id) == 0) else if (graph_comp.out_degree(id) == 0)
{ {
REQUIRE_FALSE(std::holds_alternative<CpPbGr::RootNode>(node)); REQUIRE_FALSE(
std::holds_alternative<CompressedProblemsGraph::RootNode>(node)
);
} }
else else
{ {
REQUIRE(std::holds_alternative<CpPbGr::PackageListNode>(node)); REQUIRE(std::holds_alternative<CompressedProblemsGraph::PackageListNode>(node
));
} }
// All nodes reachable from the root // All nodes reachable from the root
REQUIRE(is_reachable(graph_comp, pbs_comp.root_node(), id)); REQUIRE(is_reachable(graph_comp, pbs_comp.root_node(), id));
@ -768,8 +769,12 @@ TEST_CASE("Create problem graph", "[mamba::solver]")
const auto& conflicts_comp = pbs_comp.conflicts(); const auto& conflicts_comp = pbs_comp.conflicts();
for (const auto& [n, _] : conflicts_comp) for (const auto& [n, _] : conflicts_comp)
{ {
bool tmp = std::holds_alternative<CpPbGr::PackageListNode>(graph_comp.node(n)) bool tmp = std::holds_alternative<CompressedProblemsGraph::PackageListNode>(
|| std::holds_alternative<CpPbGr::ConstraintListNode>(graph_comp.node(n)); graph_comp.node(n)
)
|| std::holds_alternative<CompressedProblemsGraph::ConstraintListNode>(
graph_comp.node(n)
);
REQUIRE(tmp); REQUIRE(tmp);
} }
@ -780,7 +785,7 @@ TEST_CASE("Create problem graph", "[mamba::solver]")
auto message_contains = [&message, &name_copy](const auto& node) auto message_contains = [&message, &name_copy](const auto& node)
{ {
using Node = std::remove_cv_t<std::remove_reference_t<decltype(node)>>; using Node = std::remove_cv_t<std::remove_reference_t<decltype(node)>>;
if constexpr (!std::is_same_v<Node, CpPbGr::RootNode>) if constexpr (!std::is_same_v<Node, CompressedProblemsGraph::RootNode>)
{ {
if ((name_copy == "Pin conflict") && util::contains(node.name(), "pin on")) if ((name_copy == "Pin conflict") && util::contains(node.name(), "pin on"))
{ {

View File

@ -53,12 +53,12 @@ namespace
j["track_features"] = nl::json::array(); j["track_features"] = nl::json::array();
{ {
const auto p = j.get<RepoDataPackage>(); const auto p = j.get<RepoDataPackage>();
REQUIRE(p.name == j.at("name")); REQUIRE(j.at("name") == p.name);
// Note Version::parse is not injective // Note Version::parse is not injective
REQUIRE(p.version.to_string() == j.at("version")); REQUIRE(j.at("version") == p.version.to_string());
REQUIRE(p.build_string == j.at("build")); REQUIRE(j.at("build") == p.build_string);
REQUIRE(p.build_number == j.at("build_number")); REQUIRE(j.at("build_number") == p.build_number);
REQUIRE(p.subdir == j.at("subdir")); REQUIRE(j.at("subdir") == p.subdir);
REQUIRE_FALSE(p.md5.has_value()); REQUIRE_FALSE(p.md5.has_value());
REQUIRE_FALSE(p.platform.has_value()); REQUIRE_FALSE(p.platform.has_value());
REQUIRE(p.depends == decltype(p.depends){ "libsolv>=1.0" }); REQUIRE(p.depends == decltype(p.depends){ "libsolv>=1.0" });
@ -134,11 +134,11 @@ namespace
REQUIRE(data.info.has_value()); REQUIRE(data.info.has_value());
REQUIRE(platform_name(data.info.value().subdir) == j["info"]["subdir"].get<std::string_view>()); REQUIRE(platform_name(data.info.value().subdir) == j["info"]["subdir"].get<std::string_view>());
REQUIRE( REQUIRE(
data.packages.at("mamba-1.0-h12345.tar.bz2").name j["packages"]["mamba-1.0-h12345.tar.bz2"]["name"]
== j["packages"]["mamba-1.0-h12345.tar.bz2"]["name"] == data.packages.at("mamba-1.0-h12345.tar.bz2").name
); );
REQUIRE(data.conda_packages.empty()); REQUIRE(data.conda_packages.empty());
REQUIRE(data.removed == j["removed"]); REQUIRE(j["removed"] == data.removed);
} }
TEST_CASE("repodata_json") TEST_CASE("repodata_json")

View File

@ -6,7 +6,6 @@
#include <cmath> #include <cmath>
#include <limits> #include <limits>
#include <utility>
#include <catch2/catch_all.hpp> #include <catch2/catch_all.hpp>
@ -28,7 +27,7 @@ namespace
REQUIRE(safe_num_cast<To>(from_max) == static_cast<To>(from_max)); REQUIRE(safe_num_cast<To>(from_max) == static_cast<To>(from_max));
} }
TEST_CASE("Exact num cast widen - integers") TEST_CASE("Exact num cast widen - integers", "[mamba::util]")
{ {
check_exact_num_cast_widen<char, int>(); check_exact_num_cast_widen<char, int>();
check_exact_num_cast_widen<unsigned char, int>(); check_exact_num_cast_widen<unsigned char, int>();
@ -38,12 +37,12 @@ namespace
check_exact_num_cast_widen<unsigned int, unsigned long long int>(); check_exact_num_cast_widen<unsigned int, unsigned long long int>();
} }
TEST_CASE("Exact num cast widen - floats") TEST_CASE("Exact num cast widen - floats", "[mamba::util]")
{ {
check_exact_num_cast_widen<float, double>(); check_exact_num_cast_widen<float, double>();
} }
TEST_CASE("Exact num cast widen - mixed") TEST_CASE("Exact num cast widen - mixed", "[mamba::util]")
{ {
check_exact_num_cast_widen<char, float>(); check_exact_num_cast_widen<char, float>();
check_exact_num_cast_widen<unsigned char, float>(); check_exact_num_cast_widen<unsigned char, float>();
@ -58,7 +57,7 @@ namespace
REQUIRE(safe_num_cast<To>(From(1)) == To(1)); REQUIRE(safe_num_cast<To>(From(1)) == To(1));
} }
TEST_CASE("Exact num cast narrow - integers") TEST_CASE("Exact num cast narrow - integers", "[mamba::util]")
{ {
check_exact_num_cast_narrow<int, char>(); check_exact_num_cast_narrow<int, char>();
check_exact_num_cast_narrow<unsigned int, unsigned char>(); check_exact_num_cast_narrow<unsigned int, unsigned char>();
@ -66,7 +65,7 @@ namespace
check_exact_num_cast_narrow<unsigned long long int, unsigned int>(); check_exact_num_cast_narrow<unsigned long long int, unsigned int>();
} }
TEST_CASE("Exact num cast narrow - floats") TEST_CASE("Exact num cast narrow - floats", "[mamba::util]")
{ {
check_exact_num_cast_narrow<double, float>(); check_exact_num_cast_narrow<double, float>();
} }
@ -78,7 +77,7 @@ namespace
REQUIRE_THROWS_AS(safe_num_cast<To>(from_lowest), std::overflow_error); REQUIRE_THROWS_AS(safe_num_cast<To>(from_lowest), std::overflow_error);
} }
TEST_CASE("Exact num cast overflow - integers") TEST_CASE("Exact num cast overflow - integers", "[mamba::util]")
{ {
check_exact_num_cast_overflow<char, unsigned char>(); check_exact_num_cast_overflow<char, unsigned char>();
check_exact_num_cast_overflow<char, unsigned int>(); check_exact_num_cast_overflow<char, unsigned int>();
@ -86,18 +85,18 @@ namespace
check_exact_num_cast_overflow<int, unsigned long long int>(); check_exact_num_cast_overflow<int, unsigned long long int>();
} }
TEST_CASE("Exact num cast overflow - floats") TEST_CASE("Exact num cast overflow - floats", "[mamba::util]")
{ {
check_exact_num_cast_overflow<double, float>(); check_exact_num_cast_overflow<double, float>();
} }
TEST_CASE("Exact num cast overflow - mixed") TEST_CASE("Exact num cast overflow - mixed", "[mamba::util]")
{ {
check_exact_num_cast_overflow<double, int>(); check_exact_num_cast_overflow<double, int>();
check_exact_num_cast_overflow<float, char>(); check_exact_num_cast_overflow<float, char>();
} }
TEST_CASE("precision") TEST_CASE("precision", "[mamba::util]")
{ {
REQUIRE_THROWS_AS(safe_num_cast<int>(1.1), std::runtime_error); REQUIRE_THROWS_AS(safe_num_cast<int>(1.1), std::runtime_error);
REQUIRE_THROWS_AS(safe_num_cast<float>(std::nextafter(double(1), 2)), std::runtime_error); REQUIRE_THROWS_AS(safe_num_cast<float>(std::nextafter(double(1), 2)), std::runtime_error);

View File

@ -1,88 +0,0 @@
// Copyright (c) 2023, QuantStack and Mamba Contributors
//
// Distributed under the terms of the BSD 3-Clause License.
//
// The full license is in the file LICENSE, distributed with this software.
#include <limits>
#include <utility>
#include <catch2/catch_all.hpp>
#include "mamba/util/compare.hpp"
using namespace mamba::util;
namespace
{
TEST_CASE("equal")
{
REQUIRE(cmp_equal(char{ 0 }, char{ 0 }));
REQUIRE(cmp_equal(char{ 1 }, char{ 1 }));
REQUIRE(cmp_equal(char{ -1 }, char{ -1 }));
REQUIRE(cmp_equal(int{ 0 }, int{ 0 }));
REQUIRE(cmp_equal(int{ 1 }, int{ 1 }));
REQUIRE(cmp_equal(int{ -1 }, int{ -1 }));
REQUIRE(cmp_equal(std::size_t{ 0 }, std::size_t{ 0 }));
REQUIRE(cmp_equal(std::size_t{ 1 }, std::size_t{ 1 }));
REQUIRE(cmp_equal(char{ 0 }, int{ 0 }));
REQUIRE(cmp_equal(char{ 1 }, int{ 1 }));
REQUIRE(cmp_equal(char{ -1 }, int{ -1 }));
REQUIRE(cmp_equal(std::size_t{ 0 }, char{ 0 }));
REQUIRE(cmp_equal(std::size_t{ 1 }, char{ 1 }));
REQUIRE(cmp_equal(std::size_t{ 0 }, int{ 0 }));
REQUIRE(cmp_equal(std::size_t{ 1 }, int{ 1 }));
REQUIRE_FALSE(cmp_equal(char{ 0 }, char{ 1 }));
REQUIRE_FALSE(cmp_equal(char{ 1 }, char{ -1 }));
REQUIRE_FALSE(cmp_equal(int{ 0 }, int{ 1 }));
REQUIRE_FALSE(cmp_equal(int{ -1 }, int{ 1 }));
REQUIRE_FALSE(cmp_equal(std::size_t{ 0 }, std::size_t{ 1 }));
REQUIRE_FALSE(cmp_equal(char{ 0 }, int{ 1 }));
REQUIRE_FALSE(cmp_equal(char{ 1 }, int{ -1 }));
REQUIRE_FALSE(cmp_equal(char{ -1 }, int{ 1 }));
REQUIRE_FALSE(cmp_equal(std::size_t{ 1 }, int{ -1 }));
REQUIRE_FALSE(cmp_equal(static_cast<std::size_t>(-1), int{ -1 }));
REQUIRE_FALSE(cmp_equal(std::size_t{ 1 }, int{ 0 }));
REQUIRE_FALSE(cmp_equal(std::numeric_limits<std::size_t>::max(), int{ 0 }));
}
TEST_CASE("less")
{
REQUIRE(cmp_less(char{ 0 }, char{ 1 }));
REQUIRE(cmp_less(char{ -1 }, char{ 0 }));
REQUIRE(cmp_less(int{ 0 }, int{ 1 }));
REQUIRE(cmp_less(int{ -1 }, int{ 1 }));
REQUIRE(cmp_less(std::size_t{ 0 }, std::size_t{ 1 }));
REQUIRE(cmp_less(char{ 0 }, int{ 1 }));
REQUIRE(cmp_less(char{ -1 }, int{ 0 }));
REQUIRE(cmp_less(char{ -1 }, int{ 1 }));
REQUIRE(cmp_less(char{ -1 }, std::size_t{ 1 }));
REQUIRE(cmp_less(std::size_t{ 0 }, int{ 1 }));
REQUIRE(cmp_less(std::numeric_limits<int>::min(), char{ 0 }));
REQUIRE(cmp_less(std::numeric_limits<int>::min(), std::size_t{ 0 }));
REQUIRE(cmp_less(int{ -1 }, std::numeric_limits<std::size_t>::max()));
REQUIRE(cmp_less(std::size_t{ 1 }, std::numeric_limits<int>::max()));
REQUIRE_FALSE(cmp_less(char{ 1 }, char{ 0 }));
REQUIRE_FALSE(cmp_less(char{ 1 }, char{ 1 }));
REQUIRE_FALSE(cmp_less(char{ 0 }, char{ -1 }));
REQUIRE_FALSE(cmp_less(int{ 1 }, int{ 0 }));
REQUIRE_FALSE(cmp_less(int{ 1 }, int{ -1 }));
REQUIRE_FALSE(cmp_less(std::size_t{ 1 }, std::size_t{ 0 }));
REQUIRE_FALSE(cmp_less(char{ 1 }, int{ 1 }));
REQUIRE_FALSE(cmp_less(char{ 1 }, int{ 0 }));
REQUIRE_FALSE(cmp_less(char{ 0 }, int{ -1 }));
REQUIRE_FALSE(cmp_less(char{ 1 }, int{ -11 }));
REQUIRE_FALSE(cmp_less(std::size_t{ 1 }, char{ -1 }));
REQUIRE_FALSE(cmp_less(int{ 1 }, std::size_t{ 0 }));
REQUIRE_FALSE(cmp_less(char{ 0 }, std::numeric_limits<int>::min()));
REQUIRE_FALSE(cmp_less(std::size_t{ 0 }, std::numeric_limits<int>::min()));
REQUIRE_FALSE(cmp_less(std::numeric_limits<std::size_t>::max(), int{ -1 }));
REQUIRE_FALSE(cmp_less(std::numeric_limits<int>::max(), std::size_t{ 1 }));
}
}

View File

@ -16,7 +16,7 @@ using namespace mamba::util;
namespace namespace
{ {
TEST_CASE("Hexadecimal") TEST_CASE("Hexadecimal", "[mamba::util]")
{ {
SECTION("nibble_to_hex") SECTION("nibble_to_hex")
{ {
@ -107,7 +107,7 @@ namespace
} }
} }
TEST_CASE("percent") TEST_CASE("percent", "[mamba::util]")
{ {
SECTION("encode") SECTION("encode")
{ {
@ -141,7 +141,7 @@ namespace
} }
} }
TEST_CASE("base64") TEST_CASE("base64", "[mamba::util]")
{ {
SECTION("encode") SECTION("encode")
{ {
@ -149,7 +149,7 @@ namespace
REQUIRE(encode_base64("Hello World!").value() == "SGVsbG8gV29ybGQh"); REQUIRE(encode_base64("Hello World!").value() == "SGVsbG8gV29ybGQh");
REQUIRE(encode_base64("!@#$%^U&I*O").value() == "IUAjJCVeVSZJKk8="); REQUIRE(encode_base64("!@#$%^U&I*O").value() == "IUAjJCVeVSZJKk8=");
REQUIRE( REQUIRE(
encode_base64(u8"_私のにほHelloわへたです").value() encode_base64(to_utf8_std_string(u8"_私のにほHelloわへたです")).value()
== "X+engeOBruOBq+OBu0hlbGxv44KP44G444Gf44Gn44GZ" == "X+engeOBruOBq+OBu0hlbGxv44KP44G444Gf44Gn44GZ"
); );
REQUIRE(encode_base64("xyzpass").value() == "eHl6cGFzcw=="); REQUIRE(encode_base64("xyzpass").value() == "eHl6cGFzcw==");
@ -161,7 +161,7 @@ namespace
REQUIRE(decode_base64("SGVsbG8gV29ybGQh").value() == "Hello World!"); REQUIRE(decode_base64("SGVsbG8gV29ybGQh").value() == "Hello World!");
REQUIRE(decode_base64("IUAjJCVeVSZJKk8=").value() == "!@#$%^U&I*O"); REQUIRE(decode_base64("IUAjJCVeVSZJKk8=").value() == "!@#$%^U&I*O");
REQUIRE( REQUIRE(
decode_base64(u8"X+engeOBruOBq+OBu0hlbGxv44KP44G444Gf44Gn44GZ").value() decode_base64(to_utf8_std_string(u8"X+engeOBruOBq+OBu0hlbGxv44KP44G444Gf44Gn44GZ")).value()
== "_私のにほHelloわへたです" == "_私のにほHelloわへたです"
); );
REQUIRE(decode_base64("eHl6cGFzcw==").value() == "xyzpass"); REQUIRE(decode_base64("eHl6cGFzcw==").value() == "xyzpass");

View File

@ -7,6 +7,7 @@
#include <catch2/catch_all.hpp> #include <catch2/catch_all.hpp>
#include "mamba/util/build.hpp" #include "mamba/util/build.hpp"
#include "mamba/util/encoding.hpp"
#include "mamba/util/environment.hpp" #include "mamba/util/environment.hpp"
#include "mambatests.hpp" #include "mambatests.hpp"
@ -15,7 +16,7 @@ using namespace mamba::util;
namespace namespace
{ {
TEST_CASE("get_env") TEST_CASE("get_env", "[mamba::util]")
{ {
const auto restore = mambatests::EnvironmentCleaner(); const auto restore = mambatests::EnvironmentCleaner();
@ -29,32 +30,32 @@ namespace
SECTION("ASCII") SECTION("ASCII")
{ {
const auto key = std::string(u8"VAR_THAT_DOES_NOT_EXIST_XYZ"); const auto key = to_utf8_std_string(u8"VAR_THAT_DOES_NOT_EXIST_XYZ");
const auto value1 = std::string(u8"VALUE"); const auto value1 = to_utf8_std_string(u8"VALUE");
set_env(key, value1); set_env(key, value1);
REQUIRE(get_env(key) == value1); REQUIRE(get_env(key) == value1);
const auto value2 = std::string(u8"VALUE_NEW"); const auto value2 = to_utf8_std_string(u8"VALUE_NEW");
set_env(key, value2); set_env(key, value2);
REQUIRE(get_env(key) == value2); REQUIRE(get_env(key) == value2);
} }
SECTION("UTF-8") SECTION("UTF-8")
{ {
const auto key = std::string(u8"VAR_私のにほんごわへたです"); const auto key = to_utf8_std_string(u8"VAR_私のにほんごわへたです");
const auto value1 = std::string(u8"😀"); const auto value1 = to_utf8_std_string(u8"😀");
set_env(key, value1); set_env(key, value1);
REQUIRE(get_env(key) == value1); REQUIRE(get_env(key) == value1);
const auto value2 = std::string(u8"🤗"); const auto value2 = to_utf8_std_string(u8"🤗");
set_env(key, value2); set_env(key, value2);
REQUIRE(get_env(key) == value2); REQUIRE(get_env(key) == value2);
} }
} }
TEST_CASE("unset_env") TEST_CASE("unset_env", "[mamba::util]")
{ {
const auto restore = mambatests::EnvironmentCleaner(); const auto restore = mambatests::EnvironmentCleaner();
const auto key = std::string(u8"VAR_THAT_DOES_NOT_EXIST_ABC_😀"); const auto key = to_utf8_std_string(u8"VAR_THAT_DOES_NOT_EXIST_ABC_😀");
REQUIRE_FALSE(get_env(key).has_value()); REQUIRE_FALSE(get_env(key).has_value());
unset_env(key); unset_env(key);
REQUIRE_FALSE(get_env(key).has_value()); REQUIRE_FALSE(get_env(key).has_value());
@ -64,7 +65,7 @@ namespace
REQUIRE_FALSE(get_env(key).has_value()); REQUIRE_FALSE(get_env(key).has_value());
} }
TEST_CASE("get_env_map") TEST_CASE("get_env_map", "[mamba::util]")
{ {
const auto restore = mambatests::EnvironmentCleaner(); const auto restore = mambatests::EnvironmentCleaner();
@ -73,64 +74,64 @@ namespace
REQUIRE(env.count("VAR_THAT_MUST_NOT_EXIST_XYZ") == 0); REQUIRE(env.count("VAR_THAT_MUST_NOT_EXIST_XYZ") == 0);
REQUIRE(env.count("PATH") == 1); REQUIRE(env.count("PATH") == 1);
const auto key = std::string(u8"VAR_私のにほHelloわへたです"); const auto key = to_utf8_std_string(u8"VAR_私のにほHelloわへたです");
const auto value = std::string(u8"😀"); const auto value = to_utf8_std_string(u8"😀");
set_env(key, value); set_env(key, value);
env = get_env_map(); env = get_env_map();
REQUIRE(env.at(key) == value); REQUIRE(env.at(key) == value);
} }
TEST_CASE("update_env_map") TEST_CASE("update_env_map", "[mamba::util]")
{ {
const auto restore = mambatests::EnvironmentCleaner(); const auto restore = mambatests::EnvironmentCleaner();
const auto key_inexistent = std::string(u8"CONDA😀"); const auto key_inexistent = to_utf8_std_string(u8"CONDA😀");
const auto key_unchanged = std::string(u8"MAMBA😀"); const auto key_unchanged = to_utf8_std_string(u8"MAMBA😀");
const auto key_changed = std::string(u8"PIXI😀"); const auto key_changed = to_utf8_std_string(u8"PIXI😀");
REQUIRE_FALSE(get_env(key_inexistent).has_value()); REQUIRE_FALSE(get_env(key_inexistent).has_value());
REQUIRE_FALSE(get_env(key_unchanged).has_value()); REQUIRE_FALSE(get_env(key_unchanged).has_value());
REQUIRE_FALSE(get_env(key_changed).has_value()); REQUIRE_FALSE(get_env(key_changed).has_value());
const auto val_set_1 = std::string(u8"a😀"); const auto val_set_1 = to_utf8_std_string(u8"a😀");
update_env_map({ { key_changed, val_set_1 }, { key_unchanged, val_set_1 } }); update_env_map({ { key_changed, val_set_1 }, { key_unchanged, val_set_1 } });
REQUIRE(get_env(key_inexistent) == std::nullopt); REQUIRE(get_env(key_inexistent) == std::nullopt);
REQUIRE(get_env(key_unchanged) == val_set_1); REQUIRE(get_env(key_unchanged) == val_set_1);
REQUIRE(get_env(key_changed) == val_set_1); REQUIRE(get_env(key_changed) == val_set_1);
const auto val_set_2 = std::string(u8"b😀"); const auto val_set_2 = to_utf8_std_string(u8"b😀");
update_env_map({ { key_changed, val_set_2 } }); update_env_map({ { key_changed, val_set_2 } });
REQUIRE(get_env(key_inexistent) == std::nullopt); REQUIRE(get_env(key_inexistent) == std::nullopt);
REQUIRE(get_env(key_unchanged) == val_set_1); REQUIRE(get_env(key_unchanged) == val_set_1);
REQUIRE(get_env(key_changed) == val_set_2); REQUIRE(get_env(key_changed) == val_set_2);
} }
TEST_CASE("set_env_map") TEST_CASE("set_env_map", "[mamba::util]")
{ {
const auto restore = mambatests::EnvironmentCleaner(); const auto restore = mambatests::EnvironmentCleaner();
const auto key_inexistent = std::string(u8"CONDA🤗"); const auto key_inexistent = to_utf8_std_string(u8"CONDA🤗");
const auto key_unchanged = std::string(u8"MAMBA🤗"); const auto key_unchanged = to_utf8_std_string(u8"MAMBA🤗");
const auto key_changed = std::string(u8"PIXI🤗"); const auto key_changed = to_utf8_std_string(u8"PIXI🤗");
REQUIRE_FALSE(get_env(key_inexistent).has_value()); REQUIRE_FALSE(get_env(key_inexistent).has_value());
REQUIRE_FALSE(get_env(key_unchanged).has_value()); REQUIRE_FALSE(get_env(key_unchanged).has_value());
REQUIRE_FALSE(get_env(key_changed).has_value()); REQUIRE_FALSE(get_env(key_changed).has_value());
const auto val_set_1 = std::string(u8"a😀"); const auto val_set_1 = to_utf8_std_string(u8"a😀");
set_env_map({ { key_changed, val_set_1 }, { key_unchanged, val_set_1 } }); set_env_map({ { key_changed, val_set_1 }, { key_unchanged, val_set_1 } });
REQUIRE(get_env(key_inexistent) == std::nullopt); REQUIRE(get_env(key_inexistent) == std::nullopt);
REQUIRE(get_env(key_unchanged) == val_set_1); REQUIRE(get_env(key_unchanged) == val_set_1);
REQUIRE(get_env(key_changed) == val_set_1); REQUIRE(get_env(key_changed) == val_set_1);
const auto val_set_2 = std::string(u8"b😀"); const auto val_set_2 = to_utf8_std_string(u8"b😀");
set_env_map({ { key_changed, val_set_2 } }); set_env_map({ { key_changed, val_set_2 } });
REQUIRE(get_env(key_inexistent) == std::nullopt); REQUIRE(get_env(key_inexistent) == std::nullopt);
REQUIRE(get_env(key_unchanged) == std::nullopt); // Difference with update_env_map REQUIRE(get_env(key_unchanged) == std::nullopt); // Difference with update_env_map
REQUIRE(get_env(key_changed) == val_set_2); REQUIRE(get_env(key_changed) == val_set_2);
} }
TEST_CASE("user_home_dir") TEST_CASE("user_home_dir", "[mamba::util]")
{ {
const auto restore = mambatests::EnvironmentCleaner(); const auto restore = mambatests::EnvironmentCleaner();
@ -165,7 +166,7 @@ namespace
} }
} }
TEST_CASE("user_xdg") TEST_CASE("user_xdg", "[mamba::util]")
{ {
const auto restore = mambatests::EnvironmentCleaner(); const auto restore = mambatests::EnvironmentCleaner();
@ -193,7 +194,7 @@ namespace
} }
} }
TEST_CASE("which_in") TEST_CASE("which_in", "[mamba::util]")
{ {
SECTION("Inexistent search dirs") SECTION("Inexistent search dirs")
{ {
@ -224,7 +225,7 @@ namespace
} }
} }
TEST_CASE("which") TEST_CASE("which", "[mamba::util]")
{ {
SECTION("echo") SECTION("echo")
{ {

View File

@ -11,6 +11,7 @@
#include <catch2/catch_all.hpp> #include <catch2/catch_all.hpp>
#include "mamba/util/build.hpp" #include "mamba/util/build.hpp"
#include "mamba/util/encoding.hpp"
#include "mamba/util/os_win.hpp" #include "mamba/util/os_win.hpp"
using namespace mamba; using namespace mamba;
@ -25,7 +26,7 @@ namespace
SKIP(); SKIP();
} }
const std::wstring text_utf16 = L"Hello, I am Joël. 私のにほんごわへたです"; const std::wstring text_utf16 = L"Hello, I am Joël. 私のにほんごわへたです";
const std::string text_utf8 = u8"Hello, I am Joël. 私のにほんごわへたです"; const std::string text_utf8 = to_utf8_std_string(u8"Hello, I am Joël. 私のにほんごわへたです");
SECTION("utf8_to_windows_encoding") SECTION("utf8_to_windows_encoding")
{ {

View File

@ -50,7 +50,14 @@ target_include_directories(bindings PRIVATE src/libmambapy/bindings)
mamba_target_add_compile_warnings(bindings WARNING_AS_ERROR ${MAMBA_WARNING_AS_ERROR}) mamba_target_add_compile_warnings(bindings WARNING_AS_ERROR ${MAMBA_WARNING_AS_ERROR})
target_link_libraries(bindings PRIVATE pybind11::pybind11 ${libmamba_target}) target_link_libraries(bindings PRIVATE pybind11::pybind11 ${libmamba_target})
target_compile_features(bindings PRIVATE cxx_std_17) target_compile_features(bindings PUBLIC cxx_std_20)
set_target_properties(
bindings
PROPERTIES
CXX_STANDARD 20
CXX_STANDARD_REQUIRED YES
CXX_EXTENSIONS NO
)
# Installation # Installation

View File

@ -36,7 +36,14 @@ mamba_target_add_compile_warnings(mamba-package WARNING_AS_ERROR ${MAMBA_WARNING
target_link_libraries(mamba-package PRIVATE mamba::libmamba) target_link_libraries(mamba-package PRIVATE mamba::libmamba)
set_target_properties(mamba-package PROPERTIES CXX_STANDARD 17) target_compile_features(mamba-package PUBLIC cxx_std_20)
set_target_properties(
mamba-package
PROPERTIES
CXX_STANDARD 20
CXX_STANDARD_REQUIRED YES
CXX_EXTENSIONS NO
)
install( install(
TARGETS mamba-package TARGETS mamba-package

View File

@ -66,7 +66,14 @@ macro(mambaexe_create_target target_name linkage output_name)
add_executable(${target_name} ${MICROMAMBA_SRCS} ${MICROMAMBA_HEADERS}) add_executable(${target_name} ${MICROMAMBA_SRCS} ${MICROMAMBA_HEADERS})
mamba_target_add_compile_warnings(${target_name} WARNING_AS_ERROR ${MAMBA_WARNING_AS_ERROR}) mamba_target_add_compile_warnings(${target_name} WARNING_AS_ERROR ${MAMBA_WARNING_AS_ERROR})
mamba_target_set_lto(${target_name} MODE ${MAMBA_LTO}) mamba_target_set_lto(${target_name} MODE ${MAMBA_LTO})
set_property(TARGET ${target_name} PROPERTY CXX_STANDARD 17) target_compile_features(${target_name} PUBLIC cxx_std_20)
set_target_properties(
${target_name}
PROPERTIES
CXX_STANDARD 20
CXX_STANDARD_REQUIRED YES
CXX_EXTENSIONS NO
)
target_link_libraries(${target_name} PRIVATE Threads::Threads reproc reproc++) target_link_libraries(${target_name} PRIVATE Threads::Threads reproc reproc++)