From 03e897b5f6edb6a028e32128eb0940118000bbad Mon Sep 17 00:00:00 2001 From: Philip Top Date: Fri, 14 Nov 2025 05:39:21 -0800 Subject: [PATCH 01/29] test for using CLI11 in a module --- include/CLI/TypeTools.hpp | 4 ++-- include/CLI/impl/App_inl.hpp | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/include/CLI/TypeTools.hpp b/include/CLI/TypeTools.hpp index 20ca59b3b..e1de8ee4c 100644 --- a/include/CLI/TypeTools.hpp +++ b/include/CLI/TypeTools.hpp @@ -33,10 +33,10 @@ namespace CLI { namespace detail { // Based generally on https://rmf.io/cxx11/almost-static-if /// Simple empty scoped class -enum class enabler : std::uint8_t {}; +enum class enabler : std::uint8_t {null}; /// An instance to use in EnableIf -constexpr enabler dummy = {}; +constexpr enabler dummy = {enabler::null}; } // namespace detail /// A copy of enable_if_t from C++14, compatible with C++11. diff --git a/include/CLI/impl/App_inl.hpp b/include/CLI/impl/App_inl.hpp index c8b33cbff..3ccdc98d0 100644 --- a/include/CLI/impl/App_inl.hpp +++ b/include/CLI/impl/App_inl.hpp @@ -1274,6 +1274,7 @@ CLI11_INLINE void App::_process_help_flags(CallbackPriority priority, bool trigg if(help_ptr != nullptr && help_ptr->count() > 0 && help_ptr->get_callback_priority() == priority) trigger_help = true; + if(help_all_ptr != nullptr && help_all_ptr->count() > 0 && help_all_ptr->get_callback_priority() == priority) if(help_all_ptr != nullptr && help_all_ptr->count() > 0 && help_all_ptr->get_callback_priority() == priority) trigger_all_help = true; From 9f5e375333f1a60f436dc5a93e4f804e1972be7f Mon Sep 17 00:00:00 2001 From: Philip Top Date: Fri, 14 Nov 2025 07:48:13 -0800 Subject: [PATCH 02/29] try to update some code to support inclusion in C++ modules --- .github/workflows/tests.yml | 18 ++ CMakeLists.txt | 4 + include/CLI/Macros.hpp | 7 + include/CLI/StringTools.hpp | 2 +- include/CLI/TypeTools.hpp | 4 +- include/CLI/impl/StringTools_inl.hpp | 291 ++++++++++++++------------- src/CMakeLists.txt | 4 + tests/CMakeLists.txt | 6 + tests/module_test/CMakeLists.txt | 20 ++ tests/module_test/LICENSE | 22 ++ tests/module_test/cmodule.ixx | 17 ++ tests/module_test/module_test.cpp | 21 ++ 12 files changed, 273 insertions(+), 143 deletions(-) create mode 100644 tests/module_test/CMakeLists.txt create mode 100644 tests/module_test/LICENSE create mode 100644 tests/module_test/cmodule.ixx create mode 100644 tests/module_test/module_test.cpp diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 028881b2e..10b3eed67 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -295,6 +295,24 @@ jobs: - name: Run tests run: ctest --output-on-failure -L Packaging working-directory: build + + install-module: + name: install module tests + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + submodules: true + - name: Configure + run: cmake -S . -B build -DCLI11_INSTALL_PACKAGE_TESTS=ON -DCLI11_MODULE=ON -DCMAKE_INSTALL_PREFIX=/home/runner/work/install + - name: Build + run: cmake --build build -j2 + - name: install + run: cmake --install build + - name: Run tests + run: ctest --output-on-failure -L Packaging + working-directory: build + cmake-config-ubuntu-2204: name: CMake config check (Ubuntu 22.04) diff --git a/CMakeLists.txt b/CMakeLists.txt index e8f6b5422..93e10aea3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -65,6 +65,10 @@ endif() option(CLI11_WARNINGS_AS_ERRORS "Turn all warnings into errors (for CI)") option(CLI11_SINGLE_FILE "Generate a single header file") option(CLI11_PRECOMPILED "Generate a precompiled static library instead of a header-only" OFF) +if (CMAKE_CXX_STANDARD GREATER 19) + option(CLI11_MODULE "modify some code to support inclusion of CLI11 in a module" OFF) +endif() + cmake_dependent_option(CLI11_SANITIZERS "Download the sanitizers CMake config" OFF "NOT CMAKE_VERSION VERSION_LESS 3.15" OFF) diff --git a/include/CLI/Macros.hpp b/include/CLI/Macros.hpp index 97dff7403..486d1bc01 100644 --- a/include/CLI/Macros.hpp +++ b/include/CLI/Macros.hpp @@ -175,4 +175,11 @@ #else #define CLI11_INLINE inline #endif + +/** Module inline**/ +#ifdef CLI11_MODULE +#define CLI11_MODULE_INLINE inline +#else +#define CLI11_MODULE_INLINE +#endif // [CLI11:macros_hpp:end] diff --git a/include/CLI/StringTools.hpp b/include/CLI/StringTools.hpp index d7f5ce1ab..5d7601d12 100644 --- a/include/CLI/StringTools.hpp +++ b/include/CLI/StringTools.hpp @@ -45,7 +45,7 @@ using enums::operator<<; namespace detail { /// a constant defining an expected max vector size defined to be a big number that could be multiplied by 4 and not /// produce overflow for some expected uses -constexpr int expected_max_vector_size{1 << 29}; +CLI11_MODULE_INLINE constexpr int expected_max_vector_size{1 << 29}; // Based on http://stackoverflow.com/questions/236129/split-a-string-in-c /// Split a string by a delim CLI11_INLINE std::vector split(const std::string &s, char delim); diff --git a/include/CLI/TypeTools.hpp b/include/CLI/TypeTools.hpp index e1de8ee4c..54cbb4168 100644 --- a/include/CLI/TypeTools.hpp +++ b/include/CLI/TypeTools.hpp @@ -33,10 +33,10 @@ namespace CLI { namespace detail { // Based generally on https://rmf.io/cxx11/almost-static-if /// Simple empty scoped class -enum class enabler : std::uint8_t {null}; +enum class enabler : std::uint8_t {}; /// An instance to use in EnableIf -constexpr enabler dummy = {enabler::null}; +CLI11_MODULE_INLINE constexpr enabler dummy = {}; } // namespace detail /// A copy of enable_if_t from C++14, compatible with C++11. diff --git a/include/CLI/impl/StringTools_inl.hpp b/include/CLI/impl/StringTools_inl.hpp index 7773ae5b3..c3ef96e92 100644 --- a/include/CLI/impl/StringTools_inl.hpp +++ b/include/CLI/impl/StringTools_inl.hpp @@ -21,174 +21,185 @@ namespace CLI { // [CLI11:string_tools_inl_hpp:verbatim] -namespace detail { -CLI11_INLINE std::vector split(const std::string &s, char delim) { - std::vector elems; - // Check to see if empty string, give consistent result - if(s.empty()) { - elems.emplace_back(); - } else { - std::stringstream ss; - ss.str(s); - std::string item; - while(std::getline(ss, item, delim)) { - elems.push_back(item); + namespace detail { + CLI11_INLINE std::vector split(const std::string& s, char delim) { + std::vector elems; + // Check to see if empty string, give consistent result + if (s.empty()) { + elems.emplace_back(); + } + else { + std::stringstream ss; + ss.str(s); + std::string item; + while (std::getline(ss, item, delim)) { + elems.push_back(item); + } + } + return elems; } - } - return elems; -} -CLI11_INLINE std::string <rim(std::string &str) { - auto it = std::find_if(str.begin(), str.end(), [](char ch) { return !std::isspace(ch, std::locale()); }); - str.erase(str.begin(), it); - return str; -} + CLI11_INLINE std::string& ltrim(std::string& str) { + auto it = std::find_if(str.begin(), str.end(), [](char ch) { return !std::isspace(ch, std::locale()); }); + str.erase(str.begin(), it); + return str; + } -CLI11_INLINE std::string <rim(std::string &str, const std::string &filter) { - auto it = std::find_if(str.begin(), str.end(), [&filter](char ch) { return filter.find(ch) == std::string::npos; }); - str.erase(str.begin(), it); - return str; -} + CLI11_INLINE std::string& ltrim(std::string& str, const std::string& filter) { + auto it = std::find_if(str.begin(), str.end(), [&filter](char ch) { return filter.find(ch) == std::string::npos; }); + str.erase(str.begin(), it); + return str; + } -CLI11_INLINE std::string &rtrim(std::string &str) { - auto it = std::find_if(str.rbegin(), str.rend(), [](char ch) { return !std::isspace(ch, std::locale()); }); - str.erase(it.base(), str.end()); - return str; -} + CLI11_INLINE std::string& rtrim(std::string& str) { + auto it = std::find_if(str.rbegin(), str.rend(), [](char ch) { return !std::isspace(ch, std::locale()); }); + str.erase(it.base(), str.end()); + return str; + } -CLI11_INLINE std::string &rtrim(std::string &str, const std::string &filter) { - auto it = - std::find_if(str.rbegin(), str.rend(), [&filter](char ch) { return filter.find(ch) == std::string::npos; }); - str.erase(it.base(), str.end()); - return str; -} + CLI11_INLINE std::string& rtrim(std::string& str, const std::string& filter) { + auto it = + std::find_if(str.rbegin(), str.rend(), [&filter](char ch) { return filter.find(ch) == std::string::npos; }); + str.erase(it.base(), str.end()); + return str; + } -CLI11_INLINE std::string &remove_quotes(std::string &str) { - if(str.length() > 1 && (str.front() == '"' || str.front() == '\'' || str.front() == '`')) { - if(str.front() == str.back()) { - str.pop_back(); - str.erase(str.begin(), str.begin() + 1); + CLI11_INLINE std::string& remove_quotes(std::string& str) { + if (str.length() > 1 && (str.front() == '"' || str.front() == '\'' || str.front() == '`')) { + if (str.front() == str.back()) { + str.pop_back(); + str.erase(str.begin(), str.begin() + 1); + } + } + return str; } - } - return str; -} -CLI11_INLINE std::string &remove_outer(std::string &str, char key) { - if(str.length() > 1 && (str.front() == key)) { - if(str.front() == str.back()) { - str.pop_back(); - str.erase(str.begin(), str.begin() + 1); + CLI11_INLINE std::string& remove_outer(std::string& str, char key) { + if (str.length() > 1 && (str.front() == key)) { + if (str.front() == str.back()) { + str.pop_back(); + str.erase(str.begin(), str.begin() + 1); + } + } + return str; } - } - return str; -} -CLI11_INLINE std::string fix_newlines(const std::string &leader, std::string input) { - std::string::size_type n = 0; - while(n != std::string::npos && n < input.size()) { - n = input.find_first_of("\r\n", n); - if(n != std::string::npos) { - input = input.substr(0, n + 1) + leader + input.substr(n + 1); - n += leader.size(); + CLI11_INLINE std::string fix_newlines(const std::string& leader, std::string input) { + std::string::size_type n = 0; + while (n != std::string::npos && n < input.size()) { + n = input.find_first_of("\r\n", n); + if (n != std::string::npos) { + input = input.substr(0, n + 1) + leader + input.substr(n + 1); + n += leader.size(); + } + } + return input; } - } - return input; -} -CLI11_INLINE std::ostream &format_aliases(std::ostream &out, const std::vector &aliases, std::size_t wid) { - if(!aliases.empty()) { - out << std::setw(static_cast(wid)) << " aliases: "; - bool front = true; - for(const auto &alias : aliases) { - if(!front) { - out << ", "; - } else { - front = false; + CLI11_INLINE std::ostream& format_aliases(std::ostream& out, const std::vector& aliases, std::size_t wid) { + if (!aliases.empty()) { + out << std::setw(static_cast(wid)) << " aliases: "; + bool front = true; + for (const auto& alias : aliases) { + if (!front) { + out << ", "; + } + else { + front = false; + } + out << detail::fix_newlines(" ", alias); + } + out << "\n"; } - out << detail::fix_newlines(" ", alias); + return out; } - out << "\n"; - } - return out; -} -CLI11_INLINE bool valid_name_string(const std::string &str) { - if(str.empty() || !valid_first_char(str[0])) { - return false; - } - auto e = str.end(); - for(auto c = str.begin() + 1; c != e; ++c) - if(!valid_later_char(*c)) - return false; - return true; -} + CLI11_INLINE bool valid_name_string(const std::string& str) { + if (str.empty() || !valid_first_char(str[0])) { + return false; + } + auto e = str.end(); + for (auto c = str.begin() + 1; c != e; ++c) + if (!valid_later_char(*c)) + return false; + return true; + } -CLI11_INLINE std::string get_group_separators() { - std::string separators{"_'"}; + CLI11_INLINE std::string get_group_separators() { + std::string separators{ "_'" }; #if CLI11_HAS_RTTI != 0 - char group_separator = std::use_facet>(std::locale()).thousands_sep(); - separators.push_back(group_separator); + char group_separator = std::use_facet>(std::locale()).thousands_sep(); + separators.push_back(group_separator); #endif - return separators; -} - -CLI11_INLINE std::string find_and_replace(std::string str, std::string from, std::string to) { + return separators; + } - std::size_t start_pos = 0; + CLI11_INLINE std::string find_and_replace(std::string str, std::string from, std::string to) { - while((start_pos = str.find(from, start_pos)) != std::string::npos) { - str.replace(start_pos, from.length(), to); - start_pos += to.length(); - } + std::size_t start_pos = 0; - return str; -} + while ((start_pos = str.find(from, start_pos)) != std::string::npos) { + str.replace(start_pos, from.length(), to); + start_pos += to.length(); + } -CLI11_INLINE void remove_default_flag_values(std::string &flags) { - auto loc = flags.find_first_of('{', 2); - while(loc != std::string::npos) { - auto finish = flags.find_first_of("},", loc + 1); - if((finish != std::string::npos) && (flags[finish] == '}')) { - flags.erase(flags.begin() + static_cast(loc), - flags.begin() + static_cast(finish) + 1); + return str; } - loc = flags.find_first_of('{', loc + 1); - } - flags.erase(std::remove(flags.begin(), flags.end(), '!'), flags.end()); -} -CLI11_INLINE std::ptrdiff_t -find_member(std::string name, const std::vector names, bool ignore_case, bool ignore_underscore) { - auto it = std::end(names); - if(ignore_case) { - if(ignore_underscore) { - name = detail::to_lower(detail::remove_underscore(name)); - it = std::find_if(std::begin(names), std::end(names), [&name](std::string local_name) { - return detail::to_lower(detail::remove_underscore(local_name)) == name; - }); - } else { - name = detail::to_lower(name); - it = std::find_if(std::begin(names), std::end(names), [&name](std::string local_name) { - return detail::to_lower(local_name) == name; - }); + CLI11_INLINE void remove_default_flag_values(std::string& flags) { + auto loc = flags.find_first_of('{', 2); + while (loc != std::string::npos) { + auto finish = flags.find_first_of("},", loc + 1); + if ((finish != std::string::npos) && (flags[finish] == '}')) { + flags.erase(flags.begin() + static_cast(loc), + flags.begin() + static_cast(finish) + 1); + } + loc = flags.find_first_of('{', loc + 1); + } + flags.erase(std::remove(flags.begin(), flags.end(), '!'), flags.end()); } - } else if(ignore_underscore) { - name = detail::remove_underscore(name); - it = std::find_if(std::begin(names), std::end(names), [&name](std::string local_name) { - return detail::remove_underscore(local_name) == name; - }); - } else { - it = std::find(std::begin(names), std::end(names), name); - } + CLI11_INLINE std::ptrdiff_t + find_member(std::string name, const std::vector names, bool ignore_case, bool ignore_underscore) { + auto it = std::end(names); + if (ignore_case) { + if (ignore_underscore) { + name = detail::to_lower(detail::remove_underscore(name)); + it = std::find_if(std::begin(names), std::end(names), [&name](std::string local_name) { + return detail::to_lower(detail::remove_underscore(local_name)) == name; + }); + } + else { + name = detail::to_lower(name); + it = std::find_if(std::begin(names), std::end(names), [&name](std::string local_name) { + return detail::to_lower(local_name) == name; + }); + } - return (it != std::end(names)) ? (it - std::begin(names)) : (-1); -} + } + else if (ignore_underscore) { + name = detail::remove_underscore(name); + it = std::find_if(std::begin(names), std::end(names), [&name](std::string local_name) { + return detail::remove_underscore(local_name) == name; + }); + } + else { + it = std::find(std::begin(names), std::end(names), name); + } -static const std::string escapedChars("\b\t\n\f\r\"\\"); -static const std::string escapedCharsCode("btnfr\"\\"); -static const std::string bracketChars{"\"'`[(<{"}; -static const std::string matchBracketChars("\"'`])>}"); + return (it != std::end(names)) ? (it - std::begin(names)) : (-1); + } +#if defined(CLI11_MODULE) + CLI11_MODULE_INLINE const std::string escapedChars{"\b\t\n\f\r\"\\"}; + CLI11_MODULE_INLINE const std::string escapedCharsCode{ "btnfr\"\\" }; + CLI11_MODULE_INLINE const std::string matchBracketChars{ "\"'`])>}" }; + CLI11_MODULE_INLINE const std::string matchBracketChars{ "\"'`])>}" }; +#else +static const std::string& escapedChars{ "\b\t\n\f\r\"\\" }; +static const std::string& escapedCharsCode{ "btnfr\"\\" }; +static const std::string& bracketChars{"\"'`[(<{"}; +static const std::string& matchBracketChars{ "\"'`])>}" }; +#endif CLI11_INLINE bool has_escapable_character(const std::string &str) { return (str.find_first_of(escapedChars) != std::string::npos); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6989f6d0c..3881e058f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -35,6 +35,10 @@ else() set(SYSTEM_INCL "SYSTEM") endif() +if (CLI11_MODULE) + target_compile_definitions(CLI11 ${PUBLIC_OR_INTERFACE} -DCLI11_MODULE=1) +endif() + # Duplicated because CMake adds the current source dir if you don't. target_include_directories( CLI11 ${SYSTEM_INCL} ${PUBLIC_OR_INTERFACE} $ diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 6e05183ef..2648a0363 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -357,6 +357,12 @@ if(CLI11_INSTALL_PACKAGE_TESTS) set_property(TEST find-package-testsC PROPERTY LABELS Packaging) set_property(TEST find-package-testsC PROPERTY DEPENDS find-package-testsB) +if (CMAKE_CXX_STANDARD GREATER 19) + add_test(NAME find-package-module + COMMAND ${CMAKE_COMMAND} --build "${CMAKE_CURRENT_BINARY_DIR}/module_tests" + --config ${CLI11_PACKAGE_TEST_BUILD_TYPE}) +endif() + if(NOT MSVC) # Tests for other CMake projects using the package_config files add_test( diff --git a/tests/module_test/CMakeLists.txt b/tests/module_test/CMakeLists.txt new file mode 100644 index 000000000..227df6a96 --- /dev/null +++ b/tests/module_test/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 3.14...4.2) + +project(CLI11-module-test) + +include(CTest) + +if(CLI11_DIR) + set(CMAKE_PREFIX_PATH ${CLI11_DIR}) +endif() + +# Test the CLI11 CMake package config +find_package(CLI11 2.5 REQUIRED) + +# Test the target +add_executable(module-test module_test.cpp) +target_link_libraries(module-test CLI11::CLI11) +target_compile_definitions(module-test -DCLI11_MODULE=1) + +add_test(NAME module-test1 COMMAND module-test one) +set_property(TEST module-test1 PROPERTY PASS_REGULAR_EXPRESSION "File 1 = one") diff --git a/tests/module_test/LICENSE b/tests/module_test/LICENSE new file mode 100644 index 000000000..25792f225 --- /dev/null +++ b/tests/module_test/LICENSE @@ -0,0 +1,22 @@ +MIT License + +Copyright (c) 2024 scivision + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/tests/module_test/cmodule.ixx b/tests/module_test/cmodule.ixx new file mode 100644 index 000000000..6b4195c85 --- /dev/null +++ b/tests/module_test/cmodule.ixx @@ -0,0 +1,17 @@ +module; + +#include + +export module math; + +export void foo(CLI::App *app) { + +} + +export int add(int a, int b){ + return a + b; +} + +export int subtract(int a, int b){ + return a - b; +} \ No newline at end of file diff --git a/tests/module_test/module_test.cpp b/tests/module_test/module_test.cpp new file mode 100644 index 000000000..3fcf0a660 --- /dev/null +++ b/tests/module_test/module_test.cpp @@ -0,0 +1,21 @@ +#include +#include +#include + +import math; + +int main(){ + int a=1; + int b=2; + + int absum = add(a, b); + int abdif = subtract(a, b); + + assert(a + b == absum); + assert(a - b == abdif); + + // used this instead of to work with older compilers that may choke on implicit includes + printf("OK: export module\n"); + + return EXIT_SUCCESS; +} From 818757c32881d80a9f1522b43a1e3657b27960d6 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 14 Nov 2025 15:49:41 +0000 Subject: [PATCH 03/29] style: pre-commit.ci fixes --- CMakeLists.txt | 4 +- include/CLI/impl/App_inl.hpp | 4 +- include/CLI/impl/StringTools_inl.hpp | 291 +++++++++++++-------------- src/CMakeLists.txt | 6 +- tests/CMakeLists.txt | 10 +- tests/module_test/LICENSE | 1 - tests/module_test/cmodule.ixx | 12 +- tests/module_test/module_test.cpp | 8 +- 8 files changed, 162 insertions(+), 174 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 93e10aea3..af0d54f88 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -65,8 +65,8 @@ endif() option(CLI11_WARNINGS_AS_ERRORS "Turn all warnings into errors (for CI)") option(CLI11_SINGLE_FILE "Generate a single header file") option(CLI11_PRECOMPILED "Generate a precompiled static library instead of a header-only" OFF) -if (CMAKE_CXX_STANDARD GREATER 19) - option(CLI11_MODULE "modify some code to support inclusion of CLI11 in a module" OFF) +if(CMAKE_CXX_STANDARD GREATER 19) + option(CLI11_MODULE "modify some code to support inclusion of CLI11 in a module" OFF) endif() cmake_dependent_option(CLI11_SANITIZERS "Download the sanitizers CMake config" OFF diff --git a/include/CLI/impl/App_inl.hpp b/include/CLI/impl/App_inl.hpp index 3ccdc98d0..f39e8004f 100644 --- a/include/CLI/impl/App_inl.hpp +++ b/include/CLI/impl/App_inl.hpp @@ -1275,8 +1275,8 @@ CLI11_INLINE void App::_process_help_flags(CallbackPriority priority, bool trigg if(help_ptr != nullptr && help_ptr->count() > 0 && help_ptr->get_callback_priority() == priority) trigger_help = true; if(help_all_ptr != nullptr && help_all_ptr->count() > 0 && help_all_ptr->get_callback_priority() == priority) - if(help_all_ptr != nullptr && help_all_ptr->count() > 0 && help_all_ptr->get_callback_priority() == priority) - trigger_all_help = true; + if(help_all_ptr != nullptr && help_all_ptr->count() > 0 && help_all_ptr->get_callback_priority() == priority) + trigger_all_help = true; // If there were parsed subcommands, call those. First subcommand wins if there are multiple ones. if(!parsed_subcommands_.empty()) { diff --git a/include/CLI/impl/StringTools_inl.hpp b/include/CLI/impl/StringTools_inl.hpp index c3ef96e92..202c1cd05 100644 --- a/include/CLI/impl/StringTools_inl.hpp +++ b/include/CLI/impl/StringTools_inl.hpp @@ -21,184 +21,179 @@ namespace CLI { // [CLI11:string_tools_inl_hpp:verbatim] - namespace detail { - CLI11_INLINE std::vector split(const std::string& s, char delim) { - std::vector elems; - // Check to see if empty string, give consistent result - if (s.empty()) { - elems.emplace_back(); - } - else { - std::stringstream ss; - ss.str(s); - std::string item; - while (std::getline(ss, item, delim)) { - elems.push_back(item); - } - } - return elems; +namespace detail { +CLI11_INLINE std::vector split(const std::string &s, char delim) { + std::vector elems; + // Check to see if empty string, give consistent result + if(s.empty()) { + elems.emplace_back(); + } else { + std::stringstream ss; + ss.str(s); + std::string item; + while(std::getline(ss, item, delim)) { + elems.push_back(item); } + } + return elems; +} - CLI11_INLINE std::string& ltrim(std::string& str) { - auto it = std::find_if(str.begin(), str.end(), [](char ch) { return !std::isspace(ch, std::locale()); }); - str.erase(str.begin(), it); - return str; - } +CLI11_INLINE std::string <rim(std::string &str) { + auto it = std::find_if(str.begin(), str.end(), [](char ch) { return !std::isspace(ch, std::locale()); }); + str.erase(str.begin(), it); + return str; +} - CLI11_INLINE std::string& ltrim(std::string& str, const std::string& filter) { - auto it = std::find_if(str.begin(), str.end(), [&filter](char ch) { return filter.find(ch) == std::string::npos; }); - str.erase(str.begin(), it); - return str; - } +CLI11_INLINE std::string <rim(std::string &str, const std::string &filter) { + auto it = std::find_if(str.begin(), str.end(), [&filter](char ch) { return filter.find(ch) == std::string::npos; }); + str.erase(str.begin(), it); + return str; +} - CLI11_INLINE std::string& rtrim(std::string& str) { - auto it = std::find_if(str.rbegin(), str.rend(), [](char ch) { return !std::isspace(ch, std::locale()); }); - str.erase(it.base(), str.end()); - return str; - } +CLI11_INLINE std::string &rtrim(std::string &str) { + auto it = std::find_if(str.rbegin(), str.rend(), [](char ch) { return !std::isspace(ch, std::locale()); }); + str.erase(it.base(), str.end()); + return str; +} - CLI11_INLINE std::string& rtrim(std::string& str, const std::string& filter) { - auto it = - std::find_if(str.rbegin(), str.rend(), [&filter](char ch) { return filter.find(ch) == std::string::npos; }); - str.erase(it.base(), str.end()); - return str; - } +CLI11_INLINE std::string &rtrim(std::string &str, const std::string &filter) { + auto it = + std::find_if(str.rbegin(), str.rend(), [&filter](char ch) { return filter.find(ch) == std::string::npos; }); + str.erase(it.base(), str.end()); + return str; +} - CLI11_INLINE std::string& remove_quotes(std::string& str) { - if (str.length() > 1 && (str.front() == '"' || str.front() == '\'' || str.front() == '`')) { - if (str.front() == str.back()) { - str.pop_back(); - str.erase(str.begin(), str.begin() + 1); - } - } - return str; +CLI11_INLINE std::string &remove_quotes(std::string &str) { + if(str.length() > 1 && (str.front() == '"' || str.front() == '\'' || str.front() == '`')) { + if(str.front() == str.back()) { + str.pop_back(); + str.erase(str.begin(), str.begin() + 1); } + } + return str; +} - CLI11_INLINE std::string& remove_outer(std::string& str, char key) { - if (str.length() > 1 && (str.front() == key)) { - if (str.front() == str.back()) { - str.pop_back(); - str.erase(str.begin(), str.begin() + 1); - } - } - return str; +CLI11_INLINE std::string &remove_outer(std::string &str, char key) { + if(str.length() > 1 && (str.front() == key)) { + if(str.front() == str.back()) { + str.pop_back(); + str.erase(str.begin(), str.begin() + 1); } + } + return str; +} - CLI11_INLINE std::string fix_newlines(const std::string& leader, std::string input) { - std::string::size_type n = 0; - while (n != std::string::npos && n < input.size()) { - n = input.find_first_of("\r\n", n); - if (n != std::string::npos) { - input = input.substr(0, n + 1) + leader + input.substr(n + 1); - n += leader.size(); - } - } - return input; +CLI11_INLINE std::string fix_newlines(const std::string &leader, std::string input) { + std::string::size_type n = 0; + while(n != std::string::npos && n < input.size()) { + n = input.find_first_of("\r\n", n); + if(n != std::string::npos) { + input = input.substr(0, n + 1) + leader + input.substr(n + 1); + n += leader.size(); } + } + return input; +} - CLI11_INLINE std::ostream& format_aliases(std::ostream& out, const std::vector& aliases, std::size_t wid) { - if (!aliases.empty()) { - out << std::setw(static_cast(wid)) << " aliases: "; - bool front = true; - for (const auto& alias : aliases) { - if (!front) { - out << ", "; - } - else { - front = false; - } - out << detail::fix_newlines(" ", alias); - } - out << "\n"; +CLI11_INLINE std::ostream &format_aliases(std::ostream &out, const std::vector &aliases, std::size_t wid) { + if(!aliases.empty()) { + out << std::setw(static_cast(wid)) << " aliases: "; + bool front = true; + for(const auto &alias : aliases) { + if(!front) { + out << ", "; + } else { + front = false; } - return out; + out << detail::fix_newlines(" ", alias); } + out << "\n"; + } + return out; +} - CLI11_INLINE bool valid_name_string(const std::string& str) { - if (str.empty() || !valid_first_char(str[0])) { - return false; - } - auto e = str.end(); - for (auto c = str.begin() + 1; c != e; ++c) - if (!valid_later_char(*c)) - return false; - return true; - } +CLI11_INLINE bool valid_name_string(const std::string &str) { + if(str.empty() || !valid_first_char(str[0])) { + return false; + } + auto e = str.end(); + for(auto c = str.begin() + 1; c != e; ++c) + if(!valid_later_char(*c)) + return false; + return true; +} - CLI11_INLINE std::string get_group_separators() { - std::string separators{ "_'" }; +CLI11_INLINE std::string get_group_separators() { + std::string separators{"_'"}; #if CLI11_HAS_RTTI != 0 - char group_separator = std::use_facet>(std::locale()).thousands_sep(); - separators.push_back(group_separator); + char group_separator = std::use_facet>(std::locale()).thousands_sep(); + separators.push_back(group_separator); #endif - return separators; - } + return separators; +} - CLI11_INLINE std::string find_and_replace(std::string str, std::string from, std::string to) { +CLI11_INLINE std::string find_and_replace(std::string str, std::string from, std::string to) { - std::size_t start_pos = 0; + std::size_t start_pos = 0; - while ((start_pos = str.find(from, start_pos)) != std::string::npos) { - str.replace(start_pos, from.length(), to); - start_pos += to.length(); - } + while((start_pos = str.find(from, start_pos)) != std::string::npos) { + str.replace(start_pos, from.length(), to); + start_pos += to.length(); + } - return str; - } + return str; +} - CLI11_INLINE void remove_default_flag_values(std::string& flags) { - auto loc = flags.find_first_of('{', 2); - while (loc != std::string::npos) { - auto finish = flags.find_first_of("},", loc + 1); - if ((finish != std::string::npos) && (flags[finish] == '}')) { - flags.erase(flags.begin() + static_cast(loc), +CLI11_INLINE void remove_default_flag_values(std::string &flags) { + auto loc = flags.find_first_of('{', 2); + while(loc != std::string::npos) { + auto finish = flags.find_first_of("},", loc + 1); + if((finish != std::string::npos) && (flags[finish] == '}')) { + flags.erase(flags.begin() + static_cast(loc), flags.begin() + static_cast(finish) + 1); - } - loc = flags.find_first_of('{', loc + 1); - } - flags.erase(std::remove(flags.begin(), flags.end(), '!'), flags.end()); } + loc = flags.find_first_of('{', loc + 1); + } + flags.erase(std::remove(flags.begin(), flags.end(), '!'), flags.end()); +} - CLI11_INLINE std::ptrdiff_t - find_member(std::string name, const std::vector names, bool ignore_case, bool ignore_underscore) { - auto it = std::end(names); - if (ignore_case) { - if (ignore_underscore) { - name = detail::to_lower(detail::remove_underscore(name)); - it = std::find_if(std::begin(names), std::end(names), [&name](std::string local_name) { - return detail::to_lower(detail::remove_underscore(local_name)) == name; - }); - } - else { - name = detail::to_lower(name); - it = std::find_if(std::begin(names), std::end(names), [&name](std::string local_name) { - return detail::to_lower(local_name) == name; - }); - } +CLI11_INLINE std::ptrdiff_t +find_member(std::string name, const std::vector names, bool ignore_case, bool ignore_underscore) { + auto it = std::end(names); + if(ignore_case) { + if(ignore_underscore) { + name = detail::to_lower(detail::remove_underscore(name)); + it = std::find_if(std::begin(names), std::end(names), [&name](std::string local_name) { + return detail::to_lower(detail::remove_underscore(local_name)) == name; + }); + } else { + name = detail::to_lower(name); + it = std::find_if(std::begin(names), std::end(names), [&name](std::string local_name) { + return detail::to_lower(local_name) == name; + }); + } - } - else if (ignore_underscore) { - name = detail::remove_underscore(name); - it = std::find_if(std::begin(names), std::end(names), [&name](std::string local_name) { - return detail::remove_underscore(local_name) == name; - }); - } - else { - it = std::find(std::begin(names), std::end(names), name); - } + } else if(ignore_underscore) { + name = detail::remove_underscore(name); + it = std::find_if(std::begin(names), std::end(names), [&name](std::string local_name) { + return detail::remove_underscore(local_name) == name; + }); + } else { + it = std::find(std::begin(names), std::end(names), name); + } - return (it != std::end(names)) ? (it - std::begin(names)) : (-1); - } + return (it != std::end(names)) ? (it - std::begin(names)) : (-1); +} #if defined(CLI11_MODULE) - CLI11_MODULE_INLINE const std::string escapedChars{"\b\t\n\f\r\"\\"}; - CLI11_MODULE_INLINE const std::string escapedCharsCode{ "btnfr\"\\" }; - CLI11_MODULE_INLINE const std::string matchBracketChars{ "\"'`])>}" }; - CLI11_MODULE_INLINE const std::string matchBracketChars{ "\"'`])>}" }; +CLI11_MODULE_INLINE const std::string escapedChars{"\b\t\n\f\r\"\\"}; +CLI11_MODULE_INLINE const std::string escapedCharsCode{"btnfr\"\\"}; +CLI11_MODULE_INLINE const std::string matchBracketChars{"\"'`])>}"}; +CLI11_MODULE_INLINE const std::string matchBracketChars{"\"'`])>}"}; #else -static const std::string& escapedChars{ "\b\t\n\f\r\"\\" }; -static const std::string& escapedCharsCode{ "btnfr\"\\" }; -static const std::string& bracketChars{"\"'`[(<{"}; -static const std::string& matchBracketChars{ "\"'`])>}" }; +static const std::string &escapedChars{"\b\t\n\f\r\"\\"}; +static const std::string &escapedCharsCode{"btnfr\"\\"}; +static const std::string &bracketChars{"\"'`[(<{"}; +static const std::string &matchBracketChars{"\"'`])>}"}; #endif CLI11_INLINE bool has_escapable_character(const std::string &str) { diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3881e058f..86c7d2ac0 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -35,10 +35,10 @@ else() set(SYSTEM_INCL "SYSTEM") endif() -if (CLI11_MODULE) - target_compile_definitions(CLI11 ${PUBLIC_OR_INTERFACE} -DCLI11_MODULE=1) +if(CLI11_MODULE) + target_compile_definitions(CLI11 ${PUBLIC_OR_INTERFACE} -DCLI11_MODULE=1) endif() - + # Duplicated because CMake adds the current source dir if you don't. target_include_directories( CLI11 ${SYSTEM_INCL} ${PUBLIC_OR_INTERFACE} $ diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 2648a0363..a1e963e3b 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -357,12 +357,12 @@ if(CLI11_INSTALL_PACKAGE_TESTS) set_property(TEST find-package-testsC PROPERTY LABELS Packaging) set_property(TEST find-package-testsC PROPERTY DEPENDS find-package-testsB) -if (CMAKE_CXX_STANDARD GREATER 19) + if(CMAKE_CXX_STANDARD GREATER 19) add_test(NAME find-package-module - COMMAND ${CMAKE_COMMAND} --build "${CMAKE_CURRENT_BINARY_DIR}/module_tests" - --config ${CLI11_PACKAGE_TEST_BUILD_TYPE}) -endif() - + COMMAND ${CMAKE_COMMAND} --build "${CMAKE_CURRENT_BINARY_DIR}/module_tests" --config + ${CLI11_PACKAGE_TEST_BUILD_TYPE}) + endif() + if(NOT MSVC) # Tests for other CMake projects using the package_config files add_test( diff --git a/tests/module_test/LICENSE b/tests/module_test/LICENSE index 25792f225..8d2e24afc 100644 --- a/tests/module_test/LICENSE +++ b/tests/module_test/LICENSE @@ -19,4 +19,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - diff --git a/tests/module_test/cmodule.ixx b/tests/module_test/cmodule.ixx index 6b4195c85..64c11c153 100644 --- a/tests/module_test/cmodule.ixx +++ b/tests/module_test/cmodule.ixx @@ -4,14 +4,8 @@ module; export module math; -export void foo(CLI::App *app) { +export void foo(CLI::App *app) {} -} +export int add(int a, int b) { return a + b; } -export int add(int a, int b){ - return a + b; -} - -export int subtract(int a, int b){ - return a - b; -} \ No newline at end of file +export int subtract(int a, int b) { return a - b; } diff --git a/tests/module_test/module_test.cpp b/tests/module_test/module_test.cpp index 3fcf0a660..3cfd28e8e 100644 --- a/tests/module_test/module_test.cpp +++ b/tests/module_test/module_test.cpp @@ -1,12 +1,12 @@ -#include #include +#include #include import math; -int main(){ - int a=1; - int b=2; +int main() { + int a = 1; + int b = 2; int absum = add(a, b); int abdif = subtract(a, b); From 61d673e459cb566e1f1a719baa16140efdcc6fd8 Mon Sep 17 00:00:00 2001 From: Philip Top Date: Fri, 14 Nov 2025 08:27:22 -0800 Subject: [PATCH 04/29] update cmake standard used for some tests --- .github/workflows/tests.yml | 2 +- tests/module_test/CMakeLists.txt | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 10b3eed67..9dae9a8d3 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -304,7 +304,7 @@ jobs: with: submodules: true - name: Configure - run: cmake -S . -B build -DCLI11_INSTALL_PACKAGE_TESTS=ON -DCLI11_MODULE=ON -DCMAKE_INSTALL_PREFIX=/home/runner/work/install + run: cmake -S . -B build -DCLI11_INSTALL_PACKAGE_TESTS=ON -DCLI11_MODULE=ON -DCMAKE_INSTALL_PREFIX=/home/runner/work/install -DCMAKE_CXX_STANDARD=23 - name: Build run: cmake --build build -j2 - name: install diff --git a/tests/module_test/CMakeLists.txt b/tests/module_test/CMakeLists.txt index 227df6a96..5994e347b 100644 --- a/tests/module_test/CMakeLists.txt +++ b/tests/module_test/CMakeLists.txt @@ -8,6 +8,7 @@ if(CLI11_DIR) set(CMAKE_PREFIX_PATH ${CLI11_DIR}) endif() +set(CMAKE_CXX_STANDARD 23) # Test the CLI11 CMake package config find_package(CLI11 2.5 REQUIRED) From c5a0408107af041d0d0f8c1e99b1e52688ba6838 Mon Sep 17 00:00:00 2001 From: Philip Top Date: Fri, 14 Nov 2025 19:14:33 -0800 Subject: [PATCH 05/29] more tweaks --- include/CLI/impl/StringTools_inl.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/CLI/impl/StringTools_inl.hpp b/include/CLI/impl/StringTools_inl.hpp index 202c1cd05..a80f001cc 100644 --- a/include/CLI/impl/StringTools_inl.hpp +++ b/include/CLI/impl/StringTools_inl.hpp @@ -185,10 +185,10 @@ find_member(std::string name, const std::vector names, bool ignore_ return (it != std::end(names)) ? (it - std::begin(names)) : (-1); } #if defined(CLI11_MODULE) -CLI11_MODULE_INLINE const std::string escapedChars{"\b\t\n\f\r\"\\"}; -CLI11_MODULE_INLINE const std::string escapedCharsCode{"btnfr\"\\"}; -CLI11_MODULE_INLINE const std::string matchBracketChars{"\"'`])>}"}; -CLI11_MODULE_INLINE const std::string matchBracketChars{"\"'`])>}"}; +CLI11_MODULE_INLINE const std::string &escapedChars{"\b\t\n\f\r\"\\"}; +CLI11_MODULE_INLINE const std::string &escapedCharsCode{"btnfr\"\\"}; +CLI11_MODULE_INLINE const std::string &matchBracketChars{"\"'`])>}"}; +CLI11_MODULE_INLINE const std::string &matchBracketChars{"\"'`])>}"}; #else static const std::string &escapedChars{"\b\t\n\f\r\"\\"}; static const std::string &escapedCharsCode{"btnfr\"\\"}; From 42f2833294d6c9dd004c7ac7b14bbf64dfb6a85b Mon Sep 17 00:00:00 2001 From: Philip Top Date: Fri, 14 Nov 2025 19:55:54 -0800 Subject: [PATCH 06/29] try slightly different definition for string constants --- include/CLI/impl/StringTools_inl.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/CLI/impl/StringTools_inl.hpp b/include/CLI/impl/StringTools_inl.hpp index a80f001cc..e336a2ce6 100644 --- a/include/CLI/impl/StringTools_inl.hpp +++ b/include/CLI/impl/StringTools_inl.hpp @@ -185,10 +185,10 @@ find_member(std::string name, const std::vector names, bool ignore_ return (it != std::end(names)) ? (it - std::begin(names)) : (-1); } #if defined(CLI11_MODULE) -CLI11_MODULE_INLINE const std::string &escapedChars{"\b\t\n\f\r\"\\"}; -CLI11_MODULE_INLINE const std::string &escapedCharsCode{"btnfr\"\\"}; -CLI11_MODULE_INLINE const std::string &matchBracketChars{"\"'`])>}"}; -CLI11_MODULE_INLINE const std::string &matchBracketChars{"\"'`])>}"}; +CLI11_MODULE_INLINE const std::string& escapedChars("\b\t\n\f\r\"\\"); +CLI11_MODULE_INLINE const std::string& escapedCharsCode("btnfr\"\\"); +CLI11_MODULE_INLINE const std::string& bracketChars("\"'`[(<{"); +CLI11_MODULE_INLINE const std::string& matchBracketChars("\"'`])>}"); #else static const std::string &escapedChars{"\b\t\n\f\r\"\\"}; static const std::string &escapedCharsCode{"btnfr\"\\"}; From fa2347d9c546e2a5232cbf27024b52b14528c4d8 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 15 Nov 2025 13:09:11 +0000 Subject: [PATCH 07/29] style: pre-commit.ci fixes --- include/CLI/impl/StringTools_inl.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/CLI/impl/StringTools_inl.hpp b/include/CLI/impl/StringTools_inl.hpp index e336a2ce6..394431fdd 100644 --- a/include/CLI/impl/StringTools_inl.hpp +++ b/include/CLI/impl/StringTools_inl.hpp @@ -185,10 +185,10 @@ find_member(std::string name, const std::vector names, bool ignore_ return (it != std::end(names)) ? (it - std::begin(names)) : (-1); } #if defined(CLI11_MODULE) -CLI11_MODULE_INLINE const std::string& escapedChars("\b\t\n\f\r\"\\"); -CLI11_MODULE_INLINE const std::string& escapedCharsCode("btnfr\"\\"); -CLI11_MODULE_INLINE const std::string& bracketChars("\"'`[(<{"); -CLI11_MODULE_INLINE const std::string& matchBracketChars("\"'`])>}"); +CLI11_MODULE_INLINE const std::string &escapedChars("\b\t\n\f\r\"\\"); +CLI11_MODULE_INLINE const std::string &escapedCharsCode("btnfr\"\\"); +CLI11_MODULE_INLINE const std::string &bracketChars("\"'`[(<{"); +CLI11_MODULE_INLINE const std::string &matchBracketChars("\"'`])>}"); #else static const std::string &escapedChars{"\b\t\n\f\r\"\\"}; static const std::string &escapedCharsCode{"btnfr\"\\"}; From 4f89361d818bb85e5081edfee08c0f8b5230180e Mon Sep 17 00:00:00 2001 From: Philip Top Date: Sat, 15 Nov 2025 05:28:09 -0800 Subject: [PATCH 08/29] update some docs and copyright notices on code --- book/chapters/installation.md | 1 + tests/module_test/CMakeLists.txt | 2 +- tests/module_test/cmodule.ixx | 11 ++++++++++- tests/module_test/module_test.cpp | 11 ++++++++++- 4 files changed, 22 insertions(+), 3 deletions(-) diff --git a/book/chapters/installation.md b/book/chapters/installation.md index f44fe8d6d..b09aee365 100644 --- a/book/chapters/installation.md +++ b/book/chapters/installation.md @@ -180,6 +180,7 @@ default to off if CLI11 is used as a subdirectory in another project. | ------------------------------ | -------------------------------------------------------------------------------- | | `CLI11_SINGLE_FILE=ON` | Build the `CLI11.hpp` file from the sources. Requires Python (version 3 or 2.7). | | `CLI11_PRECOMPILED=OFF` | generate a precompiled static library instead of header-only | +| `CLI11_MODULE=OFF` | change some code segments for named constants to allow use of CLI11 in a module | | `CLI11_SINGLE_FILE_TESTS=OFF` | Run the tests on the generated single file version as well | | `CLI11_BUILD_DOCS=ON` | build CLI11 documentation and book | | `CLI11_BUILD_EXAMPLES=ON` | Build the example programs. | diff --git a/tests/module_test/CMakeLists.txt b/tests/module_test/CMakeLists.txt index 5994e347b..bd07c0fd1 100644 --- a/tests/module_test/CMakeLists.txt +++ b/tests/module_test/CMakeLists.txt @@ -18,4 +18,4 @@ target_link_libraries(module-test CLI11::CLI11) target_compile_definitions(module-test -DCLI11_MODULE=1) add_test(NAME module-test1 COMMAND module-test one) -set_property(TEST module-test1 PROPERTY PASS_REGULAR_EXPRESSION "File 1 = one") +set_property(TEST module-test1 PROPERTY PASS_REGULAR_EXPRESSION "OK: export module") diff --git a/tests/module_test/cmodule.ixx b/tests/module_test/cmodule.ixx index 64c11c153..7da9e84c7 100644 --- a/tests/module_test/cmodule.ixx +++ b/tests/module_test/cmodule.ixx @@ -1,8 +1,17 @@ +// Copyright (c) 2024 scivision +// Copyright (c) 2025 University of Cincinnati, developed by Henry Schreiner +// under NSF AWARD 1414736 and by the respective contributors. +// All rights reserved. +// +// SPDX-License-Identifier: MIT + +// modified from https://github.com/iTrooz/CppModules/blob/cli11 for use in CLI11 tests + module; #include -export module math; +export module cmodule; export void foo(CLI::App *app) {} diff --git a/tests/module_test/module_test.cpp b/tests/module_test/module_test.cpp index 3cfd28e8e..243324fac 100644 --- a/tests/module_test/module_test.cpp +++ b/tests/module_test/module_test.cpp @@ -1,8 +1,17 @@ +// Copyright (c) 2024 scivision +// Copyright (c) 2025 University of Cincinnati, developed by Henry Schreiner +// under NSF AWARD 1414736 and by the respective contributors. +// All rights reserved. +// +// SPDX-License-Identifier: MIT + +// modified from https://github.com/iTrooz/CppModules/blob/cli11 for use in CLI11 tests + #include #include #include -import math; +import cmodule; int main() { int a = 1; From 2006cf149cde09919397be0c2a001e929472879f Mon Sep 17 00:00:00 2001 From: Philip Top Date: Sat, 15 Nov 2025 07:05:33 -0800 Subject: [PATCH 09/29] remove duplicate line --- include/CLI/impl/App_inl.hpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/include/CLI/impl/App_inl.hpp b/include/CLI/impl/App_inl.hpp index f39e8004f..582515b04 100644 --- a/include/CLI/impl/App_inl.hpp +++ b/include/CLI/impl/App_inl.hpp @@ -1272,16 +1272,18 @@ CLI11_INLINE void App::_process_help_flags(CallbackPriority priority, bool trigg const Option *help_ptr = get_help_ptr(); const Option *help_all_ptr = get_help_all_ptr(); - if(help_ptr != nullptr && help_ptr->count() > 0 && help_ptr->get_callback_priority() == priority) + if (help_ptr != nullptr && help_ptr->count() > 0 && help_ptr->get_callback_priority() == priority) { trigger_help = true; - if(help_all_ptr != nullptr && help_all_ptr->count() > 0 && help_all_ptr->get_callback_priority() == priority) - if(help_all_ptr != nullptr && help_all_ptr->count() > 0 && help_all_ptr->get_callback_priority() == priority) - trigger_all_help = true; + } + if (help_all_ptr != nullptr && help_all_ptr->count() > 0 && help_all_ptr->get_callback_priority() == priority) { + trigger_all_help = true; + } // If there were parsed subcommands, call those. First subcommand wins if there are multiple ones. if(!parsed_subcommands_.empty()) { - for(const App *sub : parsed_subcommands_) + for (const App* sub : parsed_subcommands_) { sub->_process_help_flags(priority, trigger_help, trigger_all_help); + } // Only the final subcommand should call for help. All help wins over help. } else if(trigger_all_help) { From cb6350a3c52dc8038085e29114b96d2e7e035f5a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 15 Nov 2025 15:05:54 +0000 Subject: [PATCH 10/29] style: pre-commit.ci fixes --- include/CLI/impl/App_inl.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/CLI/impl/App_inl.hpp b/include/CLI/impl/App_inl.hpp index 582515b04..db9fb5d91 100644 --- a/include/CLI/impl/App_inl.hpp +++ b/include/CLI/impl/App_inl.hpp @@ -1272,16 +1272,16 @@ CLI11_INLINE void App::_process_help_flags(CallbackPriority priority, bool trigg const Option *help_ptr = get_help_ptr(); const Option *help_all_ptr = get_help_all_ptr(); - if (help_ptr != nullptr && help_ptr->count() > 0 && help_ptr->get_callback_priority() == priority) { + if(help_ptr != nullptr && help_ptr->count() > 0 && help_ptr->get_callback_priority() == priority) { trigger_help = true; } - if (help_all_ptr != nullptr && help_all_ptr->count() > 0 && help_all_ptr->get_callback_priority() == priority) { + if(help_all_ptr != nullptr && help_all_ptr->count() > 0 && help_all_ptr->get_callback_priority() == priority) { trigger_all_help = true; } // If there were parsed subcommands, call those. First subcommand wins if there are multiple ones. if(!parsed_subcommands_.empty()) { - for (const App* sub : parsed_subcommands_) { + for(const App *sub : parsed_subcommands_) { sub->_process_help_flags(priority, trigger_help, trigger_all_help); } From 759ba3b9c2db353ce8e426f8b5a9161158164908 Mon Sep 17 00:00:00 2001 From: Philip Top Date: Sat, 15 Nov 2025 09:03:32 -0800 Subject: [PATCH 11/29] update the testing command for the module test --- tests/CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index a1e963e3b..31d022fd0 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -359,8 +359,9 @@ if(CLI11_INSTALL_PACKAGE_TESTS) if(CMAKE_CXX_STANDARD GREATER 19) add_test(NAME find-package-module - COMMAND ${CMAKE_COMMAND} --build "${CMAKE_CURRENT_BINARY_DIR}/module_tests" --config + COMMAND ${CMAKE_CTEST_COMMAND} --build-and-test "${CMAKE_CURRENT_BINARY_DIR}/module_tests" --config ${CLI11_PACKAGE_TEST_BUILD_TYPE}) + set_property(TEST find-package-module PROPERTY LABELS Packaging) endif() if(NOT MSVC) From 16f9d4ac78634f6dccb5e7bcd2fba9956c096a3a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 15 Nov 2025 17:04:03 +0000 Subject: [PATCH 12/29] style: pre-commit.ci fixes --- tests/CMakeLists.txt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 31d022fd0..d49d7d456 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -358,9 +358,10 @@ if(CLI11_INSTALL_PACKAGE_TESTS) set_property(TEST find-package-testsC PROPERTY DEPENDS find-package-testsB) if(CMAKE_CXX_STANDARD GREATER 19) - add_test(NAME find-package-module - COMMAND ${CMAKE_CTEST_COMMAND} --build-and-test "${CMAKE_CURRENT_BINARY_DIR}/module_tests" --config - ${CLI11_PACKAGE_TEST_BUILD_TYPE}) + add_test( + NAME find-package-module + COMMAND ${CMAKE_CTEST_COMMAND} --build-and-test "${CMAKE_CURRENT_BINARY_DIR}/module_tests" + --config ${CLI11_PACKAGE_TEST_BUILD_TYPE}) set_property(TEST find-package-module PROPERTY LABELS Packaging) endif() From 21bd7b4c67c0f0a8efc31f74b7e97d983da87e93 Mon Sep 17 00:00:00 2001 From: Philip Top Date: Sat, 15 Nov 2025 14:10:15 -0800 Subject: [PATCH 13/29] update the ctest command for module testing --- tests/CMakeLists.txt | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index d49d7d456..94ef3c4f3 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -358,10 +358,22 @@ if(CLI11_INSTALL_PACKAGE_TESTS) set_property(TEST find-package-testsC PROPERTY DEPENDS find-package-testsB) if(CMAKE_CXX_STANDARD GREATER 19) - add_test( - NAME find-package-module - COMMAND ${CMAKE_CTEST_COMMAND} --build-and-test "${CMAKE_CURRENT_BINARY_DIR}/module_tests" - --config ${CLI11_PACKAGE_TEST_BUILD_TYPE}) + add_test( + find-package-module + ${CMAKE_CTEST_COMMAND} + --build-and-test + "${CMAKE_CURRENT_SOURCE_DIR}/module_test" + "${CMAKE_CURRENT_BINARY_DIR}/module_test" + --build-generator + "${CMAKE_GENERATOR}" + --build-generator-platform + "${CMAKE_GENERATOR_PLATFORM}" + --build-options + "-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}" + "-DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD}" + "-DCLI11_DIR=${CMAKE_INSTALL_PREFIX}" + ${package_test_command}) + set_property(TEST find-package-module PROPERTY LABELS Packaging) endif() From d4d99b642ad0d730c9b1553718c33866f6662159 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 19 Nov 2025 12:35:23 +0000 Subject: [PATCH 14/29] style: pre-commit.ci fixes --- tests/CMakeLists.txt | 4 ++-- tests/module_test/CMakeLists.txt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 94ef3c4f3..a39bf5462 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -358,7 +358,7 @@ if(CLI11_INSTALL_PACKAGE_TESTS) set_property(TEST find-package-testsC PROPERTY DEPENDS find-package-testsB) if(CMAKE_CXX_STANDARD GREATER 19) - add_test( + add_test( find-package-module ${CMAKE_CTEST_COMMAND} --build-and-test @@ -373,7 +373,7 @@ if(CLI11_INSTALL_PACKAGE_TESTS) "-DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD}" "-DCLI11_DIR=${CMAKE_INSTALL_PREFIX}" ${package_test_command}) - + set_property(TEST find-package-module PROPERTY LABELS Packaging) endif() diff --git a/tests/module_test/CMakeLists.txt b/tests/module_test/CMakeLists.txt index bd07c0fd1..1ee7c44ce 100644 --- a/tests/module_test/CMakeLists.txt +++ b/tests/module_test/CMakeLists.txt @@ -15,7 +15,7 @@ find_package(CLI11 2.5 REQUIRED) # Test the target add_executable(module-test module_test.cpp) target_link_libraries(module-test CLI11::CLI11) -target_compile_definitions(module-test -DCLI11_MODULE=1) +target_compile_definitions(module-test PUBLIC -DCLI11_MODULE=1) add_test(NAME module-test1 COMMAND module-test one) set_property(TEST module-test1 PROPERTY PASS_REGULAR_EXPRESSION "OK: export module") From 88156f424a105b536d12337dc54cbb856729c992 Mon Sep 17 00:00:00 2001 From: Philip Top Date: Wed, 19 Nov 2025 05:56:51 -0800 Subject: [PATCH 15/29] update the tests --- tests/CMakeLists.txt | 2 +- tests/module_test/CMakeLists.txt | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index a39bf5462..4c3e8cf5d 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -357,7 +357,7 @@ if(CLI11_INSTALL_PACKAGE_TESTS) set_property(TEST find-package-testsC PROPERTY LABELS Packaging) set_property(TEST find-package-testsC PROPERTY DEPENDS find-package-testsB) - if(CMAKE_CXX_STANDARD GREATER 19) + if(CLI11_MODULE OR CLI11_MODULE_TEST) add_test( find-package-module ${CMAKE_CTEST_COMMAND} diff --git a/tests/module_test/CMakeLists.txt b/tests/module_test/CMakeLists.txt index 1ee7c44ce..b7067ec57 100644 --- a/tests/module_test/CMakeLists.txt +++ b/tests/module_test/CMakeLists.txt @@ -14,8 +14,16 @@ find_package(CLI11 2.5 REQUIRED) # Test the target add_executable(module-test module_test.cpp) + +target_sources(module-test PUBLIC +FILE_SET cmodule +TYPE CXX_MODULES +FILES cmodule.ixx +) + target_link_libraries(module-test CLI11::CLI11) target_compile_definitions(module-test PUBLIC -DCLI11_MODULE=1) +target_compile_options(module-test PUBLIC -fmodules-ts) add_test(NAME module-test1 COMMAND module-test one) set_property(TEST module-test1 PROPERTY PASS_REGULAR_EXPRESSION "OK: export module") From 2a9533c17b4d0a979fae1d664d9bb5b448aae17c Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 19 Nov 2025 14:26:49 +0000 Subject: [PATCH 16/29] style: pre-commit.ci fixes --- tests/module_test/CMakeLists.txt | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/tests/module_test/CMakeLists.txt b/tests/module_test/CMakeLists.txt index b7067ec57..85b39c3f1 100644 --- a/tests/module_test/CMakeLists.txt +++ b/tests/module_test/CMakeLists.txt @@ -15,11 +15,7 @@ find_package(CLI11 2.5 REQUIRED) # Test the target add_executable(module-test module_test.cpp) -target_sources(module-test PUBLIC -FILE_SET cmodule -TYPE CXX_MODULES -FILES cmodule.ixx -) +target_sources(module-test PUBLIC FILE_SET cmodule TYPE CXX_MODULES FILES cmodule.ixx) target_link_libraries(module-test CLI11::CLI11) target_compile_definitions(module-test PUBLIC -DCLI11_MODULE=1) From e4035aea24b4ad69b274abe940d4094f0d4662b3 Mon Sep 17 00:00:00 2001 From: Philip Top Date: Wed, 19 Nov 2025 06:40:07 -0800 Subject: [PATCH 17/29] change to ninja generator for test --- tests/CMakeLists.txt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 4c3e8cf5d..397ce90a8 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -365,9 +365,7 @@ if(CLI11_INSTALL_PACKAGE_TESTS) "${CMAKE_CURRENT_SOURCE_DIR}/module_test" "${CMAKE_CURRENT_BINARY_DIR}/module_test" --build-generator - "${CMAKE_GENERATOR}" - --build-generator-platform - "${CMAKE_GENERATOR_PLATFORM}" + "Ninja" --build-options "-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}" "-DCMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD}" From e1268c55210eb4671d5af3c53c66578c609e5921 Mon Sep 17 00:00:00 2001 From: Philip Top Date: Wed, 19 Nov 2025 07:24:36 -0800 Subject: [PATCH 18/29] try using an action to get gcc 15 --- .github/workflows/tests.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 9dae9a8d3..641a33155 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -300,6 +300,10 @@ jobs: name: install module tests runs-on: ubuntu-latest steps: + - uses: egor-tensin/setup-gcc@v1 + with: + version: 15 + platform: x64 - uses: actions/checkout@v4 with: submodules: true From 75e0a184475974c52e6914816b5fef5fef26b740 Mon Sep 17 00:00:00 2001 From: Philip Top Date: Wed, 19 Nov 2025 18:06:04 -0800 Subject: [PATCH 19/29] update some tests --- .github/workflows/tests.yml | 8 ++++---- azure-pipelines.yml | 8 ++++++++ 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 641a33155..765b6d1f9 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -299,14 +299,14 @@ jobs: install-module: name: install module tests runs-on: ubuntu-latest + container: gcc:15 steps: - - uses: egor-tensin/setup-gcc@v1 - with: - version: 15 - platform: x64 - uses: actions/checkout@v4 with: submodules: true + - uses: ./.github/actions/quick_cmake + with: + cmake-version: "4.2" - name: Configure run: cmake -S . -B build -DCLI11_INSTALL_PACKAGE_TESTS=ON -DCLI11_MODULE=ON -DCMAKE_INSTALL_PREFIX=/home/runner/work/install -DCMAKE_CXX_STANDARD=23 - name: Build diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 1ad5890ea..0f606f23b 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -184,6 +184,14 @@ jobs: vmImage: "ubuntu-latest" strategy: matrix: + gcc15_23: + containerImage: gcc:15 + cli11.std: 23 + cli11.options: + -DCMAKE_CXX_FLAGS="-Wstrict-overflow=5" + -DCLI11_ENABLE_EXTRA_VALIDATORS=1 + -DCLI11_MODULE=ON + gcc13_17: containerImage: gcc:13 cli11.std: 17 From dd8ca495dce021373b4af41b66c786914b2c4107 Mon Sep 17 00:00:00 2001 From: Philip Top Date: Thu, 20 Nov 2025 04:48:50 -0800 Subject: [PATCH 20/29] add ninja install --- .github/workflows/tests.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 765b6d1f9..9b82965b0 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -307,6 +307,7 @@ jobs: - uses: ./.github/actions/quick_cmake with: cmake-version: "4.2" + - uses: seanmiddleditch/gha-setup-ninja@master - name: Configure run: cmake -S . -B build -DCLI11_INSTALL_PACKAGE_TESTS=ON -DCLI11_MODULE=ON -DCMAKE_INSTALL_PREFIX=/home/runner/work/install -DCMAKE_CXX_STANDARD=23 - name: Build From 6c43ac264dc6cc1929db2eff3a399bb0985cbf4f Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 20 Nov 2025 13:12:25 +0000 Subject: [PATCH 21/29] style: pre-commit.ci fixes --- azure-pipelines.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 0f606f23b..1c9f3e396 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -189,8 +189,7 @@ jobs: cli11.std: 23 cli11.options: -DCMAKE_CXX_FLAGS="-Wstrict-overflow=5" - -DCLI11_ENABLE_EXTRA_VALIDATORS=1 - -DCLI11_MODULE=ON + -DCLI11_ENABLE_EXTRA_VALIDATORS=1 -DCLI11_MODULE=ON gcc13_17: containerImage: gcc:13 From d17147138309162bfa502ddf4c4d2521af16fd80 Mon Sep 17 00:00:00 2001 From: Philip Top Date: Thu, 20 Nov 2025 05:43:09 -0800 Subject: [PATCH 22/29] update to support cmake 4.2 --- .codacy.yml | 7 +++++-- .github/workflows/tests.yml | 6 ++++++ CMakeLists.txt | 2 +- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/.codacy.yml b/.codacy.yml index 8fd2cc96a..75a734f5d 100644 --- a/.codacy.yml +++ b/.codacy.yml @@ -9,8 +9,11 @@ engines: coverage: enabled: false cppcheck: - enabled: false - language: c++ + enabled: true + options: + suppress: + - missingIncludeSystem + languages: ignore: diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 9b82965b0..3ac8326cf 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -453,3 +453,9 @@ jobs: with: cmake-version: "4.1" if: success() || failure() + + - name: Check CMake 4.2 + uses: ./.github/actions/quick_cmake + with: + cmake-version: "4.2" + if: success() || failure() diff --git a/CMakeLists.txt b/CMakeLists.txt index af0d54f88..5cc68ce9f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.14...4.1) +cmake_minimum_required(VERSION 3.14...4.2) # Note: this is a header only library. If you have an older CMake than 3.14, # just add the CLI11/include directory and that's all you need to do. From 7fd46cb7b685c79ed3d9a5d8f76e736fc9bba409 Mon Sep 17 00:00:00 2001 From: Philip Top Date: Fri, 21 Nov 2025 04:47:26 -0800 Subject: [PATCH 23/29] Apply suggestions from code review Co-authored-by: Henry Schreiner --- CMakeLists.txt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5cc68ce9f..687f77df0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -65,9 +65,7 @@ endif() option(CLI11_WARNINGS_AS_ERRORS "Turn all warnings into errors (for CI)") option(CLI11_SINGLE_FILE "Generate a single header file") option(CLI11_PRECOMPILED "Generate a precompiled static library instead of a header-only" OFF) -if(CMAKE_CXX_STANDARD GREATER 19) - option(CLI11_MODULE "modify some code to support inclusion of CLI11 in a module" OFF) -endif() +cmake_dependent_option(CLI11_MODULE "Modify some code to support inclusion of CLI11 in a module" OFF "CMAKE_CXX_STANDARD GREATER_EQUAL 20" OFF) cmake_dependent_option(CLI11_SANITIZERS "Download the sanitizers CMake config" OFF "NOT CMAKE_VERSION VERSION_LESS 3.15" OFF) From a078faaa4dcce8f1daa74a6bf614369c1f558cdd Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 21 Nov 2025 12:47:43 +0000 Subject: [PATCH 24/29] style: pre-commit.ci fixes --- CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 687f77df0..a43ba6728 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -65,7 +65,8 @@ endif() option(CLI11_WARNINGS_AS_ERRORS "Turn all warnings into errors (for CI)") option(CLI11_SINGLE_FILE "Generate a single header file") option(CLI11_PRECOMPILED "Generate a precompiled static library instead of a header-only" OFF) -cmake_dependent_option(CLI11_MODULE "Modify some code to support inclusion of CLI11 in a module" OFF "CMAKE_CXX_STANDARD GREATER_EQUAL 20" OFF) +cmake_dependent_option(CLI11_MODULE "Modify some code to support inclusion of CLI11 in a module" + OFF "CMAKE_CXX_STANDARD GREATER_EQUAL 20" OFF) cmake_dependent_option(CLI11_SANITIZERS "Download the sanitizers CMake config" OFF "NOT CMAKE_VERSION VERSION_LESS 3.15" OFF) From f4ab7122b07b2ead2b5df9eebe87faafec5b8bcc Mon Sep 17 00:00:00 2001 From: Philip Top Date: Sat, 22 Nov 2025 04:35:45 -0800 Subject: [PATCH 25/29] try making the CLI11 module code changes dependent on C++17 for compile test --- .github/workflows/tests.yml | 2 +- include/CLI/Macros.hpp | 2 +- include/CLI/impl/StringTools_inl.hpp | 2 +- tests/CMakeLists.txt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 133b72cf9..1cbd607ca 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -328,7 +328,7 @@ jobs: cmake-version: "4.2" - uses: seanmiddleditch/gha-setup-ninja@master - name: Configure - run: cmake -S . -B build -DCLI11_INSTALL_PACKAGE_TESTS=ON -DCLI11_MODULE=ON -DCMAKE_INSTALL_PREFIX=/home/runner/work/install -DCMAKE_CXX_STANDARD=23 + run: cmake -S . -B build -DCLI11_INSTALL_PACKAGE_TESTS=ON -DCLI11_MODULE_TESTS=ON -DCMAKE_INSTALL_PREFIX=/home/runner/work/install -DCMAKE_CXX_STANDARD=23 - name: Build run: cmake --build build -j2 - name: install diff --git a/include/CLI/Macros.hpp b/include/CLI/Macros.hpp index 486d1bc01..dd6ddc5bf 100644 --- a/include/CLI/Macros.hpp +++ b/include/CLI/Macros.hpp @@ -177,7 +177,7 @@ #endif /** Module inline**/ -#ifdef CLI11_MODULE +#if defined CLI11_CPP17 #define CLI11_MODULE_INLINE inline #else #define CLI11_MODULE_INLINE diff --git a/include/CLI/impl/StringTools_inl.hpp b/include/CLI/impl/StringTools_inl.hpp index 394431fdd..e3852c513 100644 --- a/include/CLI/impl/StringTools_inl.hpp +++ b/include/CLI/impl/StringTools_inl.hpp @@ -184,7 +184,7 @@ find_member(std::string name, const std::vector names, bool ignore_ return (it != std::end(names)) ? (it - std::begin(names)) : (-1); } -#if defined(CLI11_MODULE) +#if defined(CLI11_CPP17) CLI11_MODULE_INLINE const std::string &escapedChars("\b\t\n\f\r\"\\"); CLI11_MODULE_INLINE const std::string &escapedCharsCode("btnfr\"\\"); CLI11_MODULE_INLINE const std::string &bracketChars("\"'`[(<{"); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 397ce90a8..3156291fe 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -357,7 +357,7 @@ if(CLI11_INSTALL_PACKAGE_TESTS) set_property(TEST find-package-testsC PROPERTY LABELS Packaging) set_property(TEST find-package-testsC PROPERTY DEPENDS find-package-testsB) - if(CLI11_MODULE OR CLI11_MODULE_TEST) + if(CLI11_MODULE_TESTS) add_test( find-package-module ${CMAKE_CTEST_COMMAND} From 18f9850b70753bbd3f72f6aff3a6b8fd9f994741 Mon Sep 17 00:00:00 2001 From: Philip Top Date: Sat, 22 Nov 2025 06:24:42 -0800 Subject: [PATCH 26/29] try with static --- include/CLI/Macros.hpp | 2 +- include/CLI/impl/StringTools_inl.hpp | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/include/CLI/Macros.hpp b/include/CLI/Macros.hpp index dd6ddc5bf..b1a6321c9 100644 --- a/include/CLI/Macros.hpp +++ b/include/CLI/Macros.hpp @@ -180,6 +180,6 @@ #if defined CLI11_CPP17 #define CLI11_MODULE_INLINE inline #else -#define CLI11_MODULE_INLINE +#define CLI11_MODULE_INLINE static #endif // [CLI11:macros_hpp:end] diff --git a/include/CLI/impl/StringTools_inl.hpp b/include/CLI/impl/StringTools_inl.hpp index e3852c513..04aef1bf6 100644 --- a/include/CLI/impl/StringTools_inl.hpp +++ b/include/CLI/impl/StringTools_inl.hpp @@ -184,17 +184,17 @@ find_member(std::string name, const std::vector names, bool ignore_ return (it != std::end(names)) ? (it - std::begin(names)) : (-1); } -#if defined(CLI11_CPP17) +//#if defined(CLI11_CPP17) CLI11_MODULE_INLINE const std::string &escapedChars("\b\t\n\f\r\"\\"); CLI11_MODULE_INLINE const std::string &escapedCharsCode("btnfr\"\\"); CLI11_MODULE_INLINE const std::string &bracketChars("\"'`[(<{"); CLI11_MODULE_INLINE const std::string &matchBracketChars("\"'`])>}"); -#else -static const std::string &escapedChars{"\b\t\n\f\r\"\\"}; -static const std::string &escapedCharsCode{"btnfr\"\\"}; -static const std::string &bracketChars{"\"'`[(<{"}; -static const std::string &matchBracketChars{"\"'`])>}"}; -#endif +//#else +//static const std::string &escapedChars{"\b\t\n\f\r\"\\"}; +//static const std::string &escapedCharsCode{"btnfr\"\\"}; +//static const std::string &bracketChars{"\"'`[(<{"}; +//static const std::string &matchBracketChars{"\"'`])>}"}; +//#endif CLI11_INLINE bool has_escapable_character(const std::string &str) { return (str.find_first_of(escapedChars) != std::string::npos); From a2148d18316e2eabb5dd155dd23cf5bd82b990ca Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 22 Nov 2025 14:25:01 +0000 Subject: [PATCH 27/29] style: pre-commit.ci fixes --- include/CLI/impl/StringTools_inl.hpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/include/CLI/impl/StringTools_inl.hpp b/include/CLI/impl/StringTools_inl.hpp index 04aef1bf6..306d43620 100644 --- a/include/CLI/impl/StringTools_inl.hpp +++ b/include/CLI/impl/StringTools_inl.hpp @@ -184,17 +184,17 @@ find_member(std::string name, const std::vector names, bool ignore_ return (it != std::end(names)) ? (it - std::begin(names)) : (-1); } -//#if defined(CLI11_CPP17) +// #if defined(CLI11_CPP17) CLI11_MODULE_INLINE const std::string &escapedChars("\b\t\n\f\r\"\\"); CLI11_MODULE_INLINE const std::string &escapedCharsCode("btnfr\"\\"); CLI11_MODULE_INLINE const std::string &bracketChars("\"'`[(<{"); CLI11_MODULE_INLINE const std::string &matchBracketChars("\"'`])>}"); -//#else -//static const std::string &escapedChars{"\b\t\n\f\r\"\\"}; -//static const std::string &escapedCharsCode{"btnfr\"\\"}; -//static const std::string &bracketChars{"\"'`[(<{"}; -//static const std::string &matchBracketChars{"\"'`])>}"}; -//#endif +// #else +// static const std::string &escapedChars{"\b\t\n\f\r\"\\"}; +// static const std::string &escapedCharsCode{"btnfr\"\\"}; +// static const std::string &bracketChars{"\"'`[(<{"}; +// static const std::string &matchBracketChars{"\"'`])>}"}; +// #endif CLI11_INLINE bool has_escapable_character(const std::string &str) { return (str.find_first_of(escapedChars) != std::string::npos); From eb6800ae7ae1d29b192ecac0a1d78cd60846bfff Mon Sep 17 00:00:00 2001 From: Philip Top Date: Sat, 22 Nov 2025 08:01:03 -0800 Subject: [PATCH 28/29] more code cleanup --- book/chapters/installation.md | 29 ++++++++++++++-------------- include/CLI/Macros.hpp | 2 +- include/CLI/impl/StringTools_inl.hpp | 9 ++------- 3 files changed, 18 insertions(+), 22 deletions(-) diff --git a/book/chapters/installation.md b/book/chapters/installation.md index 37f1876db..042709a79 100644 --- a/book/chapters/installation.md +++ b/book/chapters/installation.md @@ -176,20 +176,21 @@ Total Test time (real) = 0.34 sec For the curious, the CMake options and defaults are listed below. Most options default to off if CLI11 is used as a subdirectory in another project. -| Option | Description | -| ------------------------------ | -------------------------------------------------------------------------------- | -| `CLI11_SINGLE_FILE=ON` | Build the `CLI11.hpp` file from the sources. Requires Python (version 3 or 2.7). | -| `CLI11_PRECOMPILED=OFF` | generate a precompiled static library instead of header-only | -| `CLI11_MODULE=OFF` | change some code segments for named constants to allow use of CLI11 in a module | -| `CLI11_SINGLE_FILE_TESTS=OFF` | Run the tests on the generated single file version as well | -| `CLI11_BUILD_DOCS=ON` | build CLI11 documentation and book | -| `CLI11_BUILD_EXAMPLES=ON` | Build the example programs. | -| `CLI11_BUILD_EXAMPLES_JSON=ON` | Build some additional example using json libraries | -| `CLI11_INSTALL=ON` | install CLI11 to the install folder during the install process | -| `CLI11_FULL_INSTALL=ON` | install all CLI11 headers/libraries regardless of other settings | -| `CLI11_FORCE_LIBCXX=OFF` | use libc++ instead of libstdc++ if building with clang on linux | -| `CLI11_CUDA_TESTS=OFF` | build the tests with NVCC | -| `CLI11_BUILD_TESTS=ON` | Build the tests. | +| Option | Description | +| -------------------------------- | -------------------------------------------------------------------------------- | +| `CLI11_SINGLE_FILE=ON` | Build the `CLI11.hpp` file from the sources. Requires Python (version 3 or 2.7). | +| `CLI11_PRECOMPILED=OFF` | Generate a precompiled static library instead of header-only | +| `CLI11_INSTALL_PACKAGE_TESTS=OFF`| Run tests checking the installation | +| `CLI11_MODULE_TEST=OFF` | Run a test checking that CLI11 works with modules | +| `CLI11_SINGLE_FILE_TESTS=OFF` | Run the tests on the generated single file version as well | +| `CLI11_BUILD_DOCS=ON` | Build CLI11 documentation and book | +| `CLI11_BUILD_EXAMPLES=ON` | Build the example programs. | +| `CLI11_BUILD_EXAMPLES_JSON=ON` | Build some additional example using json libraries | +| `CLI11_INSTALL=ON` | Install CLI11 to the install folder during the install process | +| `CLI11_FULL_INSTALL=ON` | Install all CLI11 headers/libraries regardless of other settings | +| `CLI11_FORCE_LIBCXX=OFF` | Use libc++ instead of libstdc++ if building with clang on linux | +| `CLI11_CUDA_TESTS=OFF` | Build the tests with NVCC | +| `CLI11_BUILD_TESTS=ON` | Build the tests. | [^1]: Docker is being used to create a pristine disposable environment; there is diff --git a/include/CLI/Macros.hpp b/include/CLI/Macros.hpp index b1a6321c9..8caff0cc6 100644 --- a/include/CLI/Macros.hpp +++ b/include/CLI/Macros.hpp @@ -176,7 +176,7 @@ #define CLI11_INLINE inline #endif -/** Module inline**/ +/** Module inline to support module operations**/ #if defined CLI11_CPP17 #define CLI11_MODULE_INLINE inline #else diff --git a/include/CLI/impl/StringTools_inl.hpp b/include/CLI/impl/StringTools_inl.hpp index 306d43620..bf7dc0ad6 100644 --- a/include/CLI/impl/StringTools_inl.hpp +++ b/include/CLI/impl/StringTools_inl.hpp @@ -184,17 +184,12 @@ find_member(std::string name, const std::vector names, bool ignore_ return (it != std::end(names)) ? (it - std::begin(names)) : (-1); } -// #if defined(CLI11_CPP17) + CLI11_MODULE_INLINE const std::string &escapedChars("\b\t\n\f\r\"\\"); CLI11_MODULE_INLINE const std::string &escapedCharsCode("btnfr\"\\"); CLI11_MODULE_INLINE const std::string &bracketChars("\"'`[(<{"); CLI11_MODULE_INLINE const std::string &matchBracketChars("\"'`])>}"); -// #else -// static const std::string &escapedChars{"\b\t\n\f\r\"\\"}; -// static const std::string &escapedCharsCode{"btnfr\"\\"}; -// static const std::string &bracketChars{"\"'`[(<{"}; -// static const std::string &matchBracketChars{"\"'`])>}"}; -// #endif + CLI11_INLINE bool has_escapable_character(const std::string &str) { return (str.find_first_of(escapedChars) != std::string::npos); From b973307b429fb10d6d716d1d590d45a377667e15 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 22 Nov 2025 16:12:42 +0000 Subject: [PATCH 29/29] style: pre-commit.ci fixes --- book/chapters/installation.md | 30 ++++++++++++++-------------- include/CLI/impl/StringTools_inl.hpp | 1 - 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/book/chapters/installation.md b/book/chapters/installation.md index 042709a79..49a27b76f 100644 --- a/book/chapters/installation.md +++ b/book/chapters/installation.md @@ -176,21 +176,21 @@ Total Test time (real) = 0.34 sec For the curious, the CMake options and defaults are listed below. Most options default to off if CLI11 is used as a subdirectory in another project. -| Option | Description | -| -------------------------------- | -------------------------------------------------------------------------------- | -| `CLI11_SINGLE_FILE=ON` | Build the `CLI11.hpp` file from the sources. Requires Python (version 3 or 2.7). | -| `CLI11_PRECOMPILED=OFF` | Generate a precompiled static library instead of header-only | -| `CLI11_INSTALL_PACKAGE_TESTS=OFF`| Run tests checking the installation | -| `CLI11_MODULE_TEST=OFF` | Run a test checking that CLI11 works with modules | -| `CLI11_SINGLE_FILE_TESTS=OFF` | Run the tests on the generated single file version as well | -| `CLI11_BUILD_DOCS=ON` | Build CLI11 documentation and book | -| `CLI11_BUILD_EXAMPLES=ON` | Build the example programs. | -| `CLI11_BUILD_EXAMPLES_JSON=ON` | Build some additional example using json libraries | -| `CLI11_INSTALL=ON` | Install CLI11 to the install folder during the install process | -| `CLI11_FULL_INSTALL=ON` | Install all CLI11 headers/libraries regardless of other settings | -| `CLI11_FORCE_LIBCXX=OFF` | Use libc++ instead of libstdc++ if building with clang on linux | -| `CLI11_CUDA_TESTS=OFF` | Build the tests with NVCC | -| `CLI11_BUILD_TESTS=ON` | Build the tests. | +| Option | Description | +| --------------------------------- | -------------------------------------------------------------------------------- | +| `CLI11_SINGLE_FILE=ON` | Build the `CLI11.hpp` file from the sources. Requires Python (version 3 or 2.7). | +| `CLI11_PRECOMPILED=OFF` | Generate a precompiled static library instead of header-only | +| `CLI11_INSTALL_PACKAGE_TESTS=OFF` | Run tests checking the installation | +| `CLI11_MODULE_TEST=OFF` | Run a test checking that CLI11 works with modules | +| `CLI11_SINGLE_FILE_TESTS=OFF` | Run the tests on the generated single file version as well | +| `CLI11_BUILD_DOCS=ON` | Build CLI11 documentation and book | +| `CLI11_BUILD_EXAMPLES=ON` | Build the example programs. | +| `CLI11_BUILD_EXAMPLES_JSON=ON` | Build some additional example using json libraries | +| `CLI11_INSTALL=ON` | Install CLI11 to the install folder during the install process | +| `CLI11_FULL_INSTALL=ON` | Install all CLI11 headers/libraries regardless of other settings | +| `CLI11_FORCE_LIBCXX=OFF` | Use libc++ instead of libstdc++ if building with clang on linux | +| `CLI11_CUDA_TESTS=OFF` | Build the tests with NVCC | +| `CLI11_BUILD_TESTS=ON` | Build the tests. | [^1]: Docker is being used to create a pristine disposable environment; there is diff --git a/include/CLI/impl/StringTools_inl.hpp b/include/CLI/impl/StringTools_inl.hpp index bf7dc0ad6..d0a622346 100644 --- a/include/CLI/impl/StringTools_inl.hpp +++ b/include/CLI/impl/StringTools_inl.hpp @@ -190,7 +190,6 @@ CLI11_MODULE_INLINE const std::string &escapedCharsCode("btnfr\"\\"); CLI11_MODULE_INLINE const std::string &bracketChars("\"'`[(<{"); CLI11_MODULE_INLINE const std::string &matchBracketChars("\"'`])>}"); - CLI11_INLINE bool has_escapable_character(const std::string &str) { return (str.find_first_of(escapedChars) != std::string::npos); }