From 8652b3bfc3058e79560c8b197c4fa4f4469cecba Mon Sep 17 00:00:00 2001 From: DockedFerret800 Date: Sat, 28 Feb 2026 17:07:50 +0000 Subject: [PATCH 01/19] Modules support --- .github/workflows/modules.yml | 50 ++++ .gitignore | 2 + CMakeLists.txt | 48 +++- CMakePresets.json | 100 ++++++++ README.md | 17 +- cmake/install-config.cmake | 2 +- cmake/install-rules.cmake | 23 +- modules/ut.ixx | 414 ++++++++++++++++++++++++++++++++++ src/main.cpp | 11 +- tests/ut_run_tests.cpp | 5 +- 10 files changed, 653 insertions(+), 19 deletions(-) create mode 100644 .github/workflows/modules.yml create mode 100644 CMakePresets.json create mode 100644 modules/ut.ixx diff --git a/.github/workflows/modules.yml b/.github/workflows/modules.yml new file mode 100644 index 0000000..da770f2 --- /dev/null +++ b/.github/workflows/modules.yml @@ -0,0 +1,50 @@ +name: Build & Test with modules + +on: + push: + branches: + - main + pull_request: + branches: + - main + workflow_dispatch: + release: + types: [published] + +jobs: + build: + name: ${{ matrix.runs_on }}-${{ matrix.compiler }}-${{ matrix.configure_preset }} + runs-on: ${{ matrix.runs_on }} + strategy: + fail-fast: false + matrix: + include: + - runs_on: windows-2025-vs2026 + compiler: msvc-14.50 + configure_preset: "x64" + build_preset: "x64-build" + test_preset : "x64-test" + # To be uncommented once GitHub Actions add GCC 15 or 16 with Ubuntu 26 + # - runs_on: ubuntu-latest + # compiler: gcc-15 + # configure_preset: "linux-debug" + # build_preset: "linux-debug-build" + # test_preset : "linux-debug-test" + - runs_on: ubuntu-latest + compiler: clang-18 + configure_preset: "linux-debug" + build_preset: "linux-debug-build" + test_preset : "linux-debug-test" + steps: + + - uses: actions/checkout@v6 + + - uses: lukka/get-cmake@latest + + - name: "Build & Test" + uses: lukka/run-cmake@v10 + with: + configurePreset: ${{ matrix.configure_preset }} + configurePresetAdditionalArgs: "['-DUT_ENABLE_MODULES=ON']" + buildPreset: ${{ matrix.build_preset }} + testPreset: ${{ matrix.test_preset }} \ No newline at end of file diff --git a/.gitignore b/.gitignore index efe9691..d7edafb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,8 @@ build* +out/ compile_commands.json .vscode .cache .DS_Store +.vs \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index ec7cfcd..24380f4 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,6 @@ -cmake_minimum_required(VERSION 3.24) +cmake_minimum_required(VERSION 4.2) + +set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD "d0edc3af-4c50-42ea-a356-e2862fe7a444") # Should be removed after 4.3 is released include(cmake/prelude.cmake) @@ -11,9 +13,46 @@ project( include(cmake/project-is-top-level.cmake) include(cmake/variables.cmake) -add_library(${PROJECT_NAME}_${PROJECT_NAME} INTERFACE) +option(UT_ENABLE_MODULES "Enable modules with import std" OFF) +option(UT_COMPILE_TIME "Enable compile time features" ON) + +if (UT_ENABLE_MODULES) + if (NOT CMAKE_GENERATOR MATCHES "Visual Studio") ## Support should be added with 4.4, todo: check when available + set(CMAKE_CXX_MODULE_STD 1) + endif() +endif() + +if (UT_ENABLE_MODULES) + add_library(${PROJECT_NAME}_${PROJECT_NAME} STATIC) + set(UT_LIB_TYPE PUBLIC) +else() + add_library(${PROJECT_NAME}_${PROJECT_NAME} INTERFACE) + set(UT_LIB_TYPE INTERFACE) +endif() add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME}_${PROJECT_NAME}) +if (PROJECT_IS_TOP_LEVEL) + include(cmake/dev-mode.cmake) +endif() + +target_compile_features(${PROJECT_NAME}_${PROJECT_NAME} ${UT_LIB_TYPE} cxx_std_23) + +if(UT_ENABLE_MODULES) + target_sources(${PROJECT_NAME}_${PROJECT_NAME} + PUBLIC + FILE_SET modules TYPE CXX_MODULES + BASE_DIRS "${PROJECT_SOURCE_DIR}/modules" + FILES + "${PROJECT_SOURCE_DIR}/modules/ut.ixx" + ) + + target_compile_definitions(${PROJECT_NAME}_${PROJECT_NAME} PRIVATE UT_ENABLE_MODULES) +endif() + +if (UT_COMPILE_TIME) + target_compile_definitions(${PROJECT_NAME}_${PROJECT_NAME} ${UT_LIB_TYPE} UT_COMPILE_TIME) +endif() + if (MSVC) string(REGEX MATCH "\/cl(.exe)?$" matched_cl ${CMAKE_CXX_COMPILER}) if (matched_cl) @@ -25,7 +64,6 @@ endif() set_property(TARGET ${PROJECT_NAME}_${PROJECT_NAME} PROPERTY EXPORT_NAME ${PROJECT_NAME}) -target_compile_features(${PROJECT_NAME}_${PROJECT_NAME} INTERFACE cxx_std_23) target_include_directories( ${PROJECT_NAME}_${PROJECT_NAME} ${warning_guard} INTERFACE "$" @@ -34,7 +72,3 @@ target_include_directories( if(NOT CMAKE_SKIP_INSTALL_RULES) include(cmake/install-rules.cmake) endif() - -if (PROJECT_IS_TOP_LEVEL) - include(cmake/dev-mode.cmake) -endif() \ No newline at end of file diff --git a/CMakePresets.json b/CMakePresets.json new file mode 100644 index 0000000..5b1fd42 --- /dev/null +++ b/CMakePresets.json @@ -0,0 +1,100 @@ +{ + "version": 9, + "cmakeMinimumRequired": { + "major": 4, + "minor": 2, + "patch": 0 + }, + "configurePresets": [ + { + "name": "windows-base", + "hidden": true, + "generator": "Ninja", + "binaryDir": "${sourceDir}/out/build/${presetName}", + "installDir": "${sourceDir}/out/install/${presetName}", + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Windows" + } + }, + { + "name": "x64", + "displayName": "x64", + "description": "Target Windows (64-bit) with the Visual Studio development environment.", + "inherits": "windows-base", + "architecture": { + "value": "x64", + "strategy": "external" + } + }, + { + "name": "windows-base-preview", + "description": "Base preset for MSVC v14.51", + "hidden": true, + "generator": "Ninja", + "binaryDir": "${sourceDir}/out/build/${presetName}", + "installDir": "${sourceDir}/out/install/${presetName}", + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Windows" + }, + "toolset": { + "value": "v145,host=x64,version=14.51" + } + }, + { + "name": "x64-preview", + "displayName": "x64 Debug (MSVC v14.51 Preview)", + "inherits": "windows-base-preview", + "architecture": { + "value": "x64", + "strategy": "external" + } + }, + { + "name": "linux-debug", + "displayName": "Linux Debug", + "generator": "Ninja", + "binaryDir": "${sourceDir}/out/build/${presetName}", + "installDir": "${sourceDir}/out/install/${presetName}", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug" + }, + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Linux" + } + } + ], + "buildPresets": [ + { + "name": "x64-build", + "configurePreset": "x64" + }, + { + "name": "x64-preview-build", + "configurePreset": "x64-preview" + }, + { + "name": "linux-debug-build", + "configurePreset": "linux-debug" + } + ], + "testPresets": [ + { + "name": "x64-test", + "configurePreset": "x64" + }, + { + "name": "x64-preview-test", + "configurePreset": "x64-preview" + }, + { + "name": "linux-debug-test", + "configurePreset": "linux-debug" + } + ] +} \ No newline at end of file diff --git a/README.md b/README.md index 7ff30b4..77a45bd 100644 --- a/README.md +++ b/README.md @@ -14,10 +14,24 @@ A simple and fast compiling unit test library. - Single header -To enable compile time testing you must define the macro: `UT_COMPILE_TIME` +To enable compile time testing, set the option `UT_COMPILE_TIME` to ON. For example: +```cmake +set(UT_COMPILE_TIME ON) +``` Runtime testing is always enabled. +The library supports C++ modules. To enable, set the option `UT_ENABLE_MODULES` to ON. For example: +```cmake +set (UT_ENABLE_MODULES ON) +``` +You can then import the library: +```cpp +import ut; +``` + +Please note that modules integration requires compiler and build tools that support `import std`. + ### Running Specific Tests Use the `UT_RUN` environment variable to run specific tests by name: @@ -33,6 +47,7 @@ UT_RUN="[test1,test2,test3]" ./my_tests ### Requirements - C++23 +- CMake 4.2 ## Example diff --git a/cmake/install-config.cmake b/cmake/install-config.cmake index e9af6c2..e6b051b 100644 --- a/cmake/install-config.cmake +++ b/cmake/install-config.cmake @@ -1 +1 @@ -include("${CMAKE_CURRENT_LIST_DIR}/${PROJECT_NAME}Targets.cmake") \ No newline at end of file +include("${CMAKE_CURRENT_LIST_DIR}/utTargets.cmake") diff --git a/cmake/install-rules.cmake b/cmake/install-rules.cmake index 261fa29..be5333e 100644 --- a/cmake/install-rules.cmake +++ b/cmake/install-rules.cmake @@ -17,11 +17,23 @@ install( COMPONENT ${PROJECT_NAME}_Development ) -install( - TARGETS ${PROJECT_NAME}_${PROJECT_NAME} - EXPORT ${PROJECT_NAME}Targets - INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" -) +if(UT_ENABLE_MODULES) + install( + TARGETS ${PROJECT_NAME}_${PROJECT_NAME} + EXPORT ${PROJECT_NAME}Targets + INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" + FILE_SET modules DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" + CXX_MODULES_BMI DESTINATION "" + COMPONENT ${PROJECT_NAME}_Development + ) +else() + install( + TARGETS ${PROJECT_NAME}_${PROJECT_NAME} + EXPORT ${PROJECT_NAME}Targets + INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" + COMPONENT ${PROJECT_NAME}_Development + ) +endif() write_basic_package_version_file( "${package}ConfigVersion.cmake" @@ -53,6 +65,7 @@ install( EXPORT ${PROJECT_NAME}Targets NAMESPACE ${PROJECT_NAME}:: DESTINATION "${zb8_INSTALL_CMAKEDIR}" + CXX_MODULES_DIRECTORY modules COMPONENT ${PROJECT_NAME}_Development ) diff --git a/modules/ut.ixx b/modules/ut.ixx new file mode 100644 index 0000000..d05111a --- /dev/null +++ b/modules/ut.ixx @@ -0,0 +1,414 @@ +// Refactored from: +// Copyright (c) 2024 Kris Jusiak (kris at jusiak dot net) +// Distributed under the Boost Software License, Version 1.0. +// (See http://www.boost.org/LICENSE_1_0.txt) +// +// UT: A simple C++23 unit testing library with compile-time and run-time support. +// +// Running specific tests: +// Set the UT_RUN environment variable to run only specific tests by name. +// Single test: UT_RUN="my test" ./my_tests +// Multiple tests: UT_RUN="[test1,test2,test3]" ./my_tests +// If UT_RUN is not set, all tests run (default behavior). +export module ut; + +import std; + +namespace ut +{ + namespace detail + { + constexpr bool fatal = true; + + template + constexpr auto is_mutable_lambda_v = false; + template + constexpr auto is_mutable_lambda_v = true; + template + constexpr auto is_mutable_lambda_v = false; + template + constexpr auto has_capture_lambda_v = sizeof(Fn) > 1ul; + + template + struct identity + { + using type = T; + }; + } + + template + struct fixed_string + { + constexpr fixed_string(const char (&str)[Size]) + { + for (std::size_t i = 0; i < Size; ++i) { + storage[i] = str[i]; + } + } + [[nodiscard]] constexpr auto operator[](const auto i) const { return storage[i]; } + [[nodiscard]] constexpr auto data() const { return storage; } + [[nodiscard]] static constexpr auto size() { return Size - 1; } + [[nodiscard]] constexpr operator std::string_view() const { return {storage, Size - 1}; } + constexpr friend auto operator<<(auto& os, const fixed_string& fs) -> decltype(auto) + { + return os << std::string_view{fs.storage, fs.size()}; + } + char storage[Size]{}; + }; + + namespace events + { + enum class mode { run_time, compile_time }; + template + struct test_begin + { + std::string_view file_name{}; + std::uint_least32_t line{}; + std::string_view name{}; + }; + template + struct test_end + { + std::string_view file_name{}; + std::uint_least32_t line{}; + std::string_view name{}; + enum { FAILED, PASSED, COMPILE_TIME } result{}; + }; + struct assertion + { + bool passed{}; + std::string_view file_name{}; + std::uint_least32_t line{}; + }; + struct fatal + {}; + template + struct log + { + const Msg& msg; + bool result{}; + }; + struct summary + { + enum { FAILED, PASSED, COMPILE_TIME }; + std::size_t asserts[2]{}; /* FAILED, PASSED */ + std::size_t tests[3]{}; /* FAILED, PASSED, COMPILE_TIME */ + }; + } // namespace events + + template + struct outputter + { + template + constexpr auto on(const events::test_begin&) + {} + constexpr auto on(const events::test_begin& event) { current_test = event; } + template + constexpr auto on(const events::test_end&) + {} + constexpr auto on(const events::assertion& event) + { + if (not event.passed && not std::is_constant_evaluated()) { + if (initial_new_line == '\n') { + os << initial_new_line; + } + else { + initial_new_line = '\n'; + } + os << "FAILED \"" << current_test.name << "\" "; + const auto n = event.file_name.size(); + const auto start = n <= 32 ? 0 : n - 32; + if (start > 0) { + os << "..."; + } + os << event.file_name.substr(start, n) << ":" << event.line << '\n'; + } + } + constexpr auto on(const events::fatal&) {} + template + constexpr auto on(const events::log& event) + { + if (!std::is_constant_evaluated() && !event.result) { + os << ' ' << event.msg; + } + } + constexpr auto on(const events::summary& event) + { + using namespace events; + if (!std::is_constant_evaluated()) { + if (event.asserts[summary::FAILED] || event.tests[summary::FAILED]) { + os << "\nFAILED\n"; + } + else { + os << "\nPASSED\n"; + } + os << "tests: " << (event.tests[summary::PASSED] + event.tests[summary::FAILED]) << " (" + << event.tests[summary::PASSED] << " passed, " << event.tests[summary::FAILED] << " failed, " + << event.tests[summary::COMPILE_TIME] << " compile-time)\n" + << "asserts: " << (event.asserts[summary::PASSED] + event.asserts[summary::FAILED]) << " (" + << event.asserts[summary::PASSED] << " passed, " << event.asserts[summary::FAILED] << " failed)\n"; + } + } + + OStream& os; + events::test_begin current_test{}; + char initial_new_line{}; + }; + + template + struct reporter + { + constexpr auto on(const events::test_begin& event) + { + asserts_failed[current++] = summary.asserts[events::summary::FAILED]; + outputter.on(event); + } + constexpr auto on(const events::test_end& event) + { + const auto result = summary.asserts[events::summary::FAILED] == asserts_failed[--current]; + ++summary.tests[result]; + events::test_end te{event}; + te.result = static_cast(result); + outputter.on(te); + } + constexpr auto on(const events::test_begin&) + { + ++summary.tests[events::summary::COMPILE_TIME]; + } + constexpr auto on(const events::test_end&) {} + constexpr auto on(const events::assertion& event) + { + if (event.passed) { + ++summary.asserts[events::summary::PASSED]; + } + else { + ++summary.asserts[events::summary::FAILED]; + } + outputter.on(event); + } + constexpr auto on(const events::fatal& event) + { + ++summary.tests[events::summary::FAILED]; + outputter.on(event); + outputter.on(summary); + std::exit(1); + } + + ~reporter() + { // non constexpr + outputter.on(summary); + if (summary.asserts[events::summary::FAILED]) { + std::exit(1); + } + } + + Outputter& outputter; + events::summary summary{}; + std::size_t asserts_failed[MaxDepth]{}; + std::size_t current{}; + }; + + template + struct runner + { + template + constexpr auto on(Test test, const std::string_view file_name, std::uint_least32_t line, const std::string_view name) + -> bool + { + if (std::is_constant_evaluated()) { + if constexpr (requires { requires detail::is_mutable_lambda_v; }) { + return false; + } + else { + test(); + return true; + } + } + else { + static const std::string_view filter = []() -> std::string_view { + if (const char* env = std::getenv("UT_RUN")) return env; + return {}; + }(); + + auto matches_filter = [](std::string_view test_name, std::string_view f) { + if (f.empty()) return true; + + // Array format: [test1,test2,test3] + if (f.starts_with('[') && f.ends_with(']')) { + auto content = f.substr(1, f.size() - 2); + std::size_t pos = 0; + while (pos < content.size()) { + auto comma = content.find(',', pos); + auto token = + (comma == std::string_view::npos) ? content.substr(pos) : content.substr(pos, comma - pos); + if (token == test_name) return true; + if (comma == std::string_view::npos) break; + pos = comma + 1; + } + return false; + } + + // Single test name + return test_name == f; + }; + + if (!matches_filter(name, filter)) { + return false; + } + +#if defined(UT_COMPILE_TIME) + if constexpr (!requires { requires detail::is_mutable_lambda_v; } && + !detail::has_capture_lambda_v) { + reporter.on(events::test_begin{file_name, line, name}); + static_assert((test(), "[FAILED]")); + reporter.on(events::test_end{file_name, line, name}); + } +#endif + + reporter.on(events::test_begin{file_name, line, name}); + test(); + reporter.on(events::test_end{file_name, line, name}); + } + return true; + } + + Reporter& reporter; + }; +} + +namespace ut +{ + inline struct + { + struct + { + friend constexpr decltype(auto) operator<<([[maybe_unused]] auto& os, [[maybe_unused]] const auto& t) + { + static_assert(requires { std::clog << t; }); + return (std::clog << t); + } + } stream; + ut::outputter outputter{stream}; + ut::reporter reporter{outputter}; + ut::runner runner{reporter}; + } cfg; + + constexpr struct + { + template + struct eval final + { + template + requires std::convertible_to + constexpr eval(T&& test_passed, auto&& loc) : passed(static_cast(test_passed)) + { + if (std::is_constant_evaluated()) { + if (not passed) { + std::abort(); + } + } + else { + cfg.reporter.on(events::assertion{passed, loc.file_name(), loc.line()}); + if (not passed) { + if constexpr (Fatal) { + cfg.reporter.on(events::fatal{}); + } + } + } + } + bool passed{}; + }; + + template + requires std::convertible_to + constexpr auto operator()(T&& test_passed, + const std::source_location& loc = std::source_location::current()) const + { + return log{eval{test_passed, loc}.passed}; + } + +#if __cplusplus >= 202300L + // if we have C++23 + template + requires std::convertible_to + constexpr auto operator[](T&& test_passed, + const std::source_location& loc = std::source_location::current()) const + { + return log{eval{test_passed, loc}.passed}; + } +#else + template + requires std::convertible_to + constexpr auto operator[](T&& test_passed) const + { + return log{eval{test_passed, std::source_location::current()}.passed}; + } +#endif + + private: + struct log final + { + bool passed{}; + + template + constexpr const auto& operator<<(const Msg& msg) const + { + cfg.outputter.on(events::log{msg, passed}); + return *this; + } + }; + } expect{}; + + struct suite final + { + suite(auto&& tests) { tests(); } + }; + + namespace detail + { + template + struct test final + { + constexpr auto operator=(auto test) const + { + const auto& loc = std::source_location::current(); + return cfg.runner.on(test, loc.file_name(), loc.line(), Name); + } + }; + + struct runtime_test final + { + std::string_view name{}; + + constexpr auto operator=(auto test) const + { + const auto& loc = std::source_location::current(); + return cfg.runner.on(test, loc.file_name(), loc.line(), name); + } + }; + } + + constexpr auto test(const std::string_view name) { return detail::runtime_test{name}; } + + template + [[nodiscard]] constexpr auto operator""_test() + { + return detail::test{}; + } + +#if __cpp_exceptions + template + constexpr auto throws(Callable&& c, Args&&... args) + { + try { + std::forward(c)(std::forward(args)...); + } + catch (...) { + return true; + } + return false; + } +#endif +} + +using ut::operator""_test; diff --git a/src/main.cpp b/src/main.cpp index c2821ea..2614f1f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,11 +1,14 @@ -#define UT_COMPILE_TIME - +#ifndef UT_ENABLE_MODULES #include "ut/ut.hpp" -using namespace ut; - #include #include +#else +import ut; +import std; +#endif + +using namespace ut; suite tests = [] { "double"_test = [] { diff --git a/tests/ut_run_tests.cpp b/tests/ut_run_tests.cpp index ecdf5ba..1e1ef4c 100644 --- a/tests/ut_run_tests.cpp +++ b/tests/ut_run_tests.cpp @@ -1,7 +1,10 @@ // Tests for UT_RUN environment variable filtering feature // This file is used by CTest to verify the UT_RUN functionality - +#ifndef UT_ENABLE_MODULES #include "ut/ut.hpp" +#else +import ut; +#endif using namespace ut; From f9825dd59ca30bc808c2e281befeaa6783373d03 Mon Sep 17 00:00:00 2001 From: DockedFerret800 Date: Sat, 28 Feb 2026 17:08:18 +0000 Subject: [PATCH 02/19] Committing clang-format changes --- src/main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 2614f1f..accc61c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,8 +1,8 @@ #ifndef UT_ENABLE_MODULES -#include "ut/ut.hpp" - #include #include + +#include "ut/ut.hpp" #else import ut; import std; From f18c0d7ed8ca58437d5af31e245855bc7f88a973 Mon Sep 17 00:00:00 2001 From: DockedFerret800 Date: Sat, 28 Feb 2026 18:14:24 +0000 Subject: [PATCH 03/19] Add exports --- CMakeLists.txt | 2 +- modules/ut.ixx | 33 +++++++++++++++++++-------------- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 24380f4..8960a0d 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ include(cmake/prelude.cmake) project( ut - VERSION 1.1.0 + VERSION 1.2.0 LANGUAGES CXX ) diff --git a/modules/ut.ixx b/modules/ut.ixx index d05111a..6dd2157 100644 --- a/modules/ut.ixx +++ b/modules/ut.ixx @@ -36,7 +36,7 @@ namespace ut }; } - template + export template struct fixed_string { constexpr fixed_string(const char (&str)[Size]) @@ -278,9 +278,9 @@ namespace ut namespace ut { - inline struct + struct cfg_t { - struct + struct stream_t { friend constexpr decltype(auto) operator<<([[maybe_unused]] auto& os, [[maybe_unused]] const auto& t) { @@ -288,12 +288,15 @@ namespace ut return (std::clog << t); } } stream; - ut::outputter outputter{stream}; + ut::outputter outputter{stream}; ut::reporter reporter{outputter}; ut::runner runner{reporter}; - } cfg; + }; + + export extern cfg_t cfg; + cfg_t cfg{}; - constexpr struct + struct expect_fn final { template struct eval final @@ -357,16 +360,18 @@ namespace ut return *this; } }; - } expect{}; + }; + + export inline constexpr expect_fn expect{}; - struct suite final + export struct suite final { suite(auto&& tests) { tests(); } }; namespace detail { - template + export template struct test final { constexpr auto operator=(auto test) const @@ -376,7 +381,7 @@ namespace ut } }; - struct runtime_test final + export struct runtime_test final { std::string_view name{}; @@ -388,16 +393,16 @@ namespace ut }; } - constexpr auto test(const std::string_view name) { return detail::runtime_test{name}; } + export constexpr auto test(const std::string_view name) { return detail::runtime_test{name}; } - template + export template [[nodiscard]] constexpr auto operator""_test() { return detail::test{}; } #if __cpp_exceptions - template + export template constexpr auto throws(Callable&& c, Args&&... args) { try { @@ -411,4 +416,4 @@ namespace ut #endif } -using ut::operator""_test; +export using ut::operator""_test; From e559c0055eb10391fd278fb88b107cbfa54fdcf4 Mon Sep 17 00:00:00 2001 From: Yan Romao Date: Sat, 28 Feb 2026 18:21:34 +0000 Subject: [PATCH 04/19] Readme improvements --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 77a45bd..d7950e1 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ A simple and fast compiling unit test library. ### Features -- Single header +#### Single header To enable compile time testing, set the option `UT_COMPILE_TIME` to ON. For example: ```cmake @@ -21,7 +21,9 @@ set(UT_COMPILE_TIME ON) Runtime testing is always enabled. -The library supports C++ modules. To enable, set the option `UT_ENABLE_MODULES` to ON. For example: +#### Modules support + +The library supports C++ 20/23 modules. To enable, set the option `UT_ENABLE_MODULES` to ON. For example: ```cmake set (UT_ENABLE_MODULES ON) ``` From cf2ccd36d67add9c8693abe7d3fee4a5ffeb0484 Mon Sep 17 00:00:00 2001 From: DockedFerret800 Date: Sun, 1 Mar 2026 00:24:51 +0000 Subject: [PATCH 05/19] Fix CI --- .github/workflows/modules.yml | 9 ++++++++- .github/workflows/msvc.yml | 2 +- CMakeLists.txt | 4 ++-- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/.github/workflows/modules.yml b/.github/workflows/modules.yml index da770f2..714e7cc 100644 --- a/.github/workflows/modules.yml +++ b/.github/workflows/modules.yml @@ -20,7 +20,9 @@ jobs: matrix: include: - runs_on: windows-2025-vs2026 - compiler: msvc-14.50 + compiler: msvc-14.51 + cc: cl + cxx: cl configure_preset: "x64" build_preset: "x64-build" test_preset : "x64-test" @@ -32,6 +34,8 @@ jobs: # test_preset : "linux-debug-test" - runs_on: ubuntu-latest compiler: clang-18 + cc: clang + cxx: clang++ configure_preset: "linux-debug" build_preset: "linux-debug-build" test_preset : "linux-debug-test" @@ -43,6 +47,9 @@ jobs: - name: "Build & Test" uses: lukka/run-cmake@v10 + env: + CC: ${{ matrix.cc }} + CXX: ${{ matrix.cxx }} with: configurePreset: ${{ matrix.configure_preset }} configurePresetAdditionalArgs: "['-DUT_ENABLE_MODULES=ON']" diff --git a/.github/workflows/msvc.yml b/.github/workflows/msvc.yml index 99686a3..a6b4798 100644 --- a/.github/workflows/msvc.yml +++ b/.github/workflows/msvc.yml @@ -19,7 +19,7 @@ env: jobs: build: - runs-on: windows-latest + runs-on: windows-2025-vs2026 timeout-minutes: 10 strategy: diff --git a/CMakeLists.txt b/CMakeLists.txt index 8960a0d..9f2dba9 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 4.2) +cmake_minimum_required(VERSION 3.31) # Raise to 4.3 on availability global release and availability in CI/CD set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD "d0edc3af-4c50-42ea-a356-e2862fe7a444") # Should be removed after 4.3 is released @@ -13,7 +13,7 @@ project( include(cmake/project-is-top-level.cmake) include(cmake/variables.cmake) -option(UT_ENABLE_MODULES "Enable modules with import std" OFF) +option(UT_ENABLE_MODULES "Enable modules with import std" ON) option(UT_COMPILE_TIME "Enable compile time features" ON) if (UT_ENABLE_MODULES) From 64a2dec5c11768e3ef426ef596c916b533aa649f Mon Sep 17 00:00:00 2001 From: DockedFerret800 Date: Sun, 1 Mar 2026 00:36:39 +0000 Subject: [PATCH 06/19] Typo correction in the workflow file --- .github/workflows/modules.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/modules.yml b/.github/workflows/modules.yml index 714e7cc..6bedb99 100644 --- a/.github/workflows/modules.yml +++ b/.github/workflows/modules.yml @@ -20,7 +20,7 @@ jobs: matrix: include: - runs_on: windows-2025-vs2026 - compiler: msvc-14.51 + compiler: msvc-14.50 cc: cl cxx: cl configure_preset: "x64" From 1870324258b050abb99aac4ca7195e74081faee1 Mon Sep 17 00:00:00 2001 From: DockedFerret800 Date: Sun, 1 Mar 2026 00:37:33 +0000 Subject: [PATCH 07/19] Modules OFF by default --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9f2dba9..5d94774 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,7 +13,7 @@ project( include(cmake/project-is-top-level.cmake) include(cmake/variables.cmake) -option(UT_ENABLE_MODULES "Enable modules with import std" ON) +option(UT_ENABLE_MODULES "Enable modules with import std" OFF) option(UT_COMPILE_TIME "Enable compile time features" ON) if (UT_ENABLE_MODULES) From d868ff92f937de13df10a488462a82fd2d626f99 Mon Sep 17 00:00:00 2001 From: DockedFerret800 Date: Sun, 1 Mar 2026 12:22:19 +0000 Subject: [PATCH 08/19] Fix workflows --- .github/workflows/modules.yml | 24 +++++++++++------------- CMakePresets.json | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 13 deletions(-) diff --git a/.github/workflows/modules.yml b/.github/workflows/modules.yml index 6bedb99..8a580c9 100644 --- a/.github/workflows/modules.yml +++ b/.github/workflows/modules.yml @@ -21,28 +21,26 @@ jobs: include: - runs_on: windows-2025-vs2026 compiler: msvc-14.50 - cc: cl - cxx: cl - configure_preset: "x64" - build_preset: "x64-build" - test_preset : "x64-test" + configure_preset: "x64-vs" + build_preset: "x64-vs-build" + test_preset : "x64-vs-test" # To be uncommented once GitHub Actions add GCC 15 or 16 with Ubuntu 26 # - runs_on: ubuntu-latest # compiler: gcc-15 # configure_preset: "linux-debug" # build_preset: "linux-debug-build" # test_preset : "linux-debug-test" - - runs_on: ubuntu-latest - compiler: clang-18 - cc: clang - cxx: clang++ - configure_preset: "linux-debug" - build_preset: "linux-debug-build" - test_preset : "linux-debug-test" + # - runs_on: ubuntu-latest + # compiler: clang-18 + # cc: clang + # cxx: clang++ + # configure_preset: "linux-debug" + # build_preset: "linux-debug-build" + # test_preset : "linux-debug-test" steps: - uses: actions/checkout@v6 - + - uses: lukka/get-cmake@latest - name: "Build & Test" diff --git a/CMakePresets.json b/CMakePresets.json index 5b1fd42..492ad86 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -53,6 +53,24 @@ "strategy": "external" } }, + { + "name": "windows-vs-base", + "hidden": true, + "generator": "Visual Studio 18 2026", + "binaryDir": "${sourceDir}/out/build/${presetName}", + "installDir": "${sourceDir}/out/install/${presetName}", + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Windows" + } + }, + { + "name": "x64-vs", + "displayName": "x64 (VS generator)", + "inherits": "windows-vs-base", + "architecture": "x64" + }, { "name": "linux-debug", "displayName": "Linux Debug", @@ -74,6 +92,11 @@ "name": "x64-build", "configurePreset": "x64" }, + { + "name": "x64-vs-build", + "configurePreset": "x64-vs", + "configuration": "Debug" + }, { "name": "x64-preview-build", "configurePreset": "x64-preview" @@ -88,6 +111,15 @@ "name": "x64-test", "configurePreset": "x64" }, + + { + "name": "x64-vs-test", + "configurePreset": "x64-vs", + "configuration": "Debug", + "output": { + "outputOnFailure": true + } + }, { "name": "x64-preview-test", "configurePreset": "x64-preview" From 1f11d97dba91ce23abab7e566c069593642f170e Mon Sep 17 00:00:00 2001 From: DockedFerret800 Date: Wed, 4 Mar 2026 18:40:23 +0000 Subject: [PATCH 09/19] Fix install and set UT_COMPILE_TIME to OFF by default. --- CMakeLists.txt | 6 ++++-- cmake/install-rules.cmake | 2 -- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5d94774..a412c36 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,7 +14,7 @@ include(cmake/project-is-top-level.cmake) include(cmake/variables.cmake) option(UT_ENABLE_MODULES "Enable modules with import std" OFF) -option(UT_COMPILE_TIME "Enable compile time features" ON) +option(UT_COMPILE_TIME "Enable compile time features" OFF) if (UT_ENABLE_MODULES) if (NOT CMAKE_GENERATOR MATCHES "Visual Studio") ## Support should be added with 4.4, todo: check when available @@ -66,7 +66,9 @@ set_property(TARGET ${PROJECT_NAME}_${PROJECT_NAME} PROPERTY EXPORT_NAME ${PROJE target_include_directories( ${PROJECT_NAME}_${PROJECT_NAME} ${warning_guard} - INTERFACE "$" + INTERFACE + "$" + "$" ) if(NOT CMAKE_SKIP_INSTALL_RULES) diff --git a/cmake/install-rules.cmake b/cmake/install-rules.cmake index be5333e..155ddc1 100644 --- a/cmake/install-rules.cmake +++ b/cmake/install-rules.cmake @@ -21,7 +21,6 @@ if(UT_ENABLE_MODULES) install( TARGETS ${PROJECT_NAME}_${PROJECT_NAME} EXPORT ${PROJECT_NAME}Targets - INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" FILE_SET modules DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" CXX_MODULES_BMI DESTINATION "" COMPONENT ${PROJECT_NAME}_Development @@ -30,7 +29,6 @@ else() install( TARGETS ${PROJECT_NAME}_${PROJECT_NAME} EXPORT ${PROJECT_NAME}Targets - INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" COMPONENT ${PROJECT_NAME}_Development ) endif() From a86e7de44c505294dc1247ac2216c4e4b9e2265e Mon Sep 17 00:00:00 2001 From: DockedFerret800 Date: Wed, 4 Mar 2026 18:51:29 +0000 Subject: [PATCH 10/19] Separe modules test --- cmake/dev-mode.cmake | 6 ++++++ tests/ut_module_consumer_tests.cpp | 16 ++++++++++++++++ vc140.pdb | Bin 0 -> 36864 bytes 3 files changed, 22 insertions(+) create mode 100644 tests/ut_module_consumer_tests.cpp create mode 100644 vc140.pdb diff --git a/cmake/dev-mode.cmake b/cmake/dev-mode.cmake index 8bc7345..292596d 100644 --- a/cmake/dev-mode.cmake +++ b/cmake/dev-mode.cmake @@ -78,3 +78,9 @@ set_tests_properties(ut_run_no_match PROPERTIES ENVIRONMENT "UT_RUN=nonexistent" PASS_REGULAR_EXPRESSION "tests: 0 \\(0 passed" ) + +if (UT_ENABLE_MODULES) + add_executable(ut_module_consumer_tests "${PROJECT_SOURCE_DIR}/tests/ut_module_consumer_tests.cpp") + target_link_libraries(ut_module_consumer_tests PRIVATE ${PROJECT_NAME}::${PROJECT_NAME}) + add_test(NAME ut_module_consumer_import COMMAND ut_module_consumer_tests) +endif() diff --git a/tests/ut_module_consumer_tests.cpp b/tests/ut_module_consumer_tests.cpp new file mode 100644 index 0000000..662ef0e --- /dev/null +++ b/tests/ut_module_consumer_tests.cpp @@ -0,0 +1,16 @@ +import ut; + +using namespace ut; + +suite filter_tests = [] { + "alpha"_test = [] { expect(true); }; + + "beta"_test = [] { expect(true); }; + + "gamma"_test = [] { expect(true); }; + + test("delta test") = [] { expect(true); }; + + test("epsilon test") = [] { expect(true); }; +}; +int main() {} diff --git a/vc140.pdb b/vc140.pdb new file mode 100644 index 0000000000000000000000000000000000000000..63eb5bd7b345e4a919637321b42913ecad240956 GIT binary patch literal 36864 zcmeI)F-ikL6adf-nxFSsUc53g=Ux1;O3w0GERwbK6JB0X+*I-48&=Yu4S z?Ifv%v>qZ25y#pz`6NJq009C72oNAZfB*pk1QtZ#=Pxq|5FkK+009C72oNAZfB=Cd z7ua3$^En;?0t5&UAV7cs0RjXF5Fn6c&+Tq)=kaa075Wt(4t|?oFX`FS=g0l~>v(V0 z!VvraW8}y8i@ik zYXSra5FkK+009C72oNApZGq;vTRVwc4FB^tiI8Gn#OfqZe*uW|FrBQ=vid(t7z79q zAV7cs0RjXF5FkKcF$Bu$|3;|v^Ywq(N!0&kD%J=PAV7cs0RjXF5FkK+Ks5!*>i@d> zznTxrt_TnyK!5-N0t5&UAV7e?oC5JZf3g2x{XZwB+XxUKK!5-N0t5&UAV7dXMFsu= D!s8yB literal 0 HcmV?d00001 From fb27fb1d7efd46e9f5689af211d0eb138d18736d Mon Sep 17 00:00:00 2001 From: DockedFerret800 Date: Wed, 4 Mar 2026 18:52:18 +0000 Subject: [PATCH 11/19] Remove build files --- vc140.pdb | Bin 36864 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 vc140.pdb diff --git a/vc140.pdb b/vc140.pdb deleted file mode 100644 index 63eb5bd7b345e4a919637321b42913ecad240956..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 36864 zcmeI)F-ikL6adf-nxFSsUc53g=Ux1;O3w0GERwbK6JB0X+*I-48&=Yu4S z?Ifv%v>qZ25y#pz`6NJq009C72oNAZfB*pk1QtZ#=Pxq|5FkK+009C72oNAZfB=Cd z7ua3$^En;?0t5&UAV7cs0RjXF5Fn6c&+Tq)=kaa075Wt(4t|?oFX`FS=g0l~>v(V0 z!VvraW8}y8i@ik zYXSra5FkK+009C72oNApZGq;vTRVwc4FB^tiI8Gn#OfqZe*uW|FrBQ=vid(t7z79q zAV7cs0RjXF5FkKcF$Bu$|3;|v^Ywq(N!0&kD%J=PAV7cs0RjXF5FkK+Ks5!*>i@d> zznTxrt_TnyK!5-N0t5&UAV7e?oC5JZf3g2x{XZwB+XxUKK!5-N0t5&UAV7dXMFsu= D!s8yB From ccb668789f1d16d426a5c9e57e3b1062796c1830 Mon Sep 17 00:00:00 2001 From: Stephen Berry Date: Fri, 6 Mar 2026 15:38:23 -0600 Subject: [PATCH 12/19] Fix install path --- CMakeLists.txt | 2 +- cmake/install-rules.cmake | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a412c36..b470b95 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -68,7 +68,7 @@ target_include_directories( ${PROJECT_NAME}_${PROJECT_NAME} ${warning_guard} INTERFACE "$" - "$" + "$" ) if(NOT CMAKE_SKIP_INSTALL_RULES) diff --git a/cmake/install-rules.cmake b/cmake/install-rules.cmake index 155ddc1..6ed9011 100644 --- a/cmake/install-rules.cmake +++ b/cmake/install-rules.cmake @@ -1,7 +1,3 @@ -if(PROJECT_IS_TOP_LEVEL) - set(CMAKE_INSTALL_INCLUDEDIR include/${PROJECT_NAME} CACHE PATH "") -endif() - # Project is configured with no languages, so tell GNUInstallDirs the lib dir set(CMAKE_INSTALL_LIBDIR lib CACHE PATH "") From e1992e0f6f07f2cd10fe24394e270664b6273d70 Mon Sep 17 00:00:00 2001 From: Stephen Berry Date: Sat, 7 Mar 2026 08:58:07 -0600 Subject: [PATCH 13/19] Use CMake 3.31 --- CMakePresets.json | 4 ++-- README.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CMakePresets.json b/CMakePresets.json index 492ad86..008091a 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -1,8 +1,8 @@ { "version": 9, "cmakeMinimumRequired": { - "major": 4, - "minor": 2, + "major": 3, + "minor": 31, "patch": 0 }, "configurePresets": [ diff --git a/README.md b/README.md index d7950e1..35e5d3f 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ UT_RUN="[test1,test2,test3]" ./my_tests ### Requirements - C++23 -- CMake 4.2 +- CMake 3.31 ## Example From b3242ba8c3a56d69255fb8f6aeeb51b222c9d8d4 Mon Sep 17 00:00:00 2001 From: Stephen Berry Date: Sat, 7 Mar 2026 09:04:48 -0600 Subject: [PATCH 14/19] Use windows-latest --- .github/workflows/msvc.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/msvc.yml b/.github/workflows/msvc.yml index a6b4798..99686a3 100644 --- a/.github/workflows/msvc.yml +++ b/.github/workflows/msvc.yml @@ -19,7 +19,7 @@ env: jobs: build: - runs-on: windows-2025-vs2026 + runs-on: windows-latest timeout-minutes: 10 strategy: From 0de68b585d1d73272e60aba5b4a5b7dabadd5a53 Mon Sep 17 00:00:00 2001 From: Stephen Berry Date: Sat, 7 Mar 2026 09:08:59 -0600 Subject: [PATCH 15/19] Remove dead code in ut.ixx --- modules/ut.ixx | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/modules/ut.ixx b/modules/ut.ixx index 6dd2157..9b68004 100644 --- a/modules/ut.ixx +++ b/modules/ut.ixx @@ -330,8 +330,6 @@ namespace ut return log{eval{test_passed, loc}.passed}; } -#if __cplusplus >= 202300L - // if we have C++23 template requires std::convertible_to constexpr auto operator[](T&& test_passed, @@ -339,14 +337,6 @@ namespace ut { return log{eval{test_passed, loc}.passed}; } -#else - template - requires std::convertible_to - constexpr auto operator[](T&& test_passed) const - { - return log{eval{test_passed, std::source_location::current()}.passed}; - } -#endif private: struct log final From be590b8b04aa8775618b74e84a1edb3f94e0438d Mon Sep 17 00:00:00 2001 From: Stephen Berry Date: Sat, 7 Mar 2026 09:17:55 -0600 Subject: [PATCH 16/19] normalize CMake indentation --- .github/workflows/modules.yml | 2 +- .gitignore | 2 +- CMakeLists.txt | 44 +++++++++++++++++------------------ CMakePresets.json | 2 +- cmake/install-rules.cmake | 2 +- 5 files changed, 26 insertions(+), 26 deletions(-) diff --git a/.github/workflows/modules.yml b/.github/workflows/modules.yml index 8a580c9..fdc831e 100644 --- a/.github/workflows/modules.yml +++ b/.github/workflows/modules.yml @@ -52,4 +52,4 @@ jobs: configurePreset: ${{ matrix.configure_preset }} configurePresetAdditionalArgs: "['-DUT_ENABLE_MODULES=ON']" buildPreset: ${{ matrix.build_preset }} - testPreset: ${{ matrix.test_preset }} \ No newline at end of file + testPreset: ${{ matrix.test_preset }} diff --git a/.gitignore b/.gitignore index d7edafb..e72a180 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,4 @@ compile_commands.json .vscode .cache .DS_Store -.vs \ No newline at end of file +.vs diff --git a/CMakeLists.txt b/CMakeLists.txt index b470b95..d8cb519 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,9 +5,9 @@ set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD "d0edc3af-4c50-42ea-a356-e2862fe7a444") # include(cmake/prelude.cmake) project( - ut - VERSION 1.2.0 - LANGUAGES CXX + ut + VERSION 1.2.0 + LANGUAGES CXX ) include(cmake/project-is-top-level.cmake) @@ -16,13 +16,13 @@ include(cmake/variables.cmake) option(UT_ENABLE_MODULES "Enable modules with import std" OFF) option(UT_COMPILE_TIME "Enable compile time features" OFF) -if (UT_ENABLE_MODULES) - if (NOT CMAKE_GENERATOR MATCHES "Visual Studio") ## Support should be added with 4.4, todo: check when available +if(UT_ENABLE_MODULES) + if(NOT CMAKE_GENERATOR MATCHES "Visual Studio") ## Support should be added with 4.4, todo: check when available set(CMAKE_CXX_MODULE_STD 1) - endif() + endif() endif() -if (UT_ENABLE_MODULES) +if(UT_ENABLE_MODULES) add_library(${PROJECT_NAME}_${PROJECT_NAME} STATIC) set(UT_LIB_TYPE PUBLIC) else() @@ -31,7 +31,7 @@ else() endif() add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME}_${PROJECT_NAME}) -if (PROJECT_IS_TOP_LEVEL) +if(PROJECT_IS_TOP_LEVEL) include(cmake/dev-mode.cmake) endif() @@ -46,29 +46,29 @@ if(UT_ENABLE_MODULES) "${PROJECT_SOURCE_DIR}/modules/ut.ixx" ) - target_compile_definitions(${PROJECT_NAME}_${PROJECT_NAME} PRIVATE UT_ENABLE_MODULES) + target_compile_definitions(${PROJECT_NAME}_${PROJECT_NAME} PRIVATE UT_ENABLE_MODULES) endif() -if (UT_COMPILE_TIME) - target_compile_definitions(${PROJECT_NAME}_${PROJECT_NAME} ${UT_LIB_TYPE} UT_COMPILE_TIME) +if(UT_COMPILE_TIME) + target_compile_definitions(${PROJECT_NAME}_${PROJECT_NAME} ${UT_LIB_TYPE} UT_COMPILE_TIME) endif() -if (MSVC) - string(REGEX MATCH "\/cl(.exe)?$" matched_cl ${CMAKE_CXX_COMPILER}) - if (matched_cl) - # for a C++ standards compliant preprocessor, not needed for clang-cl - target_compile_options(${PROJECT_NAME}_${PROJECT_NAME} INTERFACE "/Zc:preprocessor" /GL /permissive- /Zc:lambda) - target_link_options(${PROJECT_NAME}_${PROJECT_NAME} INTERFACE /LTCG /INCREMENTAL:NO) - endif() +if(MSVC) + string(REGEX MATCH "\/cl(.exe)?$" matched_cl ${CMAKE_CXX_COMPILER}) + if(matched_cl) + # for a C++ standards compliant preprocessor, not needed for clang-cl + target_compile_options(${PROJECT_NAME}_${PROJECT_NAME} INTERFACE "/Zc:preprocessor" /GL /permissive- /Zc:lambda) + target_link_options(${PROJECT_NAME}_${PROJECT_NAME} INTERFACE /LTCG /INCREMENTAL:NO) + endif() endif() set_property(TARGET ${PROJECT_NAME}_${PROJECT_NAME} PROPERTY EXPORT_NAME ${PROJECT_NAME}) target_include_directories( - ${PROJECT_NAME}_${PROJECT_NAME} ${warning_guard} - INTERFACE - "$" - "$" + ${PROJECT_NAME}_${PROJECT_NAME} ${warning_guard} + INTERFACE + "$" + "$" ) if(NOT CMAKE_SKIP_INSTALL_RULES) diff --git a/CMakePresets.json b/CMakePresets.json index 008091a..cbae41f 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -129,4 +129,4 @@ "configurePreset": "linux-debug" } ] -} \ No newline at end of file +} diff --git a/cmake/install-rules.cmake b/cmake/install-rules.cmake index 6ed9011..e3ef951 100644 --- a/cmake/install-rules.cmake +++ b/cmake/install-rules.cmake @@ -18,7 +18,7 @@ if(UT_ENABLE_MODULES) TARGETS ${PROJECT_NAME}_${PROJECT_NAME} EXPORT ${PROJECT_NAME}Targets FILE_SET modules DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" - CXX_MODULES_BMI DESTINATION "" + CXX_MODULES_BMI DESTINATION "" # BMIs are not portable; suppress installation COMPONENT ${PROJECT_NAME}_Development ) else() From 1b33eb65a363295772d1bc8456e736c316d59f18 Mon Sep 17 00:00:00 2001 From: DockedFerret800 Date: Sat, 7 Mar 2026 15:30:44 +0000 Subject: [PATCH 17/19] Remove dead code in ut.hpp --- include/ut/ut.hpp | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/include/ut/ut.hpp b/include/ut/ut.hpp index 9b6b258..fff1921 100644 --- a/include/ut/ut.hpp +++ b/include/ut/ut.hpp @@ -333,8 +333,6 @@ namespace ut return log{eval{test_passed, loc}.passed}; } -#if __cplusplus >= 202300L - // if we have C++23 template requires std::convertible_to constexpr auto operator[](T&& test_passed, @@ -342,14 +340,6 @@ namespace ut { return log{eval{test_passed, loc}.passed}; } -#else - template - requires std::convertible_to - constexpr auto operator[](T&& test_passed) const - { - return log{eval{test_passed, std::source_location::current()}.passed}; - } -#endif private: struct log final From e820bc3e8a00a9dfe9995d2e1e187a1a091b02de Mon Sep 17 00:00:00 2001 From: Stephen Berry Date: Sat, 7 Mar 2026 09:51:30 -0600 Subject: [PATCH 18/19] Check __cpp_multidimensional_subscript for MSVC --- include/ut/ut.hpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/include/ut/ut.hpp b/include/ut/ut.hpp index fff1921..2cef147 100644 --- a/include/ut/ut.hpp +++ b/include/ut/ut.hpp @@ -333,6 +333,7 @@ namespace ut return log{eval{test_passed, loc}.passed}; } +#if __cpp_multidimensional_subscript >= 202211L template requires std::convertible_to constexpr auto operator[](T&& test_passed, @@ -340,6 +341,14 @@ namespace ut { return log{eval{test_passed, loc}.passed}; } +#else + template + requires std::convertible_to + constexpr auto operator[](T&& test_passed) const + { + return log{eval{test_passed, std::source_location::current()}.passed}; + } +#endif private: struct log final From 6fcc7388bf145a44740da80adb4d233e13b54721 Mon Sep 17 00:00:00 2001 From: Stephen Berry Date: Sat, 7 Mar 2026 09:59:50 -0600 Subject: [PATCH 19/19] Update ut.hpp --- include/ut/ut.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/ut/ut.hpp b/include/ut/ut.hpp index 2cef147..10af7f9 100644 --- a/include/ut/ut.hpp +++ b/include/ut/ut.hpp @@ -333,7 +333,7 @@ namespace ut return log{eval{test_passed, loc}.passed}; } -#if __cpp_multidimensional_subscript >= 202211L +#if __cpp_multidimensional_subscript >= 202211L && !defined(_MSC_VER) template requires std::convertible_to constexpr auto operator[](T&& test_passed,