From b3dab2e80e17d70809ff3183bdeab6ad160522ea Mon Sep 17 00:00:00 2001 From: Cam Mannett Date: Tue, 8 Aug 2023 17:11:27 +0100 Subject: [PATCH] arg_router v1.4.0 (#344) * Update vcpkg submodule commit for v1.3.0 recipe * #312 Reduce code duplication in GHAs Fix #312 * README.md grammar and spelling improvements * #318 Skip irelevant GHA jobs Fix #318 * #321 Add variable list length end token Fix #321 * Fix clang-format error * Updating README.md unit test coverage badge * #302 Switch from a locally generated Conan package, to one hosted on conan-center.io Fix #302 * Fixed copyrights * Moved Conan jobs back into merge checker workflow * Add Conan to README's installation options list * #314 Add support for multi-value arguments (#326) * #314 Add support for multi-value arguments Fix #314 * Fix linting, Win and GCCv9 compilation errors * Added missing tuple include * Moved tuple include... * Multiple clang-tidy fixes Still can't reproduce the GCCv9 error locally. * Fix missing MSVC toolset for local builds Updated timeout time * Increased timeouts again Windows tests are hanging, so disabling for now and will create a bug ticket. * Updating README.md unit test coverage badge * #327 Fix MSVC builds (#328) * #327 Fix MSVC builds Fix #327 * Fixed copyright year. * Extended MSVC test run timeout * Fixed MSVC assert failure * Updating README.md unit test coverage badge * #325 Create copyright checker git hook (#329) Fix #325 * #330 Explicitly specify NOMINMAX and WIN32_LEAN_AND_MEAN (#331) * #330 Explicitly specify NOMINMAX and WIN32_LEAN_AND_MEAN Fix #330 Also: * Suppressing deprecation warnings in unit tests on Windows * Workarounds for invalid C4702 (unreachable code) warnings on MSVC * Fixed formatting * #332 Extend pre-commit hook to include clang-format (#333) Fix #332 * #317 Enable/disable nodes at runtime (#334) Fix #317 * #315 CMake function to generate translation type headers from simple text files (#336) Fix #315 * #338 arg_router_translation_generator isn't detecting changes in the TOML files (#339) Fix #338 * #335 Use Levenshtein distance to suggest possibly misspelt arguments (#337) * #335 Use Levenshtein distance to suggest possibly misspelt arguments Fix #335 * #320 Review parsing code (#340) Fix #320 * arg_router v1.4.0 (#343) Bug fixes * #330, Explicitly specify NOMINMAX and WIN32_LEAN_AND_MEAN Improvements * #302, Switch from a locally generated Conan package, to one hosted on conan-center.io * #314, Add support for multi-value arguments * #315, CMake function to generate translation type headers from simple text files * #317, Enable/disable nodes at runtime * #318, Skip irrelevant GHA jobs * #321, Add variable list length end token * #325, Create copyright checker git hook * #332, Extend pre-commit hook to include clang-format * #335, Use Levenshtein distance to suggest possibly misspelt arguments * Fix vcpkg_test_package CMakeLists.txt (#345) Used incorrect target name. * Added missing env vars to vcpkg test package GHA (#346) * vcpkg test package needs C++20 for Windows build (#347) * Disable Windows vcpkg test package for merge_checker (#348) v1.3.0 cannot be built in the latest MSVC, so this test is broken until vcpkg is carrying the v1.4.0 package. * clang_cl example build fix (#350) * Fix clang_cl support in test package (#352) Added missing constexpr step limit increase. --- .clang-format | 3 + .github/filters.yml | 44 ++ .github/workflows/bootstrap_vcpkg/action.yml | 53 ++ .github/workflows/conan_setup/action.yml | 37 + .github/workflows/docs_pusher.yml | 47 +- .github/workflows/merge_checker.yml | 258 +++---- .github/workflows/pr_checker.yml | 173 ++--- .github/workflows/release_builder.yml | 34 +- CMakeLists.txt | 12 +- README.md | 215 +++++- {scripts/ci => ci}/calculate_test_coverage.sh | 6 +- ci/conan_test_project/CMakeLists.txt | 13 + ci/conan_test_project/conanfile.py | 31 + ci/conan_test_project/main.cpp | 70 ++ {scripts/ci => ci}/create_badge_url.sh | 4 +- ci/old_coverage | 1 + .../package_test_project/CMakeLists.txt | 40 +- .../ci => ci}/package_test_project/vcpkg.json | 0 .../vcpkg_test_project/CMakeLists.txt | 14 +- .../ci => ci}/vcpkg_test_project/main.cpp | 0 .../ci => ci}/vcpkg_test_project/vcpkg.json | 0 cmake/build_types/documentation_script.cmake | 2 +- cmake/build_types/unit_test.cmake | 30 +- cmake/build_types/unit_test_coverage.cmake | 15 +- cmake/package/arg_router-config.cmake.in | 1 + cmake/package/install.cmake | 10 +- .../translation_generator.cmake | 41 ++ .../translation_generator_script.cmake | 134 ++++ cmake/versioning/version.cmake | 15 - conanfile.py | 55 -- docs/Doxyfile | 362 ++++++---- docs/related_pages/architecture.doxy | 87 ++- docs/related_pages/examples.doxy | 6 + docs/related_pages/help.doxy | 34 +- examples/CMakeLists.txt | 19 +- examples/c++17/CMakeLists.txt | 5 +- examples/c++17/basic_cat/CMakeLists.txt | 2 +- .../custom_policy_and_node/CMakeLists.txt | 2 +- .../c++17/custom_policy_and_node/main.cpp | 42 +- examples/c++17/just_cats/CMakeLists.txt | 2 +- examples/c++17/launcher/CMakeLists.txt | 28 + examples/c++17/launcher/main.cpp | 104 +++ .../c++17/runtime_node_enable/CMakeLists.txt | 22 + examples/c++17/runtime_node_enable/main.cpp | 59 ++ examples/c++17/simple/CMakeLists.txt | 2 +- examples/c++17/simple_ml/CMakeLists.txt | 2 +- examples/c++17/simple_ml/main.cpp | 16 +- examples/c++17/simple_ml_gen/CMakeLists.txt | 35 + examples/c++17/simple_ml_gen/main.cpp | 108 +++ examples/c++20/CMakeLists.txt | 5 +- examples/c++20/basic_cat/CMakeLists.txt | 2 +- .../custom_policy_and_node/CMakeLists.txt | 2 +- .../c++20/custom_policy_and_node/main.cpp | 45 +- examples/c++20/just_cats/CMakeLists.txt | 2 +- examples/c++20/launcher/CMakeLists.txt | 28 + examples/c++20/launcher/main.cpp | 101 +++ .../c++20/runtime_node_enable/CMakeLists.txt | 22 + examples/c++20/runtime_node_enable/main.cpp | 58 ++ examples/c++20/simple/CMakeLists.txt | 2 +- examples/c++20/simple_ml/CMakeLists.txt | 2 +- examples/c++20/simple_ml/main.cpp | 16 +- examples/c++20/simple_ml_gen/CMakeLists.txt | 35 + examples/c++20/simple_ml_gen/main.cpp | 108 +++ examples/resources/simple_ml_gen/en_GB.toml | 19 + examples/resources/simple_ml_gen/fr.toml | 37 + examples/resources/simple_ml_gen/ja.toml | 37 + external/vcpkg | 2 +- include/arg_router/algorithm.hpp | 40 ++ include/arg_router/arg.hpp | 61 +- include/arg_router/arg_router.hpp | 4 +- include/arg_router/config.hpp | 7 + include/arg_router/dependency/alias_group.hpp | 13 +- include/arg_router/dependency/detail.hpp | 36 +- include/arg_router/dependency/one_of.hpp | 14 +- include/arg_router/exception.hpp | 85 ++- include/arg_router/forwarding_arg.hpp | 112 +++ include/arg_router/help.hpp | 107 ++- include/arg_router/list.hpp | 2 +- include/arg_router/mode.hpp | 227 ++++-- include/arg_router/multi_arg.hpp | 124 ++++ include/arg_router/multi_arg_base.hpp | 136 ++++ include/arg_router/multi_lang/root.hpp | 1 + .../arg_router/multi_lang/root_wrapper.hpp | 5 +- .../parsing/dynamic_token_adapter.hpp | 27 +- include/arg_router/parsing/global_parser.hpp | 9 +- include/arg_router/parsing/parse_target.hpp | 34 +- include/arg_router/parsing/parsing.hpp | 67 +- include/arg_router/parsing/pre_parse_data.hpp | 10 +- include/arg_router/parsing/token_type.hpp | 33 +- .../parsing/unknown_argument_handling.hpp | 36 + include/arg_router/policy/alias.hpp | 4 +- .../policy/colour_help_formatter.hpp | 43 ++ .../policy/default_help_formatter.hpp | 132 +++- include/arg_router/policy/default_value.hpp | 4 +- .../policy/exception_translator.hpp | 4 +- include/arg_router/policy/min_max_count.hpp | 133 ++-- .../arg_router/policy/multi_stage_value.hpp | 3 +- include/arg_router/policy/runtime_enable.hpp | 127 ++++ .../arg_router/policy/short_form_expander.hpp | 3 +- .../arg_router/policy/token_end_marker.hpp | 113 +++ include/arg_router/policy/validator.hpp | 81 ++- include/arg_router/policy/value_separator.hpp | 11 + include/arg_router/positional_arg.hpp | 99 +-- include/arg_router/root.hpp | 36 +- include/arg_router/traits.hpp | 117 +++- include/arg_router/tree_node.hpp | 238 +++++-- include/arg_router/tree_node_fwd.hpp | 16 +- .../utility/compile_time_string.hpp | 26 + .../utility/dynamic_string_view.hpp | 216 ++++++ .../utility/exception_formatter.hpp | 177 +++++ include/arg_router/utility/terminal.hpp | 4 +- include/arg_router/utility/tree_recursor.hpp | 5 +- include/arg_router/utility/tuple_iterator.hpp | 3 +- .../utility/utf8/levenshtein_distance.hpp | 148 ++++ include/arg_router/utility/win_api.hpp | 24 + include/arg_router/version.hpp | 2 +- scripts/ci/conan_test_project/CMakeLists.txt | 26 - scripts/ci/conan_test_project/conanfile.txt | 6 - scripts/ci/docker/Dockerfile | 14 - scripts/ci/docker/README.md | 11 - scripts/ci/docker/base.sh | 27 - scripts/ci/docker/clang++-9.sh | 8 - scripts/ci/docker/clang-9.sh | 8 - scripts/ci/docker/cmake.sh | 8 - scripts/ci/docker/g++-9.sh | 8 - scripts/ci/docker/gcc-9.sh | 8 - scripts/ci/old_coverage | 1 - scripts/copyright_checker.py | 10 +- scripts/pre-commit | 26 + test/algorithm_test.cpp | 60 +- test/arg_test.cpp | 7 +- test/dependency/alias_group_test.cpp | 76 ++ test/dependency/one_of_test.cpp | 76 ++ test/forwarding_arg_test.cpp | 207 ++++++ test/help_test.cpp | 94 ++- test/mode_test.cpp | 117 ++-- test/multi_arg_test.cpp | 223 ++++++ test/multi_lang/root_test.cpp | 156 +++-- test/multi_lang/root_wrapper_test.cpp | 45 +- test/parsing/dynamic_token_adapter_test.cpp | 36 + test/parsing/parsing_test.cpp | 154 +++- test/policy/colour_help_formatter_test.cpp | 59 +- test/policy/default_help_formatter_test.cpp | 50 +- test/policy/exception_translator_test.cpp | 2 +- test/policy/min_max_count_test.cpp | 270 +++---- test/policy/required_test.cpp | 4 +- test/policy/runtime_enable_test.cpp | 210 ++++++ test/policy/token_end_marker_test.cpp | 340 +++++++++ test/policy/validator_rule_utilities_test.cpp | 8 +- test/policy/validator_test.cpp | 101 ++- test/policy/value_separator_test.cpp | 14 +- test/positional_arg_test.cpp | 2 +- test/root_test.cpp | 657 +++++------------- test/root_tests/basic_test.cpp | 515 ++++++++++++++ test/root_tests/positional_arg_test.cpp | 226 ++++++ test/root_tests/top_level_test.cpp | 305 +++++++- test/root_tests/variable_length_test.cpp | 296 ++++++++ test/translation_generator_test.cpp | 178 +++++ test/tree_node_test.cpp | 49 +- test/utility/dynamic_string_view_test.cpp | 341 +++++++++ test/utility/exception_formatter_test.cpp | 169 +++++ test/utility/tree_recursor_test.cpp | 6 +- .../utf8/levenshtein_distance_test.cpp | 153 ++++ 163 files changed, 8988 insertions(+), 2156 deletions(-) create mode 100644 .github/filters.yml create mode 100644 .github/workflows/bootstrap_vcpkg/action.yml create mode 100644 .github/workflows/conan_setup/action.yml rename {scripts/ci => ci}/calculate_test_coverage.sh (94%) create mode 100644 ci/conan_test_project/CMakeLists.txt create mode 100644 ci/conan_test_project/conanfile.py create mode 100644 ci/conan_test_project/main.cpp rename {scripts/ci => ci}/create_badge_url.sh (90%) create mode 100644 ci/old_coverage rename {scripts/ci => ci}/package_test_project/CMakeLists.txt (51%) rename {scripts/ci => ci}/package_test_project/vcpkg.json (100%) rename {scripts/ci => ci}/vcpkg_test_project/CMakeLists.txt (71%) rename {scripts/ci => ci}/vcpkg_test_project/main.cpp (100%) rename {scripts/ci => ci}/vcpkg_test_project/vcpkg.json (100%) create mode 100644 cmake/translation_generator/translation_generator.cmake create mode 100644 cmake/translation_generator/translation_generator_script.cmake delete mode 100644 conanfile.py create mode 100644 examples/c++17/launcher/CMakeLists.txt create mode 100644 examples/c++17/launcher/main.cpp create mode 100644 examples/c++17/runtime_node_enable/CMakeLists.txt create mode 100644 examples/c++17/runtime_node_enable/main.cpp create mode 100644 examples/c++17/simple_ml_gen/CMakeLists.txt create mode 100644 examples/c++17/simple_ml_gen/main.cpp create mode 100644 examples/c++20/launcher/CMakeLists.txt create mode 100644 examples/c++20/launcher/main.cpp create mode 100644 examples/c++20/runtime_node_enable/CMakeLists.txt create mode 100644 examples/c++20/runtime_node_enable/main.cpp create mode 100644 examples/c++20/simple_ml_gen/CMakeLists.txt create mode 100644 examples/c++20/simple_ml_gen/main.cpp create mode 100644 examples/resources/simple_ml_gen/en_GB.toml create mode 100644 examples/resources/simple_ml_gen/fr.toml create mode 100644 examples/resources/simple_ml_gen/ja.toml create mode 100644 include/arg_router/forwarding_arg.hpp create mode 100644 include/arg_router/multi_arg.hpp create mode 100644 include/arg_router/multi_arg_base.hpp create mode 100644 include/arg_router/parsing/unknown_argument_handling.hpp create mode 100644 include/arg_router/policy/runtime_enable.hpp create mode 100644 include/arg_router/policy/token_end_marker.hpp create mode 100644 include/arg_router/utility/dynamic_string_view.hpp create mode 100644 include/arg_router/utility/exception_formatter.hpp create mode 100644 include/arg_router/utility/utf8/levenshtein_distance.hpp create mode 100644 include/arg_router/utility/win_api.hpp delete mode 100644 scripts/ci/conan_test_project/CMakeLists.txt delete mode 100644 scripts/ci/conan_test_project/conanfile.txt delete mode 100644 scripts/ci/docker/Dockerfile delete mode 100644 scripts/ci/docker/README.md delete mode 100755 scripts/ci/docker/base.sh delete mode 100755 scripts/ci/docker/clang++-9.sh delete mode 100755 scripts/ci/docker/clang-9.sh delete mode 100755 scripts/ci/docker/cmake.sh delete mode 100755 scripts/ci/docker/g++-9.sh delete mode 100755 scripts/ci/docker/gcc-9.sh delete mode 100644 scripts/ci/old_coverage create mode 100755 scripts/pre-commit create mode 100644 test/forwarding_arg_test.cpp create mode 100644 test/multi_arg_test.cpp create mode 100644 test/policy/runtime_enable_test.cpp create mode 100644 test/policy/token_end_marker_test.cpp create mode 100644 test/root_tests/basic_test.cpp create mode 100644 test/root_tests/variable_length_test.cpp create mode 100644 test/translation_generator_test.cpp create mode 100644 test/utility/dynamic_string_view_test.cpp create mode 100644 test/utility/exception_formatter_test.cpp create mode 100644 test/utility/utf8/levenshtein_distance_test.cpp diff --git a/.clang-format b/.clang-format index ff79e753..94e3ff22 100644 --- a/.clang-format +++ b/.clang-format @@ -17,6 +17,9 @@ ConstructorInitializerAllOnOneLineOrOnePerLine: false IndentCaseLabels: false IndentPPDirectives: AfterHash IndentWidth: 4 +PointerAlignment: Left +QualifierAlignment: Left +ReferenceAlignment: Pointer ReflowComments: true SortUsingDeclarations: false SpacesInContainerLiterals: false diff --git a/.github/filters.yml b/.github/filters.yml new file mode 100644 index 00000000..b87ea178 --- /dev/null +++ b/.github/filters.yml @@ -0,0 +1,44 @@ +pr_checker: + - ".github/workflows/pr_checker.yml" + - ".github/workflows/bootstrap_vcpkg/action.yml" + +merge_checker: + - ".github/workflows/merge_checker.yml" + - ".github/workflows/bootstrap_vcpkg/action.yml" + +docs_pusher: + - ".github/workflows/docs_pusher.yml" + - ".github/workflows/bootstrap_vcpkg/action.yml" + +ci: &ci + - "scripts/copyright_checker.py" + - "ci/calculate_test_coverage.sh" + - "ci/create_badge_url.sh" + +source: &source + - *ci + - "include/**" + - "CMakeLists.txt" + - "cmake/**" + - "vcpkg.json" + - ".gitmodules" + - ".clang-format" + - ".clang-tidy" + +source_tests_and_examples: + - *source + - "test/**" + - "examples/**" + - "ci/package_test_project/**" + +docs: + - "cmake/build_types/documentation*" + - "docs/**" + - "README.md" + +vcpkg_test_project: + - "ci/vcpkg_test_project/**" + +conan_test_project: + - ".github/workflows/conan_setup/action.yml" + - "ci/conan_test_project/**" \ No newline at end of file diff --git a/.github/workflows/bootstrap_vcpkg/action.yml b/.github/workflows/bootstrap_vcpkg/action.yml new file mode 100644 index 00000000..14707e16 --- /dev/null +++ b/.github/workflows/bootstrap_vcpkg/action.yml @@ -0,0 +1,53 @@ +name: Bootstrap vcpkg and Configure NuGet +description: Bootsraps vcpkg with disabled metrics, and configures NuGet as a bianry cache + +inputs: + token: + description: "GitHub password token" + required: true + +runs: + using: "composite" + steps: + - name: Configure env vars + shell: bash + run: | + echo "NUGET_SOURCE_URL=https://nuget.pkg.github.com/cmannett85/index.json" >> $GITHUB_ENV + echo "VCPKG_BINARY_SOURCES=clear;nuget,vcpkg-cache,readwrite" >> $GITHUB_ENV + + - name: Bootstrap vcpkg to build the NuGet client on Linux and MacOS + if: runner.os != 'Windows' + shell: bash + run: | + cd ${{ github.workspace }} + ./external/vcpkg/bootstrap-vcpkg.sh -disableMetrics + + - name: Bootstrap vcpkg to build the NuGet client on Windows + if: runner.os == 'Windows' + shell: pwsh + run: | + cd ${{ github.workspace }} + ./external/vcpkg/bootstrap-vcpkg.bat -disableMetrics + + - name: Configure NuGet client on Linux and MacOS + if: runner.os != 'Windows' + shell: bash + run: | + mono `./external/vcpkg/vcpkg fetch nuget | tail -n 1` \ + sources add \ + -source ${{ env.NUGET_SOURCE_URL }} \ + -storepasswordincleartext \ + -name "vcpkg-cache" \ + -username "cmannett85" \ + -password "${{ inputs.token }}" + mono `./external/vcpkg/vcpkg fetch nuget | tail -n 1` \ + setapikey "${{ inputs.token }}" \ + -source ${{ env.NUGET_SOURCE_URL }} + + - name: Configure NuGet client on Windows + if: runner.os == 'Windows' + shell: pwsh + run: | + [array] $output = ./external/vcpkg/vcpkg.exe fetch nuget + & $output[-1] sources add -source ${{ env.NUGET_SOURCE_URL }} -storepasswordincleartext -name "vcpkg-cache" -username "cmannett85" -password "${{ inputs.token }}" + & $output[-1] setapikey "${{ inputs.token }}" -source ${{ env.NUGET_SOURCE_URL }} diff --git a/.github/workflows/conan_setup/action.yml b/.github/workflows/conan_setup/action.yml new file mode 100644 index 00000000..c03dc59f --- /dev/null +++ b/.github/workflows/conan_setup/action.yml @@ -0,0 +1,37 @@ +name: Installs and configures Conan +description: Installs and configures Conan for a particular C++ version (Linux only) + +inputs: + version: + description: "C++ language version" + build_dir: + description: "Build directory for the Conan test project" + +runs: + using: "composite" + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 + with: + python-version: '3.x' + + - name: Update packages + shell: bash + run: | + sudo apt update + sudo apt install ninja-build + python -m pip install --upgrade pip conan + + - name: Build Conan profile + shell: bash + run: | + conan profile detect --force + sed -i "s/^compiler.cppstd=.*/compiler.cppstd=${{ inputs.version }}/g" `conan profile path default` + + - name: Build + shell: bash + run: | + conan install ${{ github.workspace }}/ci/conan_test_project --output-folder=${{ inputs.build_dir }} --build=missing + cd ${{ inputs.build_dir }} + cmake ${{ github.workspace }}/ci/conan_test_project/ -G "Ninja" -DCMAKE_TOOLCHAIN_FILE=./build/Release/generators/conan_toolchain.cmake -DCMAKE_BUILD_TYPE=Release + cmake --build . diff --git a/.github/workflows/docs_pusher.yml b/.github/workflows/docs_pusher.yml index ea248d47..6ce9f451 100644 --- a/.github/workflows/docs_pusher.yml +++ b/.github/workflows/docs_pusher.yml @@ -8,16 +8,36 @@ on: env: SKIP_COVERAGE_UPDATE: 1 - NUGET_SOURCE_URL: "https://nuget.pkg.github.com/cmannett85/index.json" - VCPKG_BINARY_SOURCES: 'clear;nuget,vcpkg-cache,readwrite' BUILD_DIR: ${{ github.workspace }}/build jobs: + # Skip jobs based on what files have changed + changes: + runs-on: ubuntu-latest + permissions: + pull-requests: read + outputs: + docs_pusher: ${{ steps.filter.outputs.docs_pusher }} + source_tests_and_examples: ${{ steps.filter.outputs.source_tests_and_examples }} + docs: ${{ steps.filter.outputs.docs }} + steps: + - uses: actions/checkout@v3 + - uses: dorny/paths-filter@v2 + id: filter + with: + initial-fetch-depth: 2 + filters: .github/filters.yml + documentation: runs-on: ubuntu-latest + needs: changes + if: | + needs.changes.outputs.docs_pusher == 'true' || + needs.changes.outputs.source_tests_and_examples == 'true' || + needs.changes.outputs.docs == 'true' steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: submodules: true @@ -26,22 +46,9 @@ jobs: sudo apt update sudo apt install lcov ninja-build doxygen graphviz - - name: Bootstrap vcpkg to build the NuGet client - run: | - ./external/vcpkg/bootstrap-vcpkg.sh -disableMetrics - - - name: Configure NuGet client - run: | - mono `./external/vcpkg/vcpkg fetch nuget | tail -n 1` \ - sources add \ - -source ${{ env.NUGET_SOURCE_URL }} \ - -storepasswordincleartext \ - -name "vcpkg-cache" \ - -username "cmannett85" \ - -password "${{ secrets.GITHUB_TOKEN }}" - mono `./external/vcpkg/vcpkg fetch nuget | tail -n 1` \ - setapikey "${{ secrets.GITHUB_TOKEN }}" \ - -source ${{ env.NUGET_SOURCE_URL }} + - uses: ./.github/workflows/bootstrap_vcpkg + with: + token: "${{ secrets.GITHUB_TOKEN }}" - name: Generate API documentation and build Unit Tests timeout-minutes: 30 @@ -59,7 +66,7 @@ jobs: - name: Generate coverage report run: | - cd ${{ github.workspace }}/scripts/ci + cd ${{ github.workspace }}/ci ./calculate_test_coverage.sh ${{ env.BUILD_DIR }} cd ${{ env.BUILD_DIR }} genhtml arg_router.info --no-function-coverage --title "arg_router Unit Test Code Coverage" --legend --highlight --output-directory gcov_html diff --git a/.github/workflows/merge_checker.yml b/.github/workflows/merge_checker.yml index 421d5a4f..d97da72f 100644 --- a/.github/workflows/merge_checker.yml +++ b/.github/workflows/merge_checker.yml @@ -7,20 +7,39 @@ on: env: SKIP_COVERAGE_UPDATE: 1 - NUGET_SOURCE_URL: "https://nuget.pkg.github.com/cmannett85/index.json" - VCPKG_BINARY_SOURCES: 'clear;nuget,vcpkg-cache,readwrite' BUILD_DIR: ${{ github.workspace }}/build INSTALL_DIR: ${{ github.workspace }}/install PACKAGE_BUILD_DIR: ${{ github.workspace }}/package_build DOWNLOAD_DIR: ${{ github.workspace }}/download jobs: + # Skip jobs based on what files have changed + changes: + runs-on: ubuntu-latest + permissions: + pull-requests: read + outputs: + merge_checker: ${{ steps.filter.outputs.merge_checker }} + source_tests_and_examples: ${{ steps.filter.outputs.source_tests_and_examples }} + docs: ${{ steps.filter.outputs.docs }} + vcpkg_test_project: ${{ steps.filter.outputs.vcpkg_test_project }} + steps: + - uses: actions/checkout@v3 + - uses: dorny/paths-filter@v2 + id: filter + with: + filters: .github/filters.yml + # Run the more esoteric/likely to succeed unit tests when merging to main sanitizer_unit_tests: runs-on: ubuntu-22.04 + needs: changes + if: | + needs.changes.outputs.merge_checker == 'true' || + needs.changes.outputs.source_tests_and_examples == 'true' steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: submodules: true @@ -29,22 +48,9 @@ jobs: sudo apt update sudo apt install ninja-build - - name: Bootstrap vcpkg to build the NuGet client - run: | - ./external/vcpkg/bootstrap-vcpkg.sh -disableMetrics - - - name: Configure NuGet client - run: | - mono `./external/vcpkg/vcpkg fetch nuget | tail -n 1` \ - sources add \ - -source ${{ env.NUGET_SOURCE_URL }} \ - -storepasswordincleartext \ - -name "vcpkg-cache" \ - -username "cmannett85" \ - -password "${{ secrets.GITHUB_TOKEN }}" - mono `./external/vcpkg/vcpkg fetch nuget | tail -n 1` \ - setapikey "${{ secrets.GITHUB_TOKEN }}" \ - -source ${{ env.NUGET_SOURCE_URL }} + - uses: ./.github/workflows/bootstrap_vcpkg + with: + token: "${{ secrets.GITHUB_TOKEN }}" - name: Build timeout-minutes: 30 @@ -55,16 +61,20 @@ jobs: cmake --build . --target arg_router_test - name: Run unit tests under ASan/UBSan - timeout-minutes: 30 + timeout-minutes: 45 run: | cd ${{ env.BUILD_DIR }}/test ./arg_router_test -l test_suite gcc_compiler_test: runs-on: ubuntu-22.04 + needs: changes + if: | + needs.changes.outputs.merge_checker == 'true' || + needs.changes.outputs.source_tests_and_examples == 'true' steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: submodules: true @@ -73,22 +83,9 @@ jobs: sudo apt update sudo apt install ninja-build - - name: Bootstrap vcpkg to build the NuGet client - run: | - ./external/vcpkg/bootstrap-vcpkg.sh -disableMetrics - - - name: Configure NuGet client - run: | - mono `./external/vcpkg/vcpkg fetch nuget | tail -n 1` \ - sources add \ - -source ${{ env.NUGET_SOURCE_URL }} \ - -storepasswordincleartext \ - -name "vcpkg-cache" \ - -username "cmannett85" \ - -password "${{ secrets.GITHUB_TOKEN }}" - mono `./external/vcpkg/vcpkg fetch nuget | tail -n 1` \ - setapikey "${{ secrets.GITHUB_TOKEN }}" \ - -source ${{ env.NUGET_SOURCE_URL }} + - uses: ./.github/workflows/bootstrap_vcpkg + with: + token: "${{ secrets.GITHUB_TOKEN }}" - name: Build timeout-minutes: 30 @@ -106,9 +103,13 @@ jobs: thirtytwobit_gcc_compiler_test: runs-on: ubuntu-22.04 + needs: changes + if: | + needs.changes.outputs.merge_checker == 'true' || + needs.changes.outputs.source_tests_and_examples == 'true' steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: submodules: true @@ -117,22 +118,9 @@ jobs: sudo apt update sudo apt install ninja-build g++-multilib - - name: Bootstrap vcpkg to build the NuGet client - run: | - ./external/vcpkg/bootstrap-vcpkg.sh -disableMetrics - - - name: Configure NuGet client - run: | - mono `./external/vcpkg/vcpkg fetch nuget | tail -n 1` \ - sources add \ - -source ${{ env.NUGET_SOURCE_URL }} \ - -storepasswordincleartext \ - -name "vcpkg-cache" \ - -username "cmannett85" \ - -password "${{ secrets.GITHUB_TOKEN }}" - mono `./external/vcpkg/vcpkg fetch nuget | tail -n 1` \ - setapikey "${{ secrets.GITHUB_TOKEN }}" \ - -source ${{ env.NUGET_SOURCE_URL }} + - uses: ./.github/workflows/bootstrap_vcpkg + with: + token: "${{ secrets.GITHUB_TOKEN }}" - name: Build timeout-minutes: 30 @@ -150,6 +138,10 @@ jobs: windows_clang_cl_compiler_tests: runs-on: windows-2022 + needs: changes + if: | + needs.changes.outputs.merge_checker == 'true' || + needs.changes.outputs.source_tests_and_examples == 'true' # Use the CMake that ships with VS to match locally built versions env: @@ -157,22 +149,16 @@ jobs: CMD_BUILD_DIR: ${{ github.workspace }}\\build steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: submodules: true - - name: Bootstrap vcpkg to build the NuGet client - run: | - ./external/vcpkg/bootstrap-vcpkg.bat -disableMetrics - - - name: Configure NuGet client - run: | - [array] $output = ./external/vcpkg/vcpkg.exe fetch nuget - & $output[-1] sources add -source ${{ env.NUGET_SOURCE_URL }} -storepasswordincleartext -name "vcpkg-cache" -username "cmannett85" -password "${{ secrets.GITHUB_TOKEN }}" - & $output[-1] setapikey "${{ secrets.GITHUB_TOKEN }}" -source ${{ env.NUGET_SOURCE_URL }} + - uses: ./.github/workflows/bootstrap_vcpkg + with: + token: "${{ secrets.GITHUB_TOKEN }}" - name: Build - timeout-minutes: 30 + timeout-minutes: 60 shell: cmd run: | call "C:/PROGRAM FILES/MICROSOFT VISUAL STUDIO/2022/Enterprise/VC/Auxiliary/Build/vcvarsall.bat" x64 @@ -199,20 +185,26 @@ jobs: Test-Path -Path "${{ env.INSTALL_DIR }}/share/arg_router/arg_router.cmake" -PathType Leaf Test-Path -Path "${{ env.INSTALL_DIR }}/share/arg_router/arg_router-config.cmake" -PathType Leaf Test-Path -Path "${{ env.INSTALL_DIR }}/share/arg_router/arg_router-config-version.cmake" -PathType Leaf + Test-Path -Path "${{ env.INSTALL_DIR }}/share/arg_router/translation_generator.cmake" -PathType Leaf + Test-Path -Path "${{ env.INSTALL_DIR }}/share/arg_router/translation_generator_script.cmake" -PathType Leaf - name: Test CMake package integrity run: | & "C:/PROGRAM FILES/MICROSOFT VISUAL STUDIO/2022/Enterprise/VC/Auxiliary/Build/vcvarsall.bat" x64 mkdir ${{ env.PACKAGE_BUILD_DIR }} cd ${{ env.PACKAGE_BUILD_DIR }} - & "${{ env.CMAKE_EXE }}" ${{ github.workspace }}/scripts/ci/package_test_project -G "Visual Studio 17 2022" -A x64 -T "ClangCl" -DVCPKG_TARGET_TRIPLET:STRING="x64-windows-static" -DCMAKE_CXX_COMPILER:FILEPATH="C:/PROGRAM FILES/MICROSOFT VISUAL STUDIO/2022/Enterprise/VC/Tools/Llvm/x64/bin/clang-cl.exe" -DCMAKE_C_COMPILER:FILEPATH="C:/PROGRAM FILES/MICROSOFT VISUAL STUDIO/2022/Enterprise/VC/Tools/Llvm/x64/bin/clang-cl.exe" -DCMAKE_RC_COMPILER:FILEPATH="C:/PROGRAM FILES/MICROSOFT VISUAL STUDIO/2022/Enterprise/VC/Tools/Llvm/x64/bin/llvm-rc.exe" -DCMAKE_PREFIX_PATH=${{ env.INSTALL_DIR }} + & "${{ env.CMAKE_EXE }}" ${{ github.workspace }}/ci/package_test_project -G "Visual Studio 17 2022" -A x64 -T "ClangCl" -DVCPKG_TARGET_TRIPLET:STRING="x64-windows-static" -DCMAKE_CXX_COMPILER:FILEPATH="C:/PROGRAM FILES/MICROSOFT VISUAL STUDIO/2022/Enterprise/VC/Tools/Llvm/x64/bin/clang-cl.exe" -DCMAKE_C_COMPILER:FILEPATH="C:/PROGRAM FILES/MICROSOFT VISUAL STUDIO/2022/Enterprise/VC/Tools/Llvm/x64/bin/clang-cl.exe" -DCMAKE_RC_COMPILER:FILEPATH="C:/PROGRAM FILES/MICROSOFT VISUAL STUDIO/2022/Enterprise/VC/Tools/Llvm/x64/bin/llvm-rc.exe" -DCMAKE_PREFIX_PATH=${{ env.INSTALL_DIR }} & "${{ env.CMAKE_EXE }}" --build . -- /v:n macos_compiler_tests: runs-on: macos-12 + needs: changes + if: | + needs.changes.outputs.merge_checker == 'true' || + needs.changes.outputs.source_tests_and_examples == 'true' steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: submodules: true @@ -220,22 +212,9 @@ jobs: run: | brew install clang-format - - name: Bootstrap vcpkg to build the NuGet client - run: | - ./external/vcpkg/bootstrap-vcpkg.sh -disableMetrics - - - name: Configure NuGet client - run: | - mono `./external/vcpkg/vcpkg fetch nuget | tail -n 1` \ - sources add \ - -source ${{ env.NUGET_SOURCE_URL }} \ - -storepasswordincleartext \ - -name "vcpkg-cache" \ - -username "cmannett85" \ - -password "${{ secrets.GITHUB_TOKEN }}" - mono `./external/vcpkg/vcpkg fetch nuget | tail -n 1` \ - setapikey "${{ secrets.GITHUB_TOKEN }}" \ - -source ${{ env.NUGET_SOURCE_URL }} + - uses: ./.github/workflows/bootstrap_vcpkg + with: + token: "${{ secrets.GITHUB_TOKEN }}" - name: Build timeout-minutes: 30 @@ -262,20 +241,26 @@ jobs: [[ -f "./share/arg_router/arg_router.cmake" ]] [[ -f "./share/arg_router/arg_router-config.cmake" ]] [[ -f "./share/arg_router/arg_router-config-version.cmake" ]] + [[ -f "./share/arg_router/translation_generator.cmake" ]] + [[ -f "./share/arg_router/translation_generator_script.cmake" ]] - name: Test CMake package integrity run: | mkdir ${{ env.PACKAGE_BUILD_DIR }} cd ${{ env.PACKAGE_BUILD_DIR }} - cmake ${{ github.workspace }}/scripts/ci/package_test_project -DCMAKE_PREFIX_PATH=${{ env.INSTALL_DIR }} + cmake ${{ github.workspace }}/ci/package_test_project -DCMAKE_PREFIX_PATH=${{ env.INSTALL_DIR }} cmake --build . # Test vcpkg package is acquired and accessible vcpkg_test_package: runs-on: ubuntu-22.04 + needs: changes + if: | + needs.changes.outputs.merge_checker == 'true' || + needs.changes.outputs.vcpkg_test_project == 'true' steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: submodules: true @@ -284,58 +269,77 @@ jobs: sudo apt update sudo apt install ninja-build - - name: Bootstrap vcpkg to build the NuGet client - run: | - ./external/vcpkg/bootstrap-vcpkg.sh -disableMetrics - - - name: Configure NuGet client - run: | - mono `./external/vcpkg/vcpkg fetch nuget | tail -n 1` \ - sources add \ - -source ${{ env.NUGET_SOURCE_URL }} \ - -storepasswordincleartext \ - -name "vcpkg-cache" \ - -username "cmannett85" \ - -password "${{ secrets.GITHUB_TOKEN }}" - mono `./external/vcpkg/vcpkg fetch nuget | tail -n 1` \ - setapikey "${{ secrets.GITHUB_TOKEN }}" \ - -source ${{ env.NUGET_SOURCE_URL }} + - uses: ./.github/workflows/bootstrap_vcpkg + with: + token: "${{ secrets.GITHUB_TOKEN }}" - name: Build + timeout-minutes: 10 run: | mkdir -p ${{ env.BUILD_DIR }} cd ${{ env.BUILD_DIR }} - cmake ${{ github.workspace }}/scripts/ci/vcpkg_test_project -G "Ninja" -DCMAKE_CXX_STANDARD=20 + cmake ${{ github.workspace }}/ci/vcpkg_test_project -G "Ninja" -DCMAKE_CXX_STANDARD=20 cmake --build . - # Test Conan package can be created and accessible - conan_test_package: + # Temporarily disabled until vcpkg is updated to v1.4.0 due to MSVC compiler changes (bugs?) + # rendering v1.3.0 broken + # vcpkg_test_package_win: + # runs-on: windows-2022 + # needs: changes + # if: | + # needs.changes.outputs.merge_checker == 'true' || + # needs.changes.outputs.vcpkg_test_project == 'true' + + # # Use the CMake that ships with VS to match locally built versions + # env: + # CMAKE_EXE: "C:/Program Files/Microsoft Visual Studio/2022/Enterprise/Common7/IDE/CommonExtensions/Microsoft/CMake/CMake/bin/cmake.exe" + # CMD_BUILD_DIR: ${{ github.workspace }}\\build + + # steps: + # - uses: actions/checkout@v3 + # with: + # submodules: true + + # - uses: ./.github/workflows/bootstrap_vcpkg + # with: + # token: "${{ secrets.GITHUB_TOKEN }}" + + # - name: Build + # timeout-minutes: 10 + # shell: cmd + # run: | + # call "C:/PROGRAM FILES/MICROSOFT VISUAL STUDIO/2022/Enterprise/VC/Auxiliary/Build/vcvarsall.bat" x64 + # mkdir ${{ env.CMD_BUILD_DIR }} + # cd ${{ env.CMD_BUILD_DIR }} + # "${{ env.CMAKE_EXE }}" ${{ github.workspace }}/ci/vcpkg_test_project -G "Visual Studio 17 2022" -A x64 -DVCPKG_TARGET_TRIPLET:STRING="x64-windows-static" -DCMAKE_CXX_STANDARD=20 + # "${{ env.CMAKE_EXE }}" --build . + + # Test Conan package is acquired and accessible for C++17 + conan_test_package_17: runs-on: ubuntu-22.04 + needs: changes + if: | + needs.changes.outputs.merge_checker == 'true' || + needs.changes.outputs.conan_test_project == 'true' steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 + - uses: ./.github/workflows/conan_setup with: - submodules: true - - - name: Setup Python - uses: actions/setup-python@v4 - with: - python-version: '3.x' - - - name: Update packages - run: | - sudo apt update - sudo apt install ninja-build - python -m pip install --upgrade pip conan + version: "17" + build_dir: "${{ env.BUILD_DIR }}" - - name: Build Conan package - run: | - conan profile detect --force - conan create ${{ github.workspace }} + # Test Conan package is acquired and accessible for C++20 + conan_test_package_20: + runs-on: ubuntu-22.04 + needs: changes + if: | + needs.changes.outputs.merge_checker == 'true' || + needs.changes.outputs.conan_test_project == 'true' - - name: Build - run: | - conan install ${{ github.workspace }}/scripts/ci/conan_test_project --output-folder=${{ env.BUILD_DIR }} --build=missing - cd ${{ env.BUILD_DIR }} - cmake ${{ github.workspace }}/scripts/ci/conan_test_project/ -G "Ninja" -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake -DCMAKE_BUILD_TYPE=Release - cmake --build . + steps: + - uses: actions/checkout@v3 + - uses: ./.github/workflows/conan_setup + with: + version: "20" + build_dir: "${{ env.BUILD_DIR }}" diff --git a/.github/workflows/pr_checker.yml b/.github/workflows/pr_checker.yml index d255fbca..fb9cf7dc 100644 --- a/.github/workflows/pr_checker.yml +++ b/.github/workflows/pr_checker.yml @@ -7,8 +7,6 @@ on: env: SKIP_COVERAGE_UPDATE: 0 - NUGET_SOURCE_URL: "https://nuget.pkg.github.com/cmannett85/index.json" - VCPKG_BINARY_SOURCES: 'clear;nuget,vcpkg-cache,readwrite' BUILD_DIR: ${{ github.workspace }}/build INSTALL_DIR: ${{ github.workspace }}/install PACKAGE_BUILD_DIR: ${{ github.workspace }}/package_build @@ -25,13 +23,33 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # Skip jobs based on what files have changed + changes: + runs-on: ubuntu-latest + needs: purge_old_packages + permissions: + pull-requests: read + outputs: + pr_checker: ${{ steps.filter.outputs.pr_checker }} + source_tests_and_examples: ${{ steps.filter.outputs.source_tests_and_examples }} + docs: ${{ steps.filter.outputs.docs }} + steps: + - uses: actions/checkout@v3 + - uses: dorny/paths-filter@v2 + id: filter + with: + filters: .github/filters.yml + # Builds the unit tests and executes them unit_test_coverage: runs-on: ubuntu-22.04 - needs: purge_old_packages + needs: changes + if: | + needs.changes.outputs.pr_checker == 'true' || + needs.changes.outputs.source_tests_and_examples == 'true' steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: submodules: true fetch-depth: 2 @@ -48,22 +66,9 @@ jobs: sudo apt update sudo apt install valgrind lcov ninja-build - - name: Bootstrap vcpkg to build the NuGet client - run: | - ./external/vcpkg/bootstrap-vcpkg.sh -disableMetrics - - - name: Configure NuGet client - run: | - mono `./external/vcpkg/vcpkg fetch nuget | tail -n 1` \ - sources add \ - -source ${{ env.NUGET_SOURCE_URL }} \ - -storepasswordincleartext \ - -name "vcpkg-cache" \ - -username "cmannett85" \ - -password "${{ secrets.GITHUB_TOKEN }}" - mono `./external/vcpkg/vcpkg fetch nuget | tail -n 1` \ - setapikey "${{ secrets.GITHUB_TOKEN }}" \ - -source ${{ env.NUGET_SOURCE_URL }} + - uses: ./.github/workflows/bootstrap_vcpkg + with: + token: "${{ secrets.GITHUB_TOKEN }}" - name: Build timeout-minutes: 30 @@ -76,14 +81,14 @@ jobs: # We run the tests under Valgrind rather than ASan/UBSan because when combined with coverage # causes hangs in the death tests - name: Run unit tests under Valgrind and generate coverage data - timeout-minutes: 30 + timeout-minutes: 45 run: | cd ${{ env.BUILD_DIR }}/test valgrind --error-exitcode=1 --leak-check=full ./arg_router_test_coverage -l test_suite - name: Calculate unit test coverage run: | - cd ${{ github.workspace }}/scripts/ci + cd ${{ github.workspace }}/ci ./calculate_test_coverage.sh ${{ env.BUILD_DIR }} - name: Test install @@ -97,31 +102,37 @@ jobs: [[ -f "./share/arg_router/arg_router.cmake" ]] [[ -f "./share/arg_router/arg_router-config.cmake" ]] [[ -f "./share/arg_router/arg_router-config-version.cmake" ]] + [[ -f "./share/arg_router/translation_generator.cmake" ]] + [[ -f "./share/arg_router/translation_generator_script.cmake" ]] - name: Test CMake package integrity run: | mkdir -p ${{ env.PACKAGE_BUILD_DIR }} cd ${{ env.PACKAGE_BUILD_DIR }} - cmake ${{ github.workspace }}/scripts/ci/package_test_project -DCMAKE_CXX_COMPILER=clang++-14 -DCMAKE_PREFIX_PATH=${{ env.INSTALL_DIR }} + cmake ${{ github.workspace }}/ci/package_test_project -DCMAKE_CXX_COMPILER=clang++-14 -DCMAKE_PREFIX_PATH=${{ env.INSTALL_DIR }} cmake --build . - name: Update README unit test coverage badge run: | cd ${{ github.workspace }} - BADGE_URL=$(./scripts/ci/create_badge_url.sh -e) + BADGE_URL=$(./ci/create_badge_url.sh -e) sed -i -e "s/https:\/\/img\.shields\.io\/badge\/Unit_Test_Coverage-[0-9\.]\+%25-[a-z]\+/${BADGE_URL}/" ./README.md - name: Push updated README and old_coverage uses: stefanzweifel/git-auto-commit-action@v4 with: commit_message: Updating README.md unit test coverage badge - file_pattern: README.md scripts/ci/old_coverage + file_pattern: README.md ci/old_coverage old_gcc_compiler_test: runs-on: ubuntu-22.04 + needs: changes + if: | + needs.changes.outputs.pr_checker == 'true' || + needs.changes.outputs.source_tests_and_examples == 'true' steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: submodules: true @@ -130,22 +141,9 @@ jobs: sudo apt update sudo apt install ninja-build - - name: Bootstrap vcpkg to build the NuGet client - run: | - ./external/vcpkg/bootstrap-vcpkg.sh -disableMetrics - - - name: Configure NuGet client - run: | - mono `./external/vcpkg/vcpkg fetch nuget | tail -n 1` \ - sources add \ - -source ${{ env.NUGET_SOURCE_URL }} \ - -storepasswordincleartext \ - -name "vcpkg-cache" \ - -username "cmannett85" \ - -password "${{ secrets.GITHUB_TOKEN }}" - mono `./external/vcpkg/vcpkg fetch nuget | tail -n 1` \ - setapikey "${{ secrets.GITHUB_TOKEN }}" \ - -source ${{ env.NUGET_SOURCE_URL }} + - uses: ./.github/workflows/bootstrap_vcpkg + with: + token: "${{ secrets.GITHUB_TOKEN }}" - name: Build timeout-minutes: 30 @@ -162,51 +160,53 @@ jobs: ./arg_router_test -l test_suite windows_msvc_compiler_tests: - runs-on: windows-2022 - needs: purge_old_packages - - # Use the CMake that ships with VS to match locally built versions - env: - CMAKE_EXE: "C:/Program Files/Microsoft Visual Studio/2022/Enterprise/Common7/IDE/CommonExtensions/Microsoft/CMake/CMake/bin/cmake.exe" - CMD_BUILD_DIR: ${{ github.workspace }}\\build - - steps: - - uses: actions/checkout@v2 - with: - submodules: true - - - name: Bootstrap vcpkg to build the NuGet client - run: | - ./external/vcpkg/bootstrap-vcpkg.bat -disableMetrics - - - name: Configure NuGet client - run: | - [array] $output = ./external/vcpkg/vcpkg.exe fetch nuget - & $output[-1] sources add -source ${{ env.NUGET_SOURCE_URL }} -storepasswordincleartext -name "vcpkg-cache" -username "cmannett85" -password "${{ secrets.GITHUB_TOKEN }}" - & $output[-1] setapikey "${{ secrets.GITHUB_TOKEN }}" -source ${{ env.NUGET_SOURCE_URL }} - - - name: Build - timeout-minutes: 30 - shell: cmd - run: | - call "C:/PROGRAM FILES/MICROSOFT VISUAL STUDIO/2022/Enterprise/VC/Auxiliary/Build/vcvarsall.bat" x64 - mkdir ${{ env.CMD_BUILD_DIR }} - cd ${{ env.CMD_BUILD_DIR }} - "${{ env.CMAKE_EXE }}" ${{ github.workspace }} -G "Visual Studio 17 2022" -DVCPKG_TARGET_TRIPLET:STRING="x64-windows-static" -DDEATH_TEST_PARALLEL=1 - "${{ env.CMAKE_EXE }}" --build . --target arg_router_test cpp17_examples cpp20_examples - - - name: Run unit tests - timeout-minutes: 20 - run: | - cd ${{ env.BUILD_DIR }}/test/Debug - ./arg_router_test.exe -l test_suite + runs-on: windows-2022 + needs: changes + if: | + needs.changes.outputs.pr_checker == 'true' || + needs.changes.outputs.source == 'true' || + needs.changes.outputs.source_tests_and_examples == 'true' + + # Use the CMake that ships with VS to match locally built versions + env: + CMAKE_EXE: "C:/Program Files/Microsoft Visual Studio/2022/Enterprise/Common7/IDE/CommonExtensions/Microsoft/CMake/CMake/bin/cmake.exe" + CMD_BUILD_DIR: ${{ github.workspace }}\\build + + steps: + - uses: actions/checkout@v3 + with: + submodules: true + + - uses: ./.github/workflows/bootstrap_vcpkg + with: + token: "${{ secrets.GITHUB_TOKEN }}" + + - name: Build + timeout-minutes: 60 + shell: cmd + run: | + call "C:/PROGRAM FILES/MICROSOFT VISUAL STUDIO/2022/Enterprise/VC/Auxiliary/Build/vcvarsall.bat" x64 + mkdir ${{ env.CMD_BUILD_DIR }} + cd ${{ env.CMD_BUILD_DIR }} + "${{ env.CMAKE_EXE }}" ${{ github.workspace }} -G "Visual Studio 17 2022" -A x64 -DVCPKG_TARGET_TRIPLET:STRING="x64-windows-static" -DDEATH_TEST_PARALLEL=1 + "${{ env.CMAKE_EXE }}" --build . --target arg_router_test cpp20_examples + + - name: Run unit tests + timeout-minutes: 30 + run: | + cd ${{ env.BUILD_DIR }}/test/Debug + ./arg_router_test.exe -l test_suite # Test package integrity test where we use system libs and C++20 (so no nonstd::span) system_test_package: runs-on: ubuntu-22.04 + needs: changes + if: | + needs.changes.outputs.pr_checker == 'true' || + needs.changes.outputs.source_tests_and_examples == 'true' steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: submodules: true @@ -235,13 +235,15 @@ jobs: [[ -f "/usr/share/arg_router/arg_router.cmake" ]] [[ -f "/usr/share/arg_router/arg_router-config.cmake" ]] [[ -f "/usr/share/arg_router/arg_router-config-version.cmake" ]] + [[ -f "/usr/share/arg_router/translation_generator.cmake" ]] + [[ -f "/usr/share/arg_router/translation_generator_script.cmake" ]] - name: Test CMake package integrity timeout-minutes: 30 run: | mkdir ${{ env.PACKAGE_BUILD_DIR }} cd ${{ env.PACKAGE_BUILD_DIR }} - cmake ${{ github.workspace }}/scripts/ci/package_test_project -DCMAKE_CXX_STANDARD=20 -DDISABLE_VCPKG=ON + cmake ${{ github.workspace }}/ci/package_test_project -DCMAKE_CXX_STANDARD=20 -DDISABLE_VCPKG=ON cmake --build . sudo apt remove arg_router ! test -e /usr/include/arg_router @@ -250,9 +252,14 @@ jobs: # Makes sure there are no errors when generating the documentation documentation: runs-on: ubuntu-latest + needs: changes + if: | + needs.changes.outputs.pr_checker == 'true' || + needs.changes.outputs.source_tests_and_examples == 'true' || + needs.changes.outputs.docs == 'true' steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: submodules: true diff --git a/.github/workflows/release_builder.yml b/.github/workflows/release_builder.yml index f97d2ca7..917dcd05 100644 --- a/.github/workflows/release_builder.yml +++ b/.github/workflows/release_builder.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: fetch-depth: 1 @@ -55,32 +55,14 @@ jobs: - name: Create GitHub release entry id: create_release - uses: actions/create-release@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + uses: softprops/action-gh-release@v0.1.15 with: tag_name: ${{ env.TAG_VERSION }} - release_name: arg_router ${{ env.TAG_VERSION }} + name: arg_router ${{ env.TAG_VERSION }} + body: ${{ steps.commit_msg.outputs.commit_msg }} draft: false prerelease: false - body: ${{ steps.commit_msg.outputs.commit_msg }} - - - name: Upload zip - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{steps.create_release.outputs.upload_url}} - asset_path: ${{ env.BUILD_DIR }}/${{ steps.version_check.outputs.filename }}.zip - asset_name: ${{ steps.version_check.outputs.filename }}.zip - asset_content_type: application/zip - - - name: Upload deb - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{steps.create_release.outputs.upload_url}} - asset_path: ${{ env.BUILD_DIR }}/${{ steps.version_check.outputs.filename }}.deb - asset_name: ${{ steps.version_check.outputs.filename }}.deb - asset_content_type: application/vnd.debian.binary-package + fail_on_unmatched_files: true + files: | + ${{ env.BUILD_DIR }}/${{ steps.version_check.outputs.filename }}.zip + ${{ env.BUILD_DIR }}/${{ steps.version_check.outputs.filename }}.deb diff --git a/CMakeLists.txt b/CMakeLists.txt index 200b8b76..2828e649 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,7 +26,7 @@ if((NOT INSTALLATION_ONLY) AND (NOT DISABLE_VCPKG)) endif() project(arg_router - VERSION 1.3.0 + VERSION 1.4.0 DESCRIPTION "C++ command line argument parsing and routing" HOMEPAGE_URL "https://github.com/cmannett85/arg_router" LANGUAGES CXX) @@ -68,11 +68,14 @@ path_prefixer(HEADERS include/arg_router/dependency/one_of.hpp include/arg_router/exception.hpp include/arg_router/flag.hpp + include/arg_router/forwarding_arg.hpp include/arg_router/help.hpp include/arg_router/list.hpp include/arg_router/literals.hpp include/arg_router/math.hpp include/arg_router/mode.hpp + include/arg_router/multi_arg.hpp + include/arg_router/multi_arg_base.hpp include/arg_router/multi_lang/iso_locale.hpp include/arg_router/multi_lang/root_wrapper.hpp include/arg_router/multi_lang/string_selector.hpp @@ -83,6 +86,7 @@ path_prefixer(HEADERS include/arg_router/parsing/parsing.hpp include/arg_router/parsing/pre_parse_data.hpp include/arg_router/parsing/token_type.hpp + include/arg_router/parsing/unknown_argument_handling.hpp include/arg_router/policy/alias.hpp include/arg_router/policy/colour_help_formatter.hpp include/arg_router/policy/custom_parser.hpp @@ -107,8 +111,10 @@ path_prefixer(HEADERS include/arg_router/policy/program_version.hpp include/arg_router/policy/required.hpp include/arg_router/policy/router.hpp + include/arg_router/policy/runtime_enable.hpp include/arg_router/policy/short_form_expander.hpp include/arg_router/policy/short_name.hpp + include/arg_router/policy/token_end_marker.hpp include/arg_router/policy/validator.hpp include/arg_router/policy/validator_rule_utilities.hpp include/arg_router/policy/value_separator.hpp @@ -119,6 +125,8 @@ path_prefixer(HEADERS include/arg_router/tree_node_fwd.hpp include/arg_router/utility/compile_time_string.hpp include/arg_router/utility/compile_time_optional.hpp + include/arg_router/utility/dynamic_string_view.hpp + include/arg_router/utility/exception_formatter.hpp include/arg_router/utility/result.hpp include/arg_router/utility/string_to_policy.hpp include/arg_router/utility/string_view_ops.hpp @@ -131,9 +139,11 @@ path_prefixer(HEADERS include/arg_router/utility/utf8/code_point.hpp include/arg_router/utility/utf8/double_width.hpp include/arg_router/utility/utf8/grapheme_cluster_break.hpp + include/arg_router/utility/utf8/levenshtein_distance.hpp include/arg_router/utility/utf8/line_break.hpp include/arg_router/utility/utf8/whitespace.hpp include/arg_router/utility/utf8/zero_width.hpp + include/arg_router/utility/win_api.hpp include/arg_router/version.hpp ) diff --git a/README.md b/README.md index f18a65ab..629e3e17 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -![Documentation Generator](https://github.com/cmannett85/arg_router/workflows/Documentation%20Generator/badge.svg) [![Merge to main Checker](https://github.com/cmannett85/arg_router/actions/workflows/merge_checker.yml/badge.svg)](https://github.com/cmannett85/arg_router/actions/workflows/merge_checker.yml) ![Unit test coverage](https://img.shields.io/badge/Unit_Test_Coverage-97.5%25-brightgreen) +![Documentation Generator](https://github.com/cmannett85/arg_router/workflows/Documentation%20Generator/badge.svg) [![Merge to main Checker](https://github.com/cmannett85/arg_router/actions/workflows/merge_checker.yml/badge.svg)](https://github.com/cmannett85/arg_router/actions/workflows/merge_checker.yml) ![Unit test coverage](https://img.shields.io/badge/Unit_Test_Coverage-97.2%25-brightgreen) # arg_router `arg_router` is a C++17/20 command line parser and router. It uses policy-based objects hierarchically, so the parsing code is self-describing. Rather than just providing a parsing service that returns a map of `variant`s/`any`s, it allows you to bind `Callable` instances to points in the parse structure, so complex command line arguments can directly call functions with the expected arguments - rather than you having to do this yourself. @@ -13,7 +13,8 @@ - Unicode compliant by supporting UTF-8 encoded compile-time strings ([details](#unicode-compliance)) - Support of runtime language selection - Uses a macro to ease compile-time string generation when using C++17. For C++20 and above, compile-time string literals can be used directly in constructors -- Available on vcpkg! +- [Available](https://github.com/microsoft/vcpkg/tree/master/ports/arg-router) on vcpkg! +- [Available](https://conan.io/center/arg_router) on Conan Center! ### Example of the Benefits of a Compile-Time Parse Tree It's not immediately obvious why defining a parse tree at compile would bring any benefits, so before we show you _how_ `arg_router` is used, let us show you _why_. Here is a very contrived parse tree defined using the very popular and well-made [argparse](https://github.com/p-ranav/argparse): @@ -81,9 +82,10 @@ arg_router/policy/validator.hpp:206:17: error: static_assert failed due to requi ## Installation There are several ways to install `arg_router`, the most appropriate depends on your project's configuration. * If using vcpkg as your package manager, simply add `arg-router` (note the hyphen) to `dependencies` in your `vcpkg.json` +* If using Conan as your package manager, simply add `arg_router` to your requirements * If using a DEB package based Linux distribution, download the [release](https://github.com/cmannett85/arg_router/releases) Debian package and install it * If you just want to do a traditional install then download the [release](https://github.com/cmannett85/arg_router/releases) zip file and decompress it where you want. Then point your project at the `include/arg_router` directory or package config location `share/arg_router` -* Same as above but installed from the checked out repo, i.e. using the invocation `cmake ../arg_router -DINSTALLATION_ONLY=ON; cmake --install .` +* Same as above but installed from the checked out repo, i.e. using the invocation `cmake ../arg_router -DINSTALLATION_ONLY=ON; cmake --install .` (see [developer installation](https://github.com/cmannett85/arg_router/wiki/Developer-Installation)) ### Dependencies If you're a library user, you will need the following dependencies in order to build: @@ -96,25 +98,6 @@ If you're a vcpkg user, then these will be brought in automatically for you. `a **Note** currently `arg_router` requires exception support, but _not_ RTTI. -To get a pre-release `arg_router`, or build the unit tests and examples, simply check out the repo and build via CMake in the usual way - the unit tests will be built by default: -``` -$ cd arg_router -$ mkdir build -$ cd ./build -$ cmake .. -$ cmake --build . -$ cmake --install . -``` -Building these targets will require more dependencies: -* clang-format -* Python v3 (used for copyright checking) -* Doxygen -* Boost.Test v1.74+ -* Boost.Filesystem v1.74+ -* Boost.Process v1.74+ - -By default all these dependencies are provided by `vcpkg` automatically, please **note** that `vcpkg` is provided via a submodule and therefore will need initialising (`git submodule update`). If you would rather the dependencies came from the system then simply set `-DDISABLE_VCPKG=OFF`, and CMake will not bootstrap `vcpkg` and therefore try to find the packages locally. - ## Basics Let's start simple, with this `cat`-like program: ```cpp @@ -171,7 +154,7 @@ The `arp::validation::default_validator` instance provides the default validator The `help` node is used by the `root` to generate the argument documentation for the help output, by default it just prints directly to the console and then exits, but a `router` can be attached that accepts the formatted output to do something else with it. The optional `program_name_t` and `program_version_t` policies add a header to the help output. -Now let's introduce some 'policies'. Policies define common behaviours across node types, a basic one is `long_name_t` which provides long-form argument definition. By default, a standard unix double hyphen prefix for long names is added automatically. Having the name defined at compile-time means we detect duplicate names and fail the build - one less unit test you have to worry about. `short_name_t` is the single character short-form name, by default a single hyphen is prefixed automatically. `arg_router` supports short name collapsing for flags, so if you have defined flags like `-a -b -c` then `-abc` will be accepted or `-bca`, etc. (**note** short-form name collapsing is disabled if the library has been configured to have the same long and short prefix). +Now let's introduce some 'policies'. Policies define common behaviours across node types, a basic one is `long_name_t` which provides long-form argument definition. By default, a standard unix double hyphen prefix for long names is added automatically. `short_name_t` is the single character short-form name, by default a single hyphen is prefixed automatically. `arg_router` supports short name collapsing for flags, so if you have defined flags like `-a -b -c` then `-abc` will be accepted or `-bca`, etc. (**note** short-form name collapsing is disabled if the library has been configured to have the same long and short prefix, which is common on Windows). Compile-time strings are created via the `""_S` string literal, which creates an instance of a `ar::str` type. **Note** Advanced NTTP language support that allows for this is not present until C++20, see [compile-time string support](#compile-time-string-support) for what to do in C++17. @@ -192,10 +175,10 @@ Assuming parsing was successful, the final `router` is called with the parsed ar You may have noticed that the nodes are constructed with parentheses whilst the policies use braces, this is necessary due to CTAD rules that affect nodes which return a user-defined value type. This can be circumvented using a function to return the required instance, for example the actual type of a flag is `flag_t`, `flag(...)` is a factory function. ### Implicit String Policies -Although explicit, which may make it easier to read, the name policies are also verbose. To ease this most of the built-in nodes support implicit string-to-policy mapping, which allows bare compile-time strings to be passed to the node factory functions which are then mapped to appropriate built-in policies. The rules vary from node to node due, but typically they are: +Although explicit, which may make it easier to read, the name policies are also verbose. To ease this most of the built-in nodes support implicit string-to-policy mapping, which allows bare compile-time strings to be passed to the node factory functions which are then mapped to appropriate built-in policies. The rules vary from node to node, but typically they are: 1. The first multi-character string becomes a `policy::long_name_t` 2. The second multi-character string becomes a `policy::description_t` -3. The first single-charcter string becomes a `policy::short_name_t` +3. The first single-character string becomes a `policy::short_name_t` The above are unicode aware. The strings can be passed in any order relative to the other policies, but it is recommended to put them first to ease reading. Re-writing the `cat`-like program using implicit strings shortens it considerably: @@ -343,7 +326,7 @@ Note that even though we are using a custom enum, we haven't specified a `custom Short name collapsing still works as expected, so passing `-Evnv` will result in `show_ends` and `show_non_printing` being true, and `verbosity_level` will be `verbosity_level_t::INFO` in the `router` call. -We can constrain the amount of flags the user can provide by using the `max_value` policy, so passing `-vvvv` will result in a runtime error. There are min/max and min variants too. Here we are using the compile-time variant of the policy, we can do that because the value type is an integral/enum, but if your value type cannot be used as a template parameter then there equivalent runtime variants that take the paramters as function parameters instead. The compile-time variants should be used when possible due to extra checks e.g. setting the max value less than the min. +We can constrain the amount of flags the user can provide by using the `max_value` policy, so passing `-vvvv` will result in a runtime error. There are min/max and min variants too. Here we are using the compile-time variant of the policy, we can do that because the value type is an integral/enum, but if your value type cannot be used as a template parameter then there equivalent runtime variants that take the parameters as function parameters instead. The compile-time variants should be used when possible due to extra checks e.g. setting the max value less than the min. The `long_name_t` policy is allowed but usually leads to ugly invocations, however there is a better option: ```cpp @@ -411,12 +394,47 @@ It was noted in the [Basics](#basics) section that ordering does matter for posi Following the destination path are the source paths, we need at least one so we mark it as required, as our range is unbounded the `value_type` needs to be a container. `positional_arg` uses a `push_back` call on the container, so `std::vector` is the typical type to use. -Only the last `positional_arg` may be of variable length. A runtime error will only occur if there are no unbounded variable length `postional_arg`s and there are more arguments than the maximum or less than the minimum. +Only the last `positional_arg` may be of variable length - unless a `token_end_maker` policy is used. A runtime error will only occur if there are no unbounded variable length `postional_arg`s and there are more arguments than the maximum or less than the minimum. It should be noted that setting a non-zero minimum count (`min_count`, `fixed_count`, or `min_max_count`) does _not_ imply a requirement, the minimum count check only applies when there is at least one argument for the node to process. So as with an `arg`, you should use a `required` policy to explicitly state that at least one argument needs to be present, or a `default_value` policy - otherwise a default initialised value will be used instead. For `positional_arg` nodes that are marked as `required`, it is a compile-time error to have a minimum count policy value of 0. +### Token End Marker Policy +The `token_end_maker` policy allows for multiple adjacent variable length `positional_arg` nodes to be defined. It does this by defining a token that marks the end of the value token list for that node. A trivial example could be a simple launcher application (which is available as a [buildable example](https://cmannett85.github.io/arg_router/c_09_0920_2launcher_2main_8cpp-example.html)) e.g.: +``` +$ example_launcher_cpp prog1 prog2 prog3 -- arg1 arg2 arg3 +``` +Where the argument tokens are used invoke the programs as child processes. Here the `--` token is used to separate the two adjacent `positional_args`: +``` +ar::positional_arg>(arp::required, + "PROGS"_S, + "Programs to run"_S, + arp::token_end_marker_t{"--"_S}, + arp::min_count<1>), +ar::positional_arg>("ARGS"_S, + "Arguments to pass to programs"_S), +``` +There is no limit to number of `positional_arg` nodes that can be chained together like this, but they must still follow `positional_arg` rules such as being at the end of the child node list. + +### Multi-Arg and Forwarding Arg +Another variation of multi-value arguments are `multi_arg` and `forwarding_arg`. `multi_arg` is almost the same as `arg` but accepts multiple (at least one) value tokens from the command line, you can tune the min/max value token count using the `min_max_count_t` policy - although you can't use a `value_separator` policy. + +`forwarding_arg` is similar to `multi_arg` except that it is tuned for simply forwarding arguments, so has no naming restrictions, no min/max count, and is has a fixed `value_type` of `vector`. You could re-write the launcher example above to use it: +``` +ar::forwarding_arg(arp::required, + "--"_S, + "Programs to run"_S, + arp::token_end_marker_t{"--"_S}, + arp::min_count<1>), +ar::positional_arg>("ARGS"_S, + "Arguments to pass to programs"_S), +``` +Which would look like this on the command line: +``` +$ example_launcher_cpp -f -- prog1 prog2 prog3 -- arg1 arg2 arg3 +``` + ## Modes -As noted in [Basics](#basics), `mode`s allow you to group command line components under an initial token on the command line. A common example of this developers will be aware of is `git`, for example in our parlance `git clean -ffxd`; `clean` would be the mode and `ffxd` would be be the flags that are available under that mode. +As noted in [Basics](#basics), `mode`s allow you to group command line components under an initial token on the command line. A common example of this developers will be aware of is `git`, for example in our parlance `git clean -ffxd`; `clean` would be the mode and `ffxd` would be the flags that are available under that mode. As an example, let's take the `simple-copy` above and split it into two modes: ``` @@ -487,6 +505,69 @@ ar::root( ``` `ar::list` is a simple `arg` and `flag` container that `mode` and `root` instances detect and add the contents to their child/policy lists. Also don't be afraid of the copies, the majority of `arg_router` types hold no data (the advantage of compile-time!) and those that do (e.g. `default_value`) generally have small types like primitives or `std::string_view`. +## Enabling/Disabling Nodes at Runtime +Sometimes features or parameters only make sense within certain environments or scenarios that can only be detected at runtime. You can use `policy::runtime_enable` to dynamically make a node 'disappear' from the parsing process and help output by the value set at runtime in the policy's constructor. A trivial example is given by the [runtime_node_enable example](https://cmannett85.github.io/arg_router/c_09_0920_2runtime_node_enable_2main_8cpp-example.html): +``` +const auto advanced = std::getenv(license_env_var) != nullptr; +ar::root( + arp::validation::default_validator, + ar::help("help"_S, + "h"_S, + "Display this help and exit"_S, + arp::flatten_help, + arp::program_name_t{"runtime_node_enable"_S}, + arp::program_version>, + arp::program_addendum_t{"An example program for arg_router."_S}), + ar::flag("version"_S, "Output version information and exit"_S, arp::router{[](bool) { ... }}), + ar::mode("advanced"_S, + "Advanced features"_S, + ar::flag("feature1"_S, "First feature"_S), + ar::arg("feature2"_S, "Second feature"_S, arp::default_value{42}), + arp::router{[](bool f1, int f2) { ... }}, + arp::runtime_enable{advanced}), + ar::mode( + ar::flag("foo"_S, "Foo flag"_S, "f"_S), + ar::flag("bar"_S, "Bar flag"_S, "b"_S), + ar::arg("advance-foo"_S, + "Licensed foo"_S, + arp::runtime_enable_required{advanced}), + arp::router{[](bool f, bool b, std::string_view advance_foo) { ... }})) + .parse(argc, argv); +``` +Here an environment variable is used to detect if a license is installed (do not use this method in production...) and if it +is, unlocks 'advanced' features. If the license is available then the entire `advanced` mode is available and so is the `--advance-foo` `std::string_view` `arg` in the default mode. + +The help output adjusts to match: +``` +$ ./example_runtime_node_enable_cpp20 --help +runtime_node_enable v3.14 + + --help,-h Display this help and exit + --version Output version information and exit + + --foo,-f Foo flag + --bar,-b Bar flag + +An example program for arg_router. +``` +And with the license: +``` +$ AR_EXAMPLE_LICENSE=1 ./example_runtime_node_enable_cpp20 --help +runtime_node_enable v3.14 + + --help,-h Display this help and exit + --version Output version information and exit + advanced Advanced features + --feature1 First feature + --feature2 Second feature + + --foo,-f Foo flag + --bar,-b Bar flag + --advance-foo Licensed foo + +An example program for arg_router. +``` + ## Help Output As shown in prior sections, a `help` node can be a child of the `root` (and only the `root`!), which acts like an `arg`, and generates the help output when requested by the user. This node is optional, without it there is no help command line argument. As the node is `arg`-like, it requires a long and/or short name. @@ -581,7 +662,7 @@ copy Copy source files to destination An example program for arg_router. ``` ### Programmatic Access -By default when parsed, `help` will output its contents to `std::cout` and then exit the application with `EXIT_SUCCESS`. Obviously this won't always be desired, so a `router` policy can be attached that will pass a `std::ostringstream` to the user-provided `Callable`. The stream will have already been populated with the help data shown above, but it can now be appended to or converted to string for use somewhere else. +By default when parsed, `help` will output its contents to `std::cout` and then exit the application with `EXIT_SUCCESS`. Obviously this won't always be desired, so a `router` policy can be attached that will pass a `std::ostringstream` to the user-provided `Callable`. The stream will have already been populated with the help data shown above, but it can now be appended to or converted to a string for use somewhere else. Often programmatic access is desired for the help output outside of the user requesting it, for example if a parse exception is thrown, generally the exception error is printed to the terminal followed by the help output. This is exposed by the `help()` or `help(std::ostringstream&)` methods of the root object. @@ -822,15 +903,22 @@ public: str<"最大値を超えました">>, std::pair, str<"最小数に達していません">>, + std::pair, + str<"最大数を超えました">>, + std::pair, + str<"不明な引数 {}。 { } という意味でしたか?">>, std::pair, str<"モードには引数が必要です">>, std::pair, str<"必要な引数がありません">>, std::pair, str<"エイリアス値が少なすぎる">>, - std::pair< - traits::integral_constant, - str<"従属引数がありません (コマンドラインで必要なトークンの前に置く必要があります)">>>; + std::pair, + str<"従属引数がありません (コマンドラインで必要なトークンの前に置く必要があります)">>, + std::pair, + str<"「One Of」から一度に使用できる引数は 1 つだけです">>, + std::pair, + str<"値の区切り文字が必要です">>>; }; ``` Could yield: @@ -840,7 +928,62 @@ terminate called after throwing an instance of 'arg_router::parse_exception' what(): 不明な引数: -🐱 ``` -### Note ### +## Translation Generation +An annoyance of the above is that the translation types are verbose and difficult to read, so as of v1.4 a CMake function is provided by the package config that allows translation type headers to be generated from [TOML](https://en.wikipedia.org/wiki/TOML). + +For example the Japanese translation above looks like this: +``` +# Comments are supported, but only if the # character is the first on the line +force = "強制" +force_description = "既存のファイルを強制的に上書きする" +destination = "先" +destination_description = "宛先ディレクトリ" +help = "ヘルプ" +help_description = "このヘルプを表示して終了" +program_intro = "ファイルをコピーおよび移動するためのシンプルなプログラム。" +program_addendum = "「arg_router」のサンプルプログラム。" +copy = "コピー" +copy_description = "ソース ファイルを宛先にコピーする" +source = "出典" +sources_description = "ソース ファイルのパス" +move = "移動" +move_description = "ソース ファイルを宛先に移動する" +source_description = "ソース ファイル パス" + +[error_code] +unknown_argument = "不明な引数" +unhandled_arguments = "未処理の引数" +argument_has_already_been_set = "引数はすでに設定されています" +failed_to_parse = "解析に失敗しました" +no_arguments_passed = "引数が渡されませんでした" +minimum_value_not_reached = "最小値に達していません" +maximum_value_exceeded = "最大値を超えました" +minimum_count_not_reached = "最小数に達していません" +maximum_count_exceeded = "最大数を超えました" +unknown_argument_with_suggestion = "不明な引数 {}。 { } という意味でしたか?" +mode_requires_arguments = "モードには引数が必要です" +missing_required_argument = "必要な引数がありません" +too_few_values_for_alias = "エイリアス値が少なすぎる" +dependent_argument_missing = "従属引数がありません (コマンドラインで必要なトークンの前に置く必要があります)" +one_of_selected_type_mismatch = "「One Of」から一度に使用できる引数は 1 つだけです" +missing_value_separator = "値の区切り文字が必要です" +``` +The TOML file name is the language ID. Calling the CMake function like this: +``` +arg_router_translation_generator( + SOURCES "${CMAKE_SOURCE_DIR}/en_GB.toml" + "${CMAKE_SOURCE_DIR}/fr.toml" + "${CMAKE_SOURCE_DIR}/ja.toml" + OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/translations/" + TARGET my_exe_translations +) +add_executable(my_exe ...) +add_dependencies(my_exe my_exe_translations) +target_include_directories(my_exe PRIVATE "${CMAKE_CURRENT_BINARY_DIR}") +``` +Creates a header for each language with the corresponding translation specialisation that can be included in your root source file. You can see this in the [buildable example](https://cmannett85.github.io/arg_router/c_09_0920_2simple_ml_gen_2main_8cpp-example.html)). This is now the recommended approach for writing translations, but is certainly not (and never will be) a requirement. + +### Note `multi_lang::root_wrapper` from v1.0 is still present and supported, but is now marked as deprecated - new code should use `multi_lang::root`. It is not supported when using C++20 compile-time strings (see [compile-time string support](#compile-time-string-support)). ## Compile-time String Support @@ -851,7 +994,7 @@ For those targetting C++20 with existing v1.0 code, upgrading to a newer library **Note** C++17 will be supported as a first class citizen until v2.0, after that C++20 will be the minimum so I can strip out a ton of code and get better diagnostics by using concepts. ## Error Handling -Currently `arg_router` only supports exceptions as error handling. If a parsing fails for some reason a `arg_router::parse_exception` is thrown carrying information on the failure. +Currently `arg_router` only supports exceptions as error handling. If parsing fails for some reason a `arg_router::parse_exception` is thrown carrying information on the failure. ## Configuration Low-level tweaking of the library is achieved via some defines and/or CMake variables, documented [here](https://cmannett85.github.io/arg_router/configuration.html). @@ -862,7 +1005,7 @@ The CI system attached to this repo builds the unit tests and examples with: * Windows Server 2022 (Ninja, MSBuild), Clang 14.0.5, MSVC 19.34(C++20 only) * MacOS 12 (Ninja), Clang 14 -You can build on Windows with using MSBuild but you must set the CMake variable `DEATH_TEST_PARALLEL` to 1 otherwise the parallel tests will attempt to write to the project-wide `lastSuccessfulBuild` file simultaneously, which causes the build to fail. MSVC is supported but only when using the C++20-style compile-time strings due [this](https://developercommunity.visualstudio.com/t/1395099) MSVC bug. +You can build the unit tests on Windows using MSBuild but you must set the CMake variable `DEATH_TEST_PARALLEL` to 1 otherwise the parallel tests will attempt to write to the project-wide `lastSuccessfulBuild` file simultaneously, which causes the build to fail. MSVC is supported but only when using the C++20-style compile-time strings due [this](https://developercommunity.visualstudio.com/t/1395099) MSVC bug. Other compiler versions and platform combinations may work, but I'm currently limited by the built-in GitHub runners and how much I'm willing to spend on Actions! @@ -870,6 +1013,8 @@ Other compiler versions and platform combinations may work, but I'm currently li ### Do **NOT** Make the Parse Tree Type Accessible The parse tree is _very_ expensive to construct due to all the compile-time checking and meta-programming shennanigans, so do **NOT** define it in a header and have multiple source files include it - it will cause the tree to be built/checked in every source file it is included in. +This is especially important on Windows as `windows.h` is included (needed for calculating terminal column width) with `NOMINMAX` and `WIN32_LEAN_AND_MEAN` set by default. + ### Minimise Static Storage Bloat Despite not using `typeid` or `dynamic_cast` in the library, compilers will still generate class name data if RTTI is enabled, because it is used in the standard library implementations (e.g. `std::function` on Clang). Due to the highly nested templates that make up the parse tree, these class names can become huge and occupy large amount of static storage in the executable. As an example, the `basic_cat` project in the repo will create ~100KB of class name data in the binary - this data is not used and cannot be stripped out. diff --git a/scripts/ci/calculate_test_coverage.sh b/ci/calculate_test_coverage.sh similarity index 94% rename from scripts/ci/calculate_test_coverage.sh rename to ci/calculate_test_coverage.sh index a57a48dd..f47b0c74 100755 --- a/scripts/ci/calculate_test_coverage.sh +++ b/ci/calculate_test_coverage.sh @@ -1,12 +1,12 @@ #!/usr/bin/env bash -### Copyright (C) 2022 by Camden Mannett. +### Copyright (C) 2022-2023 by Camden Mannett. ### Distributed under the Boost Software License, Version 1.0. ### (See accompanying file LICENSE or copy at https://www.boost.org/LICENSE_1_0.txt) -# Run from inside the scripts/ci folder. +# Run from inside the /ci folder. # - First argument is the top-level build directory and is required -# - Second argument is optional, and defined the gcov tool (defaults to gcov-11) +# - Second argument is optional, and defined the gcov tool (defaults to llvm-gcov-14) if [ -z "$1" ]; then echo "Must pass build directory" exit 1 diff --git a/ci/conan_test_project/CMakeLists.txt b/ci/conan_test_project/CMakeLists.txt new file mode 100644 index 00000000..91f12371 --- /dev/null +++ b/ci/conan_test_project/CMakeLists.txt @@ -0,0 +1,13 @@ +### Copyright (C) 2023 by Camden Mannett. +### Distributed under the Boost Software License, Version 1.0. +### (See accompanying file LICENSE or copy at https://www.boost.org/LICENSE_1_0.txt) + +cmake_minimum_required(VERSION 3.18) + +project(conan_test_project + LANGUAGES CXX) + +find_package(arg_router REQUIRED) + +add_executable(conan_test_project "main.cpp") +target_link_libraries(conan_test_project PUBLIC arg_router::arg_router) diff --git a/ci/conan_test_project/conanfile.py b/ci/conan_test_project/conanfile.py new file mode 100644 index 00000000..0fbeb1d6 --- /dev/null +++ b/ci/conan_test_project/conanfile.py @@ -0,0 +1,31 @@ +# Copyright (C) 2023 by Camden Mannett. +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE or copy at https://www.boost.org/LICENSE_1_0.txt) + +import os + +from conan import ConanFile +from conan.tools.cmake import CMake, cmake_layout +from conan.tools.build import can_run + + +class TestPackageConan(ConanFile): + settings = "os", "compiler", "build_type", "arch" + generators = "CMakeDeps", "CMakeToolchain" + + def requirements(self): + self.requires("arg_router/[>=1.3.0]") + + def build(self): + cmake = CMake(self) + cmake.configure() + cmake.build() + + def layout(self): + cmake_layout(self) + + def test(self): + if can_run(self): + cmd = os.path.join(self.cpp.build.bindir, + "conan_test_project") + " --help" + self.run(cmd, env="conanrun") diff --git a/ci/conan_test_project/main.cpp b/ci/conan_test_project/main.cpp new file mode 100644 index 00000000..e1fb69e1 --- /dev/null +++ b/ci/conan_test_project/main.cpp @@ -0,0 +1,70 @@ +// Copyright (C) 2023 by Camden Mannett. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE or copy at https://www.boost.org/LICENSE_1_0.txt) + +#include + +namespace ar = arg_router; +namespace arp = ar::policy; + +#if (__cplusplus >= 202002L) +using namespace ar::literals; + +int main(int argc, char* argv[]) +{ + ar::root(arp::validation::default_validator, + ar::help("help"_S, + "h"_S, + "Display this help and exit"_S, + arp::program_name_t{"just-cats"_S}, + arp::program_intro_t{"Prints cats!"_S}, + arp::program_addendum_t{"An example program for arg_router."_S}), + ar::flag("cat"_S, "English cat"_S, arp::router{[](bool) { + std::cout << "cat" << std::endl; + }}), + ar::flag("猫"_S, // + arp::description_t{"日本語の猫"_S}, + arp::router{[](bool) { std::cout << "猫" << std::endl; }}), + ar::flag("🐱"_S, // + arp::description_t{"Emoji cat"_S}, + arp::router{[](bool) { std::cout << "🐱" << std::endl; }}), + ar::flag("แมว"_S, // + "แมวไทย"_S, + arp::router{[](bool) { std::cout << "แมว" << std::endl; }}), + ar::flag("кіт"_S, // + "український кіт"_S, + arp::router{[](bool) { std::cout << "кіт" << std::endl; }})) + .parse(argc, argv); + + return EXIT_SUCCESS; +} +#else +int main(int argc, char* argv[]) +{ + ar::root(arp::validation::default_validator, + ar::help(S_("help"){}, + S_('h'){}, + S_("Display this help and exit"){}, + arp::program_name, + arp::program_intro, + arp::program_addendum), + ar::flag(S_("cat"){}, // + S_("English cat"){}, + arp::router{[](bool) { std::cout << "cat" << std::endl; }}), + ar::flag(S_("猫"){}, // + arp::description, + arp::router{[](bool) { std::cout << "猫" << std::endl; }}), + ar::flag(S_("🐱"){}, // + arp::description, + arp::router{[](bool) { std::cout << "🐱" << std::endl; }}), + ar::flag(S_("แมว"){}, // + S_("แมวไทย"){}, + arp::router{[](bool) { std::cout << "แมว" << std::endl; }}), + ar::flag(S_("кіт"){}, // + S_("український кіт"){}, + arp::router{[](bool) { std::cout << "кіт" << std::endl; }})) + .parse(argc, argv); + + return EXIT_SUCCESS; +} +#endif diff --git a/scripts/ci/create_badge_url.sh b/ci/create_badge_url.sh similarity index 90% rename from scripts/ci/create_badge_url.sh rename to ci/create_badge_url.sh index ad048e64..0a31d739 100755 --- a/scripts/ci/create_badge_url.sh +++ b/ci/create_badge_url.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -### Copyright (C) 2022 by Camden Mannett. +### Copyright (C) 2022-2023 by Camden Mannett. ### Distributed under the Boost Software License, Version 1.0. ### (See accompanying file LICENSE or copy at https://www.boost.org/LICENSE_1_0.txt) @@ -10,7 +10,7 @@ if [ "$1" == "-e" ]; then fi # Creates a shields.io badge URL for the unit test coverage -COVERAGE="$(cat ./scripts/ci/old_coverage)" +COVERAGE="$(cat ./ci/old_coverage)" COLOUR="brightgreen" if (( $(echo "$COVERAGE < 50" | bc -l) )); then COLOUR="red" diff --git a/ci/old_coverage b/ci/old_coverage new file mode 100644 index 00000000..81419326 --- /dev/null +++ b/ci/old_coverage @@ -0,0 +1 @@ +97.2 diff --git a/scripts/ci/package_test_project/CMakeLists.txt b/ci/package_test_project/CMakeLists.txt similarity index 51% rename from scripts/ci/package_test_project/CMakeLists.txt rename to ci/package_test_project/CMakeLists.txt index d0e13ccc..8d68c350 100644 --- a/scripts/ci/package_test_project/CMakeLists.txt +++ b/ci/package_test_project/CMakeLists.txt @@ -6,7 +6,7 @@ cmake_minimum_required(VERSION 3.18) option(DISABLE_VCPKG "Disable vcpkg" OFF) -set(ROOT_DIR "${CMAKE_SOURCE_DIR}/../../..") +set(ROOT_DIR "${CMAKE_SOURCE_DIR}/../..") if (NOT DISABLE_VCPKG) set(CMAKE_TOOLCHAIN_FILE "${ROOT_DIR}/external/vcpkg/scripts/buildsystems/vcpkg.cmake") endif() @@ -17,6 +17,14 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON) find_package(arg_router REQUIRED) +arg_router_translation_generator( + SOURCES "${ROOT_DIR}/examples/resources/simple_ml_gen/en_GB.toml" + "${ROOT_DIR}/examples/resources/simple_ml_gen/fr.toml" + "${ROOT_DIR}/examples/resources/simple_ml_gen/ja.toml" + OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/translations/" + TARGET translation_package_test_project +) + # Default to C++20 if(NOT DEFINED CMAKE_CXX_STANDARD) set(CMAKE_CXX_STANDARD "20") @@ -24,30 +32,22 @@ endif() add_executable(package_test_project $, - "${ROOT_DIR}/examples/c++17/just_cats/main.cpp" , - "${ROOT_DIR}/examples/c++20/just_cats/main.cpp" > + "${ROOT_DIR}/examples/c++17/simple_ml_gen/main.cpp" , + "${ROOT_DIR}/examples/c++20/simple_ml_gen/main.cpp" > + ${translation_files} "${CMAKE_CURRENT_SOURCE_DIR}/vcpkg.json") target_link_libraries(package_test_project PUBLIC arg_router::arg_router) set_target_properties(package_test_project PROPERTIES CXX_EXTENSIONS OFF) +add_dependencies(package_test_project translation_package_test_project) +target_include_directories(package_test_project PRIVATE "${CMAKE_CURRENT_BINARY_DIR}") +set(EXTRA_FLAGS -Werror -Wall -Wextra) if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC" OR CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC") - target_compile_options(package_test_project PRIVATE /W4 /Z7) - target_compile_definitions(package_test_project PRIVATE - NOMINMAX - BOOST_USE_WINDOWS_H - WIN32_LEAN_AND_MEAN - _CRT_SECURE_NO_WARNINGS - ) - - if (CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC") - target_compile_options(package_test_project PRIVATE /clang:-fconstexpr-steps=10000000) + set(EXTRA_FLAGS /W4 /Z7 /GR- /permissive-) + + if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + set(EXTRA_FLAGS ${EXTRA_FLAGS} /clang:-fconstexpr-steps=10000000) endif() -else() - target_compile_options(package_test_project PRIVATE - -Werror - -Wall - -Wextra - -ftemplate-backtrace-limit=0 - ) endif() +target_compile_options(package_test_project PRIVATE ${EXTRA_FLAGS}) diff --git a/scripts/ci/package_test_project/vcpkg.json b/ci/package_test_project/vcpkg.json similarity index 100% rename from scripts/ci/package_test_project/vcpkg.json rename to ci/package_test_project/vcpkg.json diff --git a/scripts/ci/vcpkg_test_project/CMakeLists.txt b/ci/vcpkg_test_project/CMakeLists.txt similarity index 71% rename from scripts/ci/vcpkg_test_project/CMakeLists.txt rename to ci/vcpkg_test_project/CMakeLists.txt index 67048ad4..26b44e11 100644 --- a/scripts/ci/vcpkg_test_project/CMakeLists.txt +++ b/ci/vcpkg_test_project/CMakeLists.txt @@ -4,7 +4,7 @@ cmake_minimum_required(VERSION 3.18) -set(ROOT_DIR "${CMAKE_SOURCE_DIR}/../../..") +set(ROOT_DIR "${CMAKE_SOURCE_DIR}/../..") set(CMAKE_TOOLCHAIN_FILE "${ROOT_DIR}/external/vcpkg/scripts/buildsystems/vcpkg.cmake") project(vcpkg_test_project @@ -21,10 +21,8 @@ target_link_libraries(vcpkg_test_project PUBLIC arg_router::arg_router) target_compile_features(vcpkg_test_project PUBLIC cxx_std_20) set_target_properties(vcpkg_test_project PROPERTIES CXX_EXTENSIONS OFF) -target_compile_options(vcpkg_test_project PRIVATE - -Werror - -Wall - -Wextra - -ftemplate-backtrace-limit=0 -) - +if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC" OR CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC") + target_compile_options(vcpkg_test_project PRIVATE /W4 /Z7 /GR- /permissive-) +else() + target_compile_options(vcpkg_test_project PRIVATE -Werror -Wall -Wextra) +endif() diff --git a/scripts/ci/vcpkg_test_project/main.cpp b/ci/vcpkg_test_project/main.cpp similarity index 100% rename from scripts/ci/vcpkg_test_project/main.cpp rename to ci/vcpkg_test_project/main.cpp diff --git a/scripts/ci/vcpkg_test_project/vcpkg.json b/ci/vcpkg_test_project/vcpkg.json similarity index 100% rename from scripts/ci/vcpkg_test_project/vcpkg.json rename to ci/vcpkg_test_project/vcpkg.json diff --git a/cmake/build_types/documentation_script.cmake b/cmake/build_types/documentation_script.cmake index c22796d8..91ba0e40 100644 --- a/cmake/build_types/documentation_script.cmake +++ b/cmake/build_types/documentation_script.cmake @@ -21,7 +21,7 @@ set(NEW_API_MD_PATH "${ROOT}/docs/README_API.md") file(READ ${API_MD_PATH} MD_DATA) -# Remove the badges, by simply removing the couple of lines +# Remove the badges, by simply removing the first couple of lines string(FIND "${MD_DATA}" "\n\n" NL_IDX) if(${NL_IDX} EQUAL -1) message(FATAL "Cannot find README.md first newline") diff --git a/cmake/build_types/unit_test.cmake b/cmake/build_types/unit_test.cmake index b64f959b..42a3c4ca 100644 --- a/cmake/build_types/unit_test.cmake +++ b/cmake/build_types/unit_test.cmake @@ -25,10 +25,12 @@ path_prefixer(TEST_SRCS dependency/one_of_test.cpp flag_same_prefix_test.cpp flag_test.cpp + forwarding_arg_test.cpp help_test.cpp list_test.cpp math_test.cpp mode_test.cpp + multi_arg_test.cpp multi_lang/iso_locale_test.cpp multi_lang/root_test.cpp multi_lang/root_wrapper_test.cpp @@ -54,21 +56,28 @@ path_prefixer(TEST_SRCS policy/min_max_value_t_test.cpp policy/required_test.cpp policy/router_test.cpp + policy/runtime_enable_test.cpp policy/short_form_expander_test.cpp policy/short_name_test.cpp + policy/token_end_marker_test.cpp policy/validator_rule_utilities_test.cpp policy/validator_test.cpp policy/value_separator_test.cpp positional_arg_test.cpp + root_tests/basic_test.cpp root_tests/death_test.cpp root_tests/positional_arg_test.cpp + root_tests/variable_length_test.cpp root_tests/top_level_test.cpp root_test.cpp test_helpers.cpp traits_test.cpp + translation_generator_test.cpp tree_node_test.cpp utility/compile_time_string_test.cpp utility/compile_time_optional_test.cpp + utility/dynamic_string_view_test.cpp + utility/exception_formatter_test.cpp utility/result_test.cpp utility/string_to_policy_test.cpp utility/string_view_ops_test.cpp @@ -77,6 +86,7 @@ path_prefixer(TEST_SRCS utility/unsafe_any_test.cpp utility/utf8/code_point_test.cpp utility/utf8/grapheme_cluster_break_test.cpp + utility/utf8/levenshtein_distance_test.cpp utility/utf8/line_break_test.cpp utility/utf8_test.cpp ) @@ -87,8 +97,17 @@ create_clangformat_target( SOURCES ${TEST_HEADERS} ${TEST_SRCS} ) +# Translation generator unit test input files +include("${CMAKE_SOURCE_DIR}/cmake/translation_generator/translation_generator.cmake") +arg_router_translation_generator( + SOURCES "${CMAKE_SOURCE_DIR}/examples/resources/simple_ml_gen/en_GB.toml" + "${CMAKE_SOURCE_DIR}/examples/resources/simple_ml_gen/ja.toml" + OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/translations/" + TARGET translation_arg_router_test +) + add_executable(arg_router_test ${TEST_HEADERS} ${TEST_SRCS}) -add_dependencies(arg_router_test clangformat_test arg_router) +add_dependencies(arg_router_test translation_arg_router_test clangformat_test arg_router) set_target_properties(arg_router_test PROPERTIES CXX_EXTENSIONS OFF) target_compile_definitions(arg_router_test PRIVATE UNIT_TEST_BUILD) @@ -104,15 +123,15 @@ function(configure_test_build TARGET) # Clang can run in different command line argument modes to mimic gcc or cl.exe, # so we have to test for a 'frontent variant' too if (MSVC_FRONTEND) - set(EXTRA_FLAGS /Zc:__cplusplus /W4 /Z7 /GR- /permissive- /bigobj ${ARGN} ) - set(EXTRA_DEFINES NOMINMAX BOOST_USE_WINDOWS_H WIN32_LEAN_AND_MEAN _CRT_SECURE_NO_WARNINGS) + set(EXTRA_FLAGS /MP /Zc:__cplusplus /W4 /Z7 /GR- /permissive- /bigobj /wd4996 ${ARGN}) + set(EXTRA_DEFINES NOMINMAX WIN32_LEAN_AND_MEAN BOOST_USE_WINDOWS_H _CRT_SECURE_NO_WARNINGS) # /MT by default as it simplifies the running of the unit tests set_property(TARGET ${TARGET} PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") - if (CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC") - set(EXTRA_FLAGS /clang:-fconstexpr-steps=10000000) + if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + set(EXTRA_FLAGS ${EXTRA_FLAGS} /clang:-fconstexpr-steps=10000000) endif() else() set(EXTRA_FLAGS -Werror -Wall -Wextra -ftemplate-backtrace-limit=0 -fno-rtti @@ -143,6 +162,7 @@ target_link_libraries(arg_router_test target_compile_definitions(arg_router_test PRIVATE UNIT_TEST_BUILD AR_REPO_PATH="${CMAKE_SOURCE_DIR}" + UNIT_TEST_BIN_DIR="${CMAKE_CURRENT_BINARY_DIR}" ) add_test(NAME arg_router_test COMMAND arg_router_test -l message) diff --git a/cmake/build_types/unit_test_coverage.cmake b/cmake/build_types/unit_test_coverage.cmake index a6bbba1a..3ab83b95 100644 --- a/cmake/build_types/unit_test_coverage.cmake +++ b/cmake/build_types/unit_test_coverage.cmake @@ -2,13 +2,26 @@ ### Distributed under the Boost Software License, Version 1.0. ### (See accompanying file LICENSE or copy at https://www.boost.org/LICENSE_1_0.txt) +# Translation generator unit test input files +include("${CMAKE_SOURCE_DIR}/cmake/translation_generator/translation_generator.cmake") +arg_router_translation_generator( + SOURCES "${CMAKE_SOURCE_DIR}/examples/resources/simple_ml_gen/en_GB.toml" + "${CMAKE_SOURCE_DIR}/examples/resources/simple_ml_gen/ja.toml" + OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/translations/" + TARGET translation_arg_router_test_coverage +) + add_executable(arg_router_test_coverage EXCLUDE_FROM_ALL ${TEST_HEADERS} ${TEST_SRCS}) -add_dependencies(arg_router_test_coverage clangformat_test arg_router) +add_dependencies( + arg_router_test_coverage + translation_arg_router_test_coverage + clangformat_test arg_router) set_target_properties(arg_router_test_coverage PROPERTIES CXX_EXTENSIONS OFF) target_compile_definitions(arg_router_test_coverage PRIVATE UNIT_TEST_BUILD AR_REPO_PATH="${CMAKE_SOURCE_DIR}" + UNIT_TEST_BIN_DIR="${CMAKE_CURRENT_BINARY_DIR}" ) # Default to C++20 diff --git a/cmake/package/arg_router-config.cmake.in b/cmake/package/arg_router-config.cmake.in index 72bc21c6..ec474e25 100644 --- a/cmake/package/arg_router-config.cmake.in +++ b/cmake/package/arg_router-config.cmake.in @@ -18,6 +18,7 @@ if (NOT span-lite_FOUND) endif() include("${CMAKE_CURRENT_LIST_DIR}/arg_router.cmake") +include("${CMAKE_CURRENT_LIST_DIR}/translation_generator.cmake") set_and_check(arg_router_INCLUDE_DIRS "@PACKAGE_INSTALL_BASE_DIR@") check_required_components(arg_router) diff --git a/cmake/package/install.cmake b/cmake/package/install.cmake index ed467f66..1ba676f2 100644 --- a/cmake/package/install.cmake +++ b/cmake/package/install.cmake @@ -8,6 +8,9 @@ set(INSTALL_BASE_DIR "include") set(INSTALL_AR_DIR "${INSTALL_BASE_DIR}/arg_router") set(CONFIG_FILE "${CMAKE_BINARY_DIR}/cmake/package/arg_router-config.cmake") set(CONFIG_VERSION_FILE "${CMAKE_BINARY_DIR}/cmake/package/arg_router-config-version.cmake") +set(TRANSLATION_GENERATOR_FILES + "${CMAKE_SOURCE_DIR}/cmake/translation_generator/translation_generator.cmake" + "${CMAKE_SOURCE_DIR}/cmake/translation_generator/translation_generator_script.cmake") set(AR_CMAKE_PACKAGE_DIR "share/arg_router" CACHE STRING "Installation suffix of CMake package config files, defaults to share/arg_router") @@ -31,9 +34,10 @@ install(DIRECTORY "${CMAKE_SOURCE_DIR}/include/arg_router" install(FILES "${CMAKE_SOURCE_DIR}/README.md" "${CMAKE_SOURCE_DIR}/LICENSE" DESTINATION ${INSTALL_AR_DIR}) -install(FILES ${CONFIG_FILE} - ${CONFIG_VERSION_FILE} - DESTINATION "${AR_CMAKE_PACKAGE_DIR}") +install(FILES "${CONFIG_FILE}" + "${CONFIG_VERSION_FILE}" + ${TRANSLATION_GENERATOR_FILES} + DESTINATION ${AR_CMAKE_PACKAGE_DIR}) # CPack package configuration set(CPACK_PACKAGE_VENDOR "Camden Mannett") diff --git a/cmake/translation_generator/translation_generator.cmake b/cmake/translation_generator/translation_generator.cmake new file mode 100644 index 00000000..19cd4587 --- /dev/null +++ b/cmake/translation_generator/translation_generator.cmake @@ -0,0 +1,41 @@ +### Copyright (C) 2023 by Camden Mannett. +### Distributed under the Boost Software License, Version 1.0. +### (See accompanying file LICENSE or copy at https://www.boost.org/LICENSE_1_0.txt) + +function(arg_router_translation_generator) + set(single_value_args OUTPUT_DIR TARGET) + set(multi_value_args SOURCES) + cmake_parse_arguments(ARGS "" "${single_value_args}" "${multi_value_args}" ${ARGN}) + + if (NOT DEFINED ARGS_SOURCES) + message(FATAL_ERROR "Translation generator requires at least one source file") + endif() + if (NOT DEFINED ARGS_OUTPUT_DIR) + message(FATAL_ERROR "Translation generator output directory must be set, typically CMAKE_CURRENT_BINARY_DIR") + endif() + if (NOT DEFINED ARGS_TARGET) + message(FATAL_ERROR "Translation generator requires a target output variable") + endif() + + # The script file is always in the dir as this file + get_filename_component(this_file_path "${CMAKE_CURRENT_FUNCTION_LIST_FILE}" DIRECTORY) + set(script_path "${this_file_path}/translation_generator_script.cmake") + + add_custom_target(${ARGS_TARGET} + COMMAND ${CMAKE_COMMAND} + "-DSOURCES=${ARGS_SOURCES}" + -DOUTPUT_DIR=${ARGS_OUTPUT_DIR} + -P ${script_path} + SOURCES ${ARGS_SOURCES} + ${script_path} + VERBATIM) + + # set_source_files_properties cannot be used in a script so that's set here + foreach(source ${ARGS_SOURCES}) + get_filename_component(language_id "${source}" NAME_WLE) + set(output_file "${ARGS_OUTPUT_DIR}/${language_id}.hpp") + + target_sources(${ARGS_TARGET} PRIVATE "${output_file}") + set_source_files_properties("${output_file}" PROPERTIES GENERATED TRUE) + endforeach() +endfunction() diff --git a/cmake/translation_generator/translation_generator_script.cmake b/cmake/translation_generator/translation_generator_script.cmake new file mode 100644 index 00000000..4470fba1 --- /dev/null +++ b/cmake/translation_generator/translation_generator_script.cmake @@ -0,0 +1,134 @@ +### Copyright (C) 2023 by Camden Mannett. +### Distributed under the Boost Software License, Version 1.0. +### (See accompanying file LICENSE or copy at https://www.boost.org/LICENSE_1_0.txt) + +function(__ar_translation_string in_string out_string is_cpp17) + if (is_cpp17) + set(${out_string} "S_(${in_string})" PARENT_SCOPE) + else() + set(${out_string} "str<${in_string}>" PARENT_SCOPE) + endif() +endfunction() + +function(__ar_translation_body_generator) + set(option_args CPP17) + set(single_value_args LANGUAGE SOURCE OUTPUT_VAR) + cmake_parse_arguments(ARGS "${option_args}" "${single_value_args}" "" ${ARGN}) + + __ar_translation_string("\"${ARGS_LANGUAGE}\"" language_str ${ARGS_CPP17}) + set(header "template <> +class translation<${language_str}> +{ +public: +") + set(footer "};\n") + + set(error_code_header "\n using error_code_translations = std::tuple<\n") + set(error_code_footer " >;\n") + + # Write the header + set(output_data "${${ARGS_OUTPUT_VAR}}${header}") + set(error_code_section FALSE) + + cmake_policy(PUSH) + cmake_policy(SET CMP0007 NEW) + + # CMake ranges are inclusive for some reason, so we need to decrement the array length before + # using in the for-loop + file(STRINGS "${ARGS_SOURCE}" lines ENCODING UTF-8) + list(LENGTH lines num_lines_over) + math(EXPR num_lines "${num_lines_over}-1") + + foreach(line_index RANGE ${num_lines}) + list(GET lines ${line_index} line) + + # Skip comment lines + string(SUBSTRING "${line}" 0 1 first_char) + if ((line STREQUAL "") OR (first_char STREQUAL "#")) + continue() + endif() + + if (line STREQUAL "[error_code]") + set(error_code_section TRUE) + string(APPEND output_data "${error_code_header}") + continue() + endif() + + # Split the string into key and value + string(FIND "${line}" " = " div_pos) + if (div_pos EQUAL -1) + message(FATAL_ERROR "Malformed line: ${line}") + endif() + + string(SUBSTRING "${line}" 0 ${div_pos} key) + math(EXPR value_index "${div_pos}+3") + string(SUBSTRING "${line}" ${value_index} -1 value) + + __ar_translation_string("${value}" value_str ${ARGS_CPP17}) + + # Process the entry data + if (error_code_section) + set(output_line " std::pair, ${value_str}>") + # Don't append a comma if this is the last tuple entry, won't compile in C++ + if (line_index EQUAL num_lines) + string(APPEND output_line "\n") + else() + string(APPEND output_line ",\n") + endif() + string(APPEND output_data "${output_line}") + else() + set(output_line " using ${key} = ${value_str};\n") + string(APPEND output_data "${output_line}") + endif() + endforeach() + + cmake_policy(POP) + + # Write the footer and finish + if (error_code_section) + string(APPEND output_data "${error_code_footer}") + endif() + string(APPEND output_data "${footer}") + + set(${ARGS_OUTPUT_VAR} "${output_data}" PARENT_SCOPE) +endfunction() + +# Script entry point +set(header "// Generated by CMake, do not modify manually +namespace arg_router::multi_lang +{ +#ifdef AR_ENABLE_CPP20_STRINGS +") +set(midder "#else\n") +set(footer "#endif\n} // namespace arg_router::multi_lang\n") + +foreach(source ${SOURCES}) + get_filename_component(language_id "${source}" NAME_WLE) + set(output_file "${OUTPUT_DIR}/${language_id}.hpp") + set(output_data "${header}") + + __ar_translation_body_generator( + LANGUAGE "${language_id}" + SOURCE "${source}" + OUTPUT_VAR output_data) + string(APPEND output_data "${midder}") + + __ar_translation_body_generator( + LANGUAGE "${language_id}" + SOURCE "${source}" + OUTPUT_VAR output_data + CPP17) + string(APPEND output_data "${footer}") + + # Only write out the data if it differs from the existing + if (EXISTS "${output_file}") + file(READ "${output_file}" existing_data) + if (NOT "${existing_data}" STREQUAL "${output_data}") + message(STATUS "Generated ${language_id} translation file") + file(WRITE "${output_file}" "${output_data}") + endif() + else() + message(STATUS "Generated ${language_id} translation file") + file(WRITE "${output_file}" "${output_data}") + endif() +endforeach() diff --git a/cmake/versioning/version.cmake b/cmake/versioning/version.cmake index 5942dbd2..e99407da 100644 --- a/cmake/versioning/version.cmake +++ b/cmake/versioning/version.cmake @@ -52,18 +52,3 @@ if(NOT "${VCPKG_JSON_DATA}" STREQUAL "${UPDATED_VCPKG_JSON_DATA}") message(STATUS "Updating vcpkg.json library version") file(WRITE "${CMAKE_SOURCE_DIR}/vcpkg.json" ${UPDATED_VCPKG_JSON_DATA}) endif() - -# Update the version in conanfile.py -file(READ "${CMAKE_SOURCE_DIR}/conanfile.py" CONANFILE_DATA) -string( - REGEX REPLACE - "version = [0-9]*\\.[0-9]*\\.[0-9]*" - "version = ${CMAKE_PROJECT_VERSION}" - UPDATED_CONANFILE_DATA - "${CONANFILE_DATA}" -) - -if(NOT "${CONANFILE_DATA}" STREQUAL "${UPDATED_CONANFILE_DATA}") - message(STATUS "Updating conanfile.py library version") - file(WRITE "${CMAKE_SOURCE_DIR}/conanfile.py" ${UPDATED_CONANFILE_DATA}) -endif() diff --git a/conanfile.py b/conanfile.py deleted file mode 100644 index 669742d0..00000000 --- a/conanfile.py +++ /dev/null @@ -1,55 +0,0 @@ -# Copyright (C) 2023 by Camden Mannett. -# Distributed under the Boost Software License, Version 1.0. -# (See accompanying file LICENSE or copy at https://www.boost.org/LICENSE_1_0.txt) - -import os -from conan import ConanFile -from conan.tools.build import check_min_cppstd -from conan.tools.cmake import CMakeDeps, CMakeToolchain, CMake - - -class arg_routerRecipe(ConanFile): - name = "arg_router" - version = "1.2.2" - - license = "BSL-1.0" - author = "Camden Mannett" - url = "https://github.com/cmannett85/arg_router" - description = "C++ command line argument parsing and routing." - topics = ("cpp", "command-line", "argument-parser", "libraries") - - settings = "build_type", "compiler" - default_options = {"boost/*:header_only": True} - package_type = "header-library" - generators = "CMakeDeps", "CMakeToolchain" - - exports_sources = "CMakeLists.txt", "README.md", "LICENSE", "cmake/*", "include/*", "docs/*", "README.md" - no_copy_source = True - - def requirements(self): - self.requires("boost/[>=1.74.0]") - - def validate(self): - check_min_cppstd(self, 17) - - def layout(self): - self.folders.build = "." - self.folders.generators = "." - self.cpp.source.includedirs = ["include"] - - def build(self): - cmake = CMake(self) - cmake.configure(variables={"INSTALLATION_ONLY": True}) - cmake.build() - - def package(self): - cmake = CMake(self) - cmake.install() - - def package_info(self): - self.cpp_info.builddirs.append(os.path.join("share", "arg_router")) - self.cpp_info.set_property("cmake_find_mode", "none") - - def package_id(self): - # build_type and compiler are needed for the Conan's CMake tools but are not actually used - self.info.settings.clear() diff --git a/docs/Doxyfile b/docs/Doxyfile index 809258e9..8a7d7297 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -1,4 +1,4 @@ -# Doxyfile 1.9.1 +# Doxyfile 1.9.4 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. @@ -12,6 +12,15 @@ # For lists, items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (\" \"). +# +# Note: +# +# Use doxygen to compare the used configuration file with the template +# configuration file: +# doxygen -x [configFile] +# Use doxygen to compare the used configuration file with the template +# configuration file without replacing the environment variables: +# doxygen -x_noenv [configFile] #--------------------------------------------------------------------------- # Project related configuration options @@ -38,7 +47,7 @@ PROJECT_NAME = arg_router # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = 1.3.0 +PROJECT_NUMBER = 1.4.0 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a @@ -60,16 +69,28 @@ PROJECT_LOGO = OUTPUT_DIRECTORY = ./doxygen -# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- -# directories (in 2 levels) under the output directory of each output format and -# will distribute the generated files over these directories. Enabling this +# If the CREATE_SUBDIRS tag is set to YES then doxygen will create up to 4096 +# sub-directories (in 2 levels) under the output directory of each output format +# and will distribute the generated files over these directories. Enabling this # option can be useful when feeding doxygen a huge amount of source files, where # putting all generated files in the same directory would otherwise causes -# performance problems for the file system. +# performance problems for the file system. Adapt CREATE_SUBDIRS_LEVEL to +# control the number of sub-directories. # The default value is: NO. CREATE_SUBDIRS = NO +# Controls the number of sub-directories that will be created when +# CREATE_SUBDIRS tag is set to YES. Level 0 represents 16 directories, and every +# level increment doubles the number of directories, resulting in 4096 +# directories at level 8 which is the default and also the maximum value. The +# sub-directories are organized in 2 levels, the first level always has a fixed +# numer of 16 directories. +# Minimum value: 0, maximum value: 8, default value: 8. +# This tag requires that the tag CREATE_SUBDIRS is set to YES. + +CREATE_SUBDIRS_LEVEL = 8 + # If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII # characters to appear in the names of generated files. If set to NO, non-ASCII # characters will be escaped, for example _xE3_x81_x84 will be used for Unicode @@ -81,26 +102,18 @@ ALLOW_UNICODE_NAMES = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. -# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, -# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), -# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, -# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), -# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, -# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, -# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, -# Ukrainian and Vietnamese. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Bulgarian, +# Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, English +# (United States), Esperanto, Farsi (Persian), Finnish, French, German, Greek, +# Hindi, Hungarian, Indonesian, Italian, Japanese, Japanese-en (Japanese with +# English messages), Korean, Korean-en (Korean with English messages), Latvian, +# Lithuanian, Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, +# Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, +# Swedish, Turkish, Ukrainian and Vietnamese. # The default value is: English. OUTPUT_LANGUAGE = English -# The OUTPUT_TEXT_DIRECTION tag is used to specify the direction in which all -# documentation generated by doxygen is written. Doxygen will use this -# information to generate all generated output in the proper direction. -# Possible values are: None, LTR, RTL and Context. -# The default value is: None. - -OUTPUT_TEXT_DIRECTION = None - # If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member # descriptions after the members that are listed in the file and class # documentation (similar to Javadoc). Set to NO to disable this. @@ -259,16 +272,16 @@ TAB_SIZE = 4 # the documentation. An alias has the form: # name=value # For example adding -# "sideeffect=@par Side Effects:\n" +# "sideeffect=@par Side Effects:^^" # will allow you to put the command \sideeffect (or @sideeffect) in the # documentation, which will result in a user-defined paragraph with heading -# "Side Effects:". You can put \n's in the value part of an alias to insert -# newlines (in the resulting output). You can put ^^ in the value part of an -# alias to insert a newline as if a physical newline was in the original file. -# When you need a literal { or } or , in the value part of an alias you have to -# escape them by means of a backslash (\), this can lead to conflicts with the -# commands \{ and \} for these it is advised to use the version @{ and @} or use -# a double escape (\\{ and \\}) +# "Side Effects:". Note that you cannot put \n's in the value part of an alias +# to insert newlines (in the resulting output). You can put ^^ in the value part +# of an alias to insert a newline as if a physical newline was in the original +# file. When you need a literal { or } or , in the value part of an alias you +# have to escape them by means of a backslash (\), this can lead to conflicts +# with the commands \{ and \} for these it is advised to use the version @{ and +# @} or use a double escape (\\{ and \\}) ALIASES = @@ -313,8 +326,8 @@ OPTIMIZE_OUTPUT_SLICE = NO # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, and # language is one of the parsers supported by doxygen: IDL, Java, JavaScript, -# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice, VHDL, -# Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: +# Csharp (C#), C, C++, Lex, D, PHP, md (Markdown), Objective-C, Python, Slice, +# VHDL, Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: # FortranFree, unknown formatted Fortran: Fortran. In the later case the parser # tries to guess whether the code is fixed or free formatted code, this is the # default for Fortran type files). For instance to make doxygen treat .inc files @@ -361,7 +374,7 @@ AUTOLINK_SUPPORT = YES # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should set this # tag to YES in order to let doxygen match functions declarations and -# definitions whose arguments contain STL classes (e.g. func(std::string) +# definitions whose arguments contain STL classes (e.g. func(std::string); # versus func(std::string) {}). This also make the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. # The default value is: NO. @@ -461,13 +474,13 @@ TYPEDEF_HIDES_STRUCT = NO LOOKUP_CACHE_SIZE = 0 -# The NUM_PROC_THREADS specifies the number threads doxygen is allowed to use +# The NUM_PROC_THREADS specifies the number of threads doxygen is allowed to use # during processing. When set to 0 doxygen will based this on the number of # cores available in the system. You can set it explicitly to a value larger # than 0 to get more control over the balance between CPU load and processing # speed. At this moment only the input processing can be done using multiple # threads. Since this is still an experimental feature the default is set to 1, -# which efficively disables parallel processing. Please report any issues you +# which effectively disables parallel processing. Please report any issues you # encounter. Generating dot graphs in parallel is controlled by the # DOT_NUM_THREADS setting. # Minimum value: 0, maximum value: 32, default value: 1. @@ -586,7 +599,7 @@ INTERNAL_DOCS = NO # filesystem is case sensitive (i.e. it supports files in the same directory # whose names only differ in casing), the option must be set to YES to properly # deal with such files in case they appear in the input. For filesystems that -# are not case sensitive the option should be be set to NO to properly deal with +# are not case sensitive the option should be set to NO to properly deal with # output files written for symbols that only differ in casing, such as for two # classes, one named CLASS and the other named Class, and to also support # references to files without having to specify the exact matching casing. On @@ -611,6 +624,12 @@ HIDE_SCOPE_NAMES = NO HIDE_COMPOUND_REFERENCE= NO +# If the SHOW_HEADERFILE tag is set to YES then the documentation for a class +# will show which file needs to be included to use the class. +# The default value is: YES. + +SHOW_HEADERFILE = YES + # If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of # the files that are included by a file in the documentation of that file. # The default value is: YES. @@ -768,7 +787,8 @@ FILE_VERSION_FILTER = # output files in an output format independent way. To create the layout file # that represents doxygen's defaults, run doxygen with the -l option. You can # optionally specify a file name after the option, if omitted DoxygenLayout.xml -# will be used as the name of the layout file. +# will be used as the name of the layout file. See also section "Changing the +# layout of pages" for information. # # Note that if you run doxygen from a directory containing a file called # DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE @@ -814,18 +834,26 @@ WARNINGS = YES WARN_IF_UNDOCUMENTED = YES # If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for -# potential errors in the documentation, such as not documenting some parameters -# in a documented function, or documenting parameters that don't exist or using -# markup commands wrongly. +# potential errors in the documentation, such as documenting some parameters in +# a documented function twice, or documenting parameters that don't exist or +# using markup commands wrongly. # The default value is: YES. WARN_IF_DOC_ERROR = YES +# If WARN_IF_INCOMPLETE_DOC is set to YES, doxygen will warn about incomplete +# function parameter documentation. If set to NO, doxygen will accept that some +# parameters have no documentation without warning. +# The default value is: YES. + +WARN_IF_INCOMPLETE_DOC = YES + # This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that # are documented, but have no documentation for their parameters or return -# value. If set to NO, doxygen will only warn about wrong or incomplete -# parameter documentation, but not about the absence of documentation. If -# EXTRACT_ALL is set to YES then this flag will automatically be disabled. +# value. If set to NO, doxygen will only warn about wrong parameter +# documentation, but not about the absence of documentation. If EXTRACT_ALL is +# set to YES then this flag will automatically be disabled. See also +# WARN_IF_INCOMPLETE_DOC # The default value is: NO. WARN_NO_PARAMDOC = YES @@ -837,7 +865,7 @@ WARN_NO_PARAMDOC = YES # Possible values are: NO, YES and FAIL_ON_WARNINGS. # The default value is: NO. -WARN_AS_ERROR = YES +WARN_AS_ERROR = NO # The WARN_FORMAT tag determines the format of the warning messages that doxygen # can produce. The string should contain the $file, $line, and $text tags, which @@ -845,13 +873,27 @@ WARN_AS_ERROR = YES # and the warning text. Optionally the format may contain $version, which will # be replaced by the version of the file (if it could be obtained via # FILE_VERSION_FILTER) +# See also: WARN_LINE_FORMAT # The default value is: $file:$line: $text. WARN_FORMAT = "$file:$line: $text" +# In the $text part of the WARN_FORMAT command it is possible that a reference +# to a more specific place is given. To make it easier to jump to this place +# (outside of doxygen) the user can define a custom "cut" / "paste" string. +# Example: +# WARN_LINE_FORMAT = "'vi $file +$line'" +# See also: WARN_FORMAT +# The default value is: at line $line of file $file. + +WARN_LINE_FORMAT = "at line $line of file $file" + # The WARN_LOGFILE tag can be used to specify a file to which warning and error # messages should be written. If left blank the output is written to standard -# error (stderr). +# error (stderr). In case the file specified cannot be opened for writing the +# warning and error messages are written to standard error. When as file - is +# specified the warning and error messages are written to standard output +# (stdout). WARN_LOGFILE = @@ -891,10 +933,10 @@ INPUT_ENCODING = UTF-8 # # If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, # *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, -# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, -# *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C comment), -# *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd, *.vhdl, -# *.ucf, *.qsf and *.ice. +# *.hh, *.hxx, *.hpp, *.h++, *.l, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, +# *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C +# comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd, +# *.vhdl, *.ucf, *.qsf and *.ice. FILE_PATTERNS = *.hpp \ *.cpp \ @@ -935,7 +977,7 @@ EXCLUDE_PATTERNS = # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, -# AClass::ANamespace, ANamespace::*Test +# ANamespace::AClass, ANamespace::*Test # # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories use the pattern */test/* @@ -979,7 +1021,7 @@ IMAGE_PATH = related_pages/images # program writes to standard output. If FILTER_PATTERNS is specified, this tag # will be ignored. # -# Note that the filter must not add or remove lines it is applied before the +# Note that the filter must not add or remove lines; it is applied before the # code is scanned, but not when the output code is generated. If lines are added # or removed, the anchors will not be placed correctly. # @@ -1121,9 +1163,11 @@ VERBATIM_HEADERS = YES CLANG_ASSISTED_PARSING = NO -# If clang assisted parsing is enabled and the CLANG_ADD_INC_PATHS tag is set to -# YES then doxygen will add the directory of each input to the include path. +# If the CLANG_ASSISTED_PARSING tag is set to YES and the CLANG_ADD_INC_PATHS +# tag is set to YES then doxygen will add the directory of each input to the +# include path. # The default value is: YES. +# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. CLANG_ADD_INC_PATHS = YES @@ -1252,7 +1296,7 @@ HTML_EXTRA_STYLESHEET = ../external/doxygen-awesome-css/doxygen-awesome.css \ # that these files will be copied to the base HTML output directory. Use the # $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these # files. In the HTML_STYLESHEET file, use the file name only. Also note that the -# files will be copied as-is there are no commands or markers available. +# files will be copied as-is; there are no commands or markers available. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_FILES = ../external/doxygen-awesome-css/doxygen-awesome-darkmode-toggle.js \ @@ -1260,7 +1304,7 @@ HTML_EXTRA_FILES = ../external/doxygen-awesome-css/doxygen-awesome-darkmod # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen # will adjust the colors in the style sheet and background images according to -# this color. Hue is specified as an angle on a colorwheel, see +# this color. Hue is specified as an angle on a color-wheel, see # https://en.wikipedia.org/wiki/Hue for more information. For instance the value # 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 # purple, and 360 is red again. @@ -1270,7 +1314,7 @@ HTML_EXTRA_FILES = ../external/doxygen-awesome-css/doxygen-awesome-darkmod HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors -# in the HTML output. For a value of 0 the output will use grayscales only. A +# in the HTML output. For a value of 0 the output will use gray-scales only. A # value of 255 will produce the most vivid colors. # Minimum value: 0, maximum value: 255, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. @@ -1317,7 +1361,7 @@ HTML_DYNAMIC_MENUS = YES HTML_DYNAMIC_SECTIONS = NO # With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries -# shown in the various tree structured indices initially the user can expand +# shown in the various tree structured indices initially; the user can expand # and collapse entries dynamically later on. Doxygen will expand the tree to # such a level that at most the specified number of entries are visible (unless # a fully collapsed tree already exceeds this amount). So setting the number of @@ -1352,6 +1396,13 @@ GENERATE_DOCSET = NO DOCSET_FEEDNAME = "Doxygen generated docs" +# This tag determines the URL of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDURL = + # This tag specifies a string that should uniquely identify the documentation # set bundle. This should be a reverse domain-name style string, e.g. # com.mycompany.MyDocSet. Doxygen will append .docset to the name. @@ -1377,8 +1428,12 @@ DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three # additional HTML index files: index.hhp, index.hhc, and index.hhk. The # index.hhp is a project file that can be read by Microsoft's HTML Help Workshop -# (see: -# https://www.microsoft.com/en-us/download/details.aspx?id=21138) on Windows. +# on Windows. In the beginning of 2021 Microsoft took the original page, with +# a.o. the download links, offline the HTML help workshop was already many years +# in maintenance mode). You can download the HTML help workshop from the web +# archives at Installation executable (see: +# http://web.archive.org/web/20160201063255/http://download.microsoft.com/downlo +# ad/0/A/9/0A939EF6-E31C-430F-A3DF-DFAE7960D564/htmlhelp.exe). # # The HTML Help Workshop contains a compiler that can convert all HTML output # generated by doxygen into a single compiled HTML file (.chm). Compiled HTML @@ -1537,16 +1592,28 @@ DISABLE_INDEX = NO # to work a browser that supports JavaScript, DHTML, CSS and frames is required # (i.e. any modern browser). Windows users are probably better off using the # HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can -# further fine-tune the look of the index. As an example, the default style -# sheet generated by doxygen has an example that shows how to put an image at -# the root of the tree instead of the PROJECT_NAME. Since the tree basically has -# the same information as the tab index, you could consider setting -# DISABLE_INDEX to YES when enabling this option. +# further fine tune the look of the index (see "Fine-tuning the output"). As an +# example, the default style sheet generated by doxygen has an example that +# shows how to put an image at the root of the tree instead of the PROJECT_NAME. +# Since the tree basically has the same information as the tab index, you could +# consider setting DISABLE_INDEX to YES when enabling this option. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_TREEVIEW = YES +# When both GENERATE_TREEVIEW and DISABLE_INDEX are set to YES, then the +# FULL_SIDEBAR option determines if the side bar is limited to only the treeview +# area (value NO) or if it should extend to the full height of the window (value +# YES). Setting this to YES gives a layout similar to +# https://docs.readthedocs.io with more room for contents, but less room for the +# project logo, title, and description. If either GENERATE_TREEVIEW or +# DISABLE_INDEX is set to NO, this option has no effect. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FULL_SIDEBAR = NO + # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that # doxygen will group on one line in the generated HTML documentation. # @@ -1571,6 +1638,13 @@ TREEVIEW_WIDTH = 350 EXT_LINKS_IN_WINDOW = NO +# If the OBFUSCATE_EMAILS tag is set to YES, doxygen will obfuscate email +# addresses. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +OBFUSCATE_EMAILS = YES + # If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg # tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see # https://inkscape.org) to generate formulas as SVG images instead of PNGs for @@ -1619,11 +1693,29 @@ FORMULA_MACROFILE = USE_MATHJAX = NO +# With MATHJAX_VERSION it is possible to specify the MathJax version to be used. +# Note that the different versions of MathJax have different requirements with +# regards to the different settings, so it is possible that also other MathJax +# settings have to be changed when switching between the different MathJax +# versions. +# Possible values are: MathJax_2 and MathJax_3. +# The default value is: MathJax_2. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_VERSION = MathJax_2 + # When MathJax is enabled you can set the default output format to be used for -# the MathJax output. See the MathJax site (see: -# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. +# the MathJax output. For more details about the output format see MathJax +# version 2 (see: +# http://docs.mathjax.org/en/v2.7-latest/output.html) and MathJax version 3 +# (see: +# http://docs.mathjax.org/en/latest/web/components/output.html). # Possible values are: HTML-CSS (which is slower, but has the best -# compatibility), NativeMML (i.e. MathML) and SVG. +# compatibility. This is the name for Mathjax version 2, for MathJax version 3 +# this will be translated into chtml), NativeMML (i.e. MathML. Only supported +# for NathJax 2. For MathJax version 3 chtml will be used instead.), chtml (This +# is the name for Mathjax version 3, for MathJax version 2 this will be +# translated into HTML-CSS) and SVG. # The default value is: HTML-CSS. # This tag requires that the tag USE_MATHJAX is set to YES. @@ -1636,15 +1728,21 @@ MATHJAX_FORMAT = HTML-CSS # MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax # Content Delivery Network so you can quickly see the result without installing # MathJax. However, it is strongly recommended to install a local copy of -# MathJax from https://www.mathjax.org before deployment. -# The default value is: https://cdn.jsdelivr.net/npm/mathjax@2. +# MathJax from https://www.mathjax.org before deployment. The default value is: +# - in case of MathJax version 2: https://cdn.jsdelivr.net/npm/mathjax@2 +# - in case of MathJax version 3: https://cdn.jsdelivr.net/npm/mathjax@3 # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_RELPATH = https://cdn.jsdelivr.net/npm/mathjax@2 # The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax # extension names that should be enabled during MathJax rendering. For example +# for MathJax version 2 (see +# https://docs.mathjax.org/en/v2.7-latest/tex.html#tex-and-latex-extensions): # MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# For example for MathJax version 3 (see +# http://docs.mathjax.org/en/latest/input/tex/extensions/index.html): +# MATHJAX_EXTENSIONS = ams # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_EXTENSIONS = @@ -1665,7 +1763,7 @@ MATHJAX_CODEFILE = # there is already a search function so this one should typically be disabled. # For large projects the javascript based search engine can be slow, then # enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to -# search using the keyboard to jump to the search box use + S +# search using the keyboard; to jump to the search box use + S # (what the is depends on the OS and browser, but it is typically # , /