first
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
---
|
||||
name: prepare-release
|
||||
description:
|
||||
Prepare a CLI11 release — changelog entry, version bump, README feature
|
||||
markers, and related version references.
|
||||
---
|
||||
|
||||
# Prepare a CLI11 release
|
||||
|
||||
When preparing a release (`chore: prepare X.Y.Z release`):
|
||||
|
||||
## Collect the changes
|
||||
|
||||
List the merged PRs since the last release with
|
||||
`git log --oneline --first-parent v<last>..main`. Every user-visible change gets
|
||||
a changelog line.
|
||||
|
||||
## Changelog entry
|
||||
|
||||
Add a `## Version X.Y.Z: <Short title>` section at the top of `CHANGELOG.md`.
|
||||
The heading becomes the name of the GitHub release, so give it a short title,
|
||||
such as `## Version 2.7.0: Audit and documentation`.
|
||||
|
||||
- Start with a short prose paragraph that summarizes the release.
|
||||
- Group the entries under `### Added`, `### Changed`, `### Fixed`,
|
||||
`### Documentation`, and `### Internal` (and `### Removed`/`### Deprecated` if
|
||||
needed). `Documentation` covers docs-only changes; `Internal` covers CI,
|
||||
tests, and tooling changes with no user-visible effect.
|
||||
- Reference PRs as `[#1305][]` and put the matching link definitions
|
||||
(`[#1305]: https://github.com/CLIUtils/CLI11/pull/1305`) at the end of the
|
||||
version section — each section keeps its own definitions so an extracted
|
||||
section needs no fixup.
|
||||
|
||||
## Version bump
|
||||
|
||||
Bump `include/CLI/Version.hpp`: the `CLI11_VERSION_MAJOR`/`MINOR`/`PATCH` macros
|
||||
and the `CLI11_VERSION` string.
|
||||
|
||||
## Other files
|
||||
|
||||
- Only if not a patch release: Update the feature markers in `README.md`: remove
|
||||
the old 🆕 markers (the previous release's features) and change 🚧 markers
|
||||
(main-only features) to 🆕. Removing an emoji from a heading also changes its
|
||||
TOC anchor — drop the trailing `-` from the matching TOC links.
|
||||
- Check the copyright year in `LICENSE` and the version in
|
||||
`book/chapters/installation.md`.
|
||||
|
||||
## Verify
|
||||
|
||||
CI extracts the release notes from the changelog on every build, so a version
|
||||
bump without a matching changelog section fails. Check locally:
|
||||
|
||||
```bash
|
||||
python3 scripts/ExtractReleaseNotes.py --title "$(python3 scripts/ExtractVersion.py)"
|
||||
python3 scripts/ExtractReleaseNotes.py "$(python3 scripts/ExtractVersion.py)"
|
||||
```
|
||||
|
||||
## After the PR merges
|
||||
|
||||
Tell the user to tag the merge commit as `vX.Y.Z` and push the tag. CI then
|
||||
creates the GitHub release: the name comes from the changelog heading, the body
|
||||
from the changelog section, and the single header plus source packages are
|
||||
attached automatically.
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,24 @@
|
||||
steps:
|
||||
# Needed on GCC 4.8 docker image for some reason
|
||||
- script: mkdir build
|
||||
displayName: "Make build directory"
|
||||
|
||||
- task: CMake@1
|
||||
inputs:
|
||||
cmakeArgs:
|
||||
.. -DCLI11_WARNINGS_AS_ERRORS=ON -DCLI11_SINGLE_FILE=$(cli11.single)
|
||||
-DCMAKE_CXX_STANDARD=$(cli11.std)
|
||||
-DCLI11_SINGLE_FILE_TESTS=$(cli11.single)
|
||||
-DCLI11_PRECOMPILED=$(cli11.precompile)
|
||||
-DCMAKE_BUILD_TYPE=$(cli11.build_type) $(cli11.options)
|
||||
displayName: "Configure"
|
||||
|
||||
- script: cmake --build . -- -j4 --keep-going
|
||||
displayName: "Build Unix"
|
||||
workingDirectory: build
|
||||
condition: ne( variables['Agent.OS'], 'Windows_NT' )
|
||||
|
||||
- script: cmake --build .
|
||||
displayName: "Build Windows"
|
||||
workingDirectory: build
|
||||
condition: eq( variables['Agent.OS'], 'Windows_NT' )
|
||||
@@ -0,0 +1,17 @@
|
||||
steps:
|
||||
# Note that silkeh/clang does not include ca-certificates, so check the shasum for verification
|
||||
- bash: |
|
||||
wget --no-check-certificate "https://cmake.org/files/v3.14/cmake-3.14.3-Linux-x86_64.tar.gz"
|
||||
echo "29faa62fb3a0b6323caa3d9557e1a5f1205614c0d4c5c2a9917f16a74f7eff68 cmake-3.14.3-Linux-x86_64.tar.gz" | shasum -sca 256
|
||||
displayName: Download CMake
|
||||
|
||||
- task: ExtractFiles@1
|
||||
inputs:
|
||||
archiveFilePatterns: "cmake*.tar.gz"
|
||||
destinationFolder: "cmake_program"
|
||||
displayName: Extract CMake
|
||||
|
||||
- bash:
|
||||
echo
|
||||
"##vso[task.prependpath]$(Build.SourcesDirectory)/cmake_program/cmake-3.14.3-Linux-x86_64/bin"
|
||||
displayName: Add CMake to PATH
|
||||
@@ -0,0 +1,9 @@
|
||||
steps:
|
||||
- script: ctest --output-on-failure -C $(cli11.build_type) -T test
|
||||
displayName: "Test"
|
||||
workingDirectory: build
|
||||
|
||||
- task: PublishTestResults@2
|
||||
inputs:
|
||||
testResultsFormat: "cTest"
|
||||
testResultsFiles: "**/Test.xml"
|
||||
@@ -0,0 +1,86 @@
|
||||
Language: Cpp
|
||||
BasedOnStyle: LLVM
|
||||
# AccessModifierOffset: -2
|
||||
# AlignAfterOpenBracket: Align
|
||||
# AlignConsecutiveAssignments: false
|
||||
# AlignConsecutiveDeclarations: false
|
||||
# AlignEscapedNewlinesLeft: false
|
||||
# AlignOperands: true
|
||||
# AlignTrailingComments: true
|
||||
# AllowAllParametersOfDeclarationOnNextLine: true
|
||||
# AllowShortBlocksOnASingleLine: false
|
||||
# AllowShortCaseLabelsOnASingleLine: false
|
||||
# AllowShortFunctionsOnASingleLine: All
|
||||
# AllowShortIfStatementsOnASingleLine: false
|
||||
# AllowShortLoopsOnASingleLine: false
|
||||
# AlwaysBreakAfterDefinitionReturnType: None
|
||||
# AlwaysBreakAfterReturnType: None
|
||||
# AlwaysBreakBeforeMultilineStrings: false
|
||||
# AlwaysBreakTemplateDeclarations: false
|
||||
BinPackArguments: false
|
||||
BinPackParameters: false
|
||||
# BraceWrapping:
|
||||
# AfterClass: false
|
||||
# AfterControlStatement: false
|
||||
# AfterEnum: false
|
||||
# AfterFunction: false
|
||||
# AfterNamespace: false
|
||||
# AfterObjCDeclaration: false
|
||||
# AfterStruct: false
|
||||
# AfterUnion: false
|
||||
# BeforeCatch: false
|
||||
# BeforeElse: false
|
||||
# IndentBraces: false
|
||||
# BreakBeforeBinaryOperators: None
|
||||
# BreakBeforeBraces: Attach
|
||||
# BreakBeforeTernaryOperators: true
|
||||
# BreakConstructorInitializersBeforeComma: false
|
||||
# BreakAfterJavaFieldAnnotations: false
|
||||
# BreakStringLiterals: true
|
||||
ColumnLimit: 120
|
||||
# CommentPragmas: '^ IWYU pragma:'
|
||||
# ConstructorInitializerAllOnOneLineOrOnePerLine: false
|
||||
# ConstructorInitializerIndentWidth: 4
|
||||
# ContinuationIndentWidth: 4
|
||||
# Cpp11BracedListStyle: true
|
||||
# DerivePointerAlignment: false
|
||||
# DisableFormat: false
|
||||
# ExperimentalAutoDetectBinPacking: false
|
||||
# ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ]
|
||||
# IncludeIsMainRegex: '$'
|
||||
# IndentCaseLabels: false
|
||||
IndentWidth: 4
|
||||
# IndentWrappedFunctionNames: false
|
||||
# JavaScriptQuotes: Leave
|
||||
# JavaScriptWrapImports: true
|
||||
# KeepEmptyLinesAtTheStartOfBlocks: true
|
||||
# MacroBlockBegin: ''
|
||||
# MacroBlockEnd: ''
|
||||
# MaxEmptyLinesToKeep: 1
|
||||
# NamespaceIndentation: None
|
||||
# ObjCBlockIndentWidth: 2
|
||||
# ObjCSpaceAfterProperty: false
|
||||
# ObjCSpaceBeforeProtocolList: true
|
||||
# PenaltyBreakBeforeFirstCallParameter: 19
|
||||
# PenaltyBreakComment: 300
|
||||
# PenaltyBreakFirstLessLess: 120
|
||||
# PenaltyBreakString: 1000
|
||||
# PenaltyExcessCharacter: 1000000
|
||||
# PenaltyReturnTypeOnItsOwnLine: 60
|
||||
# PointerAlignment: Right
|
||||
# ReflowComments: true
|
||||
SortIncludes: true
|
||||
# SpaceAfterCStyleCast: false
|
||||
# SpaceAfterTemplateKeyword: true
|
||||
# SpaceBeforeAssignmentOperators: true
|
||||
SpaceBeforeParens: Never
|
||||
# SpaceInEmptyParentheses: false
|
||||
SpacesBeforeTrailingComments: 2
|
||||
# SpacesInAngles: false
|
||||
# SpacesInContainerLiterals: true
|
||||
# SpacesInCStyleCastParentheses: false
|
||||
# SpacesInParentheses: false
|
||||
# SpacesInSquareBrackets: false
|
||||
Standard: Cpp11
|
||||
TabWidth: 4
|
||||
UseTab: Never
|
||||
@@ -0,0 +1,93 @@
|
||||
# Checks that will be implemented in future PRs:
|
||||
# performance-unnecessary-value-param, hints to ~110 issues. Be careful with implementing the suggested changes of this one, as auto-fixes may break the code
|
||||
# bugprone-forwarding-reference-overload probably should be enabled and fixed.
|
||||
# clang-diagnostic-float-equal can be fixed by using _a from Catch::literals
|
||||
# bugprone-exception-escape due to main being a bit simple in examples
|
||||
# modernize-avoid-c-arrays trips up in TEMPLATE_TEST_CASE catch macro
|
||||
# modernize-return-braced-init-list triggers on lambdas ?
|
||||
# modernize-make-unique requires C++14
|
||||
# modernize-type_traits requires C++17
|
||||
# readability-avoid-const-params-in-decls Affected by the pre-compile split
|
||||
|
||||
Checks: |
|
||||
*bugprone*,
|
||||
-bugprone-easily-swappable-parameters,
|
||||
-bugprone-forwarding-reference-overload,
|
||||
-bugprone-exception-escape,
|
||||
-bugprone-crtp-constructor-accessibility,
|
||||
-bugprone-chained-comparison,
|
||||
clang-analyzer-optin.cplusplus.VirtualCall,
|
||||
clang-analyzer-optin.performance.Padding,
|
||||
-clang-diagnostic-float-equal,
|
||||
cppcoreguidelines-init-variables,
|
||||
cppcoreguidelines-prefer-member-initializer,
|
||||
cppcoreguidelines-pro-type-static-cast-downcast,
|
||||
cppcoreguidelines-slicing,
|
||||
google-*,
|
||||
-google-runtime-references,
|
||||
llvm-include-order,
|
||||
llvm-namespace-comment,
|
||||
misc-definitions-in-headers,
|
||||
misc-misplaced-const,
|
||||
misc-non-copyable-objects,
|
||||
misc-static-assert,
|
||||
misc-throw-by-value-catch-by-reference,
|
||||
misc-throw-by-value-catch-by-reference,
|
||||
misc-uniqueptr-reset-release,
|
||||
misc-unused-parameters,
|
||||
modernize*,
|
||||
-modernize-use-trailing-return-type,
|
||||
-modernize-concat-nested-namespaces,
|
||||
-modernize-return-braced-init-list,
|
||||
-modernize-make-unique,
|
||||
-modernize-type-traits,
|
||||
-modernize-macro-to-enum,
|
||||
-modernize-use-constraints,
|
||||
-modernize-use-ranges,
|
||||
-modernize-use-starts-ends-with,
|
||||
-modernize-use-integer-sign-comparison,
|
||||
-modernize-use-designated-initializers,
|
||||
-modernize-use-std-numbers,
|
||||
*performance*,
|
||||
-performance-unnecessary-value-param,
|
||||
-performance-inefficient-string-concatenation,
|
||||
-performance-enum-size,
|
||||
readability-const-return-type,
|
||||
readability-container-size-empty,
|
||||
readability-delete-null-pointer,
|
||||
readability-else-after-return,
|
||||
readability-implicit-bool-conversion,
|
||||
readability-inconsistent-declaration-parameter-name,
|
||||
readability-make-member-function-const,
|
||||
readability-misplaced-array-index,
|
||||
readability-non-const-parameter,
|
||||
readability-qualified-auto,
|
||||
readability-redundant-function-ptr-dereference,
|
||||
readability-redundant-smartptr-get,
|
||||
readability-redundant-string-cstr,
|
||||
readability-simplify-subscript-expr,
|
||||
readability-static-accessed-through-instance,
|
||||
readability-static-definition-in-anonymous-namespace,
|
||||
readability-string-compare,
|
||||
readability-suspicious-call-argument,
|
||||
readability-uniqueptr-delete-release,
|
||||
-clang-analyzer-optin.core.EnumCastOutOfRange
|
||||
|
||||
CheckOptions:
|
||||
- key: google-readability-braces-around-statements.ShortStatementLines
|
||||
value: "3"
|
||||
- key: performance-for-range-copy.WarnOnAllAutoCopies
|
||||
value: true
|
||||
- key: performance-inefficient-string-concatenation.StrictMode
|
||||
value: true
|
||||
- key: performance-unnecessary-value-param.AllowedTypes
|
||||
value: "exception_ptr$;"
|
||||
- key: readability-implicit-bool-conversion.AllowPointerConditions
|
||||
value: true
|
||||
- key: modernize-use-nodiscard.ReplacementString
|
||||
value: "CLI11_NODISCARD"
|
||||
|
||||
HeaderFilterRegex: "CLI.*hpp"
|
||||
|
||||
FormatStyle: file
|
||||
# WarningsAsErrors: "*"
|
||||
@@ -0,0 +1,6 @@
|
||||
format:
|
||||
line_width: 99
|
||||
|
||||
# Causes a few issues - can be solved later, possibly.
|
||||
markup:
|
||||
enable_markup: false
|
||||
@@ -0,0 +1,29 @@
|
||||
---
|
||||
engines:
|
||||
rubocop:
|
||||
enabled: true
|
||||
duplication:
|
||||
enabled: true
|
||||
metrics:
|
||||
enabled: true
|
||||
coverage:
|
||||
enabled: false
|
||||
cppcheck:
|
||||
enabled: true
|
||||
options:
|
||||
suppress:
|
||||
- missingIncludeSystem
|
||||
|
||||
languages:
|
||||
|
||||
ignore:
|
||||
- "style"
|
||||
|
||||
exclude_paths:
|
||||
- "docs/doxygen-awesome*"
|
||||
- "docs/header.html"
|
||||
- "fuzz/**/*"
|
||||
- "fuzz/*"
|
||||
- "scripts/**/*"
|
||||
- "scripts/*"
|
||||
- "**.md"
|
||||
@@ -0,0 +1,13 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
insert_final_newline = true
|
||||
end_of_line = lf
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.cpp,*.hpp,*.py]
|
||||
indent_size = 4
|
||||
|
||||
[*.yml]
|
||||
indent_size = 2
|
||||
Vendored
+90
@@ -0,0 +1,90 @@
|
||||
# Contributing
|
||||
|
||||
Thanks for considering to write a Pull Request (PR) for CLI11! Here are a few
|
||||
guidelines to get you started:
|
||||
|
||||
Make sure you are comfortable with the license; all contributions are licensed
|
||||
under the original license.
|
||||
|
||||
## Adding functionality
|
||||
|
||||
Make sure any new functions you add are are:
|
||||
|
||||
- Documented by `///` documentation for Doxygen
|
||||
- Mentioned in the instructions in the README, though brief mentions are okay
|
||||
- Explained in your PR (or previously explained in an Issue mentioned in the PR)
|
||||
- Completely covered by tests
|
||||
|
||||
In general, make sure the addition is well thought out and does not increase the
|
||||
complexity of CLI11 needlessly.
|
||||
|
||||
## Things you should know
|
||||
|
||||
- Once you make the PR, tests will run to make sure your code works on all
|
||||
supported platforms
|
||||
- The test coverage is also measured, and that should remain 100%
|
||||
- Formatting should be done with prek or pre-commit, otherwise the format check
|
||||
will not pass. However, it is trivial to apply this to your PR, so don't worry
|
||||
about this check. If you do want to run it, see below.
|
||||
- Everything must pass clang-tidy as well, run with
|
||||
`-DCMAKE_CXX_CLANG_TIDY="$(which clang-tidy)"` (if you set
|
||||
`"$(which clang-tidy) -fix"`, make sure you use a single threaded build
|
||||
process, or just build one example target).
|
||||
- Your changes must also conform to most of the
|
||||
[Google C++ Style Guide](https://google.github.io/styleguide/cppguide.html)
|
||||
rules checked by [cpplint](https://github.com/cpplint/cpplint). For unused
|
||||
cpplint filters and justifications, see [CPPLINT.cfg](/CPPLINT.cfg).
|
||||
|
||||
## Pre-commit
|
||||
|
||||
Format is handled by pre-commit / prek. You should install it (or use
|
||||
[pipx](https://pypa.github.io/pipx/) or [uv](https://docs.astral.sh/uv/)):
|
||||
|
||||
```bash
|
||||
python3 -m pip install prek
|
||||
```
|
||||
|
||||
Then, you can run it on the items you've added to your staging area, or all
|
||||
files:
|
||||
|
||||
```bash
|
||||
prek
|
||||
# OR
|
||||
prek -a
|
||||
```
|
||||
|
||||
And, if you want to always use it, you can install it as a git hook (hence the
|
||||
name, pre-commit):
|
||||
|
||||
```bash
|
||||
prek install
|
||||
```
|
||||
|
||||
## For maintainers: remember to add contributions
|
||||
|
||||
In a commit to a PR, just add
|
||||
"`@all-contributors please add <username> for <contributions>`" or similar (see
|
||||
<https://allcontributors.org>). Use `code` for code, `bug` if an issue was
|
||||
submitted, `platform` for packaging stuff, and `doc` for documentation updates.
|
||||
|
||||
To run locally, do:
|
||||
|
||||
```bash
|
||||
yarn add --dev all-contributors-cli
|
||||
yarn all-contributors add username code,bug
|
||||
```
|
||||
|
||||
## For maintainers: Making a release
|
||||
|
||||
Remember to replace the emoji in the readme, being careful not to replace the
|
||||
ones in all-contributors if any overlap.
|
||||
|
||||
Steps:
|
||||
|
||||
- Update changelog if needed
|
||||
- Update the version in `include/CLI/Version.hpp`.
|
||||
- Find and replace in README (new minor/major release only):
|
||||
- Replace " 🆕" and "🆕 " with "" (ignores the description line)
|
||||
- Check for `\/\/$` (vi syntax) to catch leftover `// 🆕`
|
||||
- Replace "🚧" with "🆕" (manually ignore the description line)
|
||||
- Make a release in the GitHub UI, use a name such as "Version X.Y(.Z): Title"
|
||||
@@ -0,0 +1,25 @@
|
||||
name: Quick CMake config
|
||||
description: "Runs CMake 3.14+ (if already setup)"
|
||||
inputs:
|
||||
args:
|
||||
description: "Other arguments"
|
||||
required: false
|
||||
default: ""
|
||||
cmake-version:
|
||||
description: "The CMake version to run"
|
||||
required: true
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: CMake ${{ inputs.cmake-version }}
|
||||
uses: jwlawson/actions-setup-cmake@v2.0.2
|
||||
with:
|
||||
cmake-version: "${{ inputs.cmake-version }}"
|
||||
- run: |
|
||||
mkdir -p build-tmp
|
||||
touch build-tmp/tmp
|
||||
rm -r build-tmp/*
|
||||
(cd build-tmp && cmake .. ${{ inputs.args }})
|
||||
rm -r build-tmp
|
||||
shell: bash
|
||||
Vendored
+25
@@ -0,0 +1,25 @@
|
||||
# Wait for all coverage jobs before computing status
|
||||
codecov:
|
||||
notify:
|
||||
after_n_builds: 8
|
||||
|
||||
# Only comment when the coverage numbers actually move
|
||||
comment:
|
||||
after_n_builds: 8
|
||||
require_changes: true
|
||||
|
||||
ignore:
|
||||
- "tests"
|
||||
- "examples"
|
||||
- "book"
|
||||
- "docs"
|
||||
- "test_package"
|
||||
- "fuzz"
|
||||
|
||||
parsers:
|
||||
gcov:
|
||||
branch_detection:
|
||||
conditional: yes
|
||||
loop: yes
|
||||
method: no
|
||||
macro: no
|
||||
Vendored
+13
@@ -0,0 +1,13 @@
|
||||
version: 2
|
||||
updates:
|
||||
# Maintain dependencies for GitHub Actions
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
target-branch: "main"
|
||||
open-pull-requests-limit: 10
|
||||
groups:
|
||||
actions:
|
||||
patterns:
|
||||
- "*"
|
||||
Vendored
+4
@@ -0,0 +1,4 @@
|
||||
needs changelog:
|
||||
- all: ["!CHANGELOG.md"]
|
||||
needs README:
|
||||
- all: ["!README.md"]
|
||||
@@ -0,0 +1,75 @@
|
||||
name: Build
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- v*
|
||||
tags:
|
||||
- "*"
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
single-header:
|
||||
name: Single header
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write # Grant write permission for contents to upload artifacts
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.x"
|
||||
|
||||
- name: Prepare CMake config
|
||||
run: cmake -S . -B build -DCLI11_SINGLE_FILE=ON
|
||||
|
||||
- name: Make package
|
||||
run: cmake --build build --target package_source
|
||||
|
||||
- name: Copy source packages
|
||||
run: |
|
||||
mkdir -p CLI11-Source
|
||||
cp build/CLI11-*-Source.* CLI11-Source
|
||||
|
||||
- name: Make header
|
||||
run: cmake --build build --target CLI11-generate-single-file
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: CLI11.hpp
|
||||
path: build/single-include/CLI11.hpp
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: CLI11-Source
|
||||
path: CLI11-Source
|
||||
|
||||
# Runs on every build, so a version bump without a changelog entry fails
|
||||
# here instead of at tag time.
|
||||
- name: Make release notes
|
||||
id: notes
|
||||
run: |
|
||||
if [ "$GITHUB_REF_TYPE" = tag ]; then
|
||||
version="$GITHUB_REF_NAME"
|
||||
else
|
||||
version="$(python3 scripts/ExtractVersion.py)"
|
||||
fi
|
||||
python3 scripts/ExtractReleaseNotes.py "$version" > release-notes.md
|
||||
title="$(python3 scripts/ExtractReleaseNotes.py --title "$version")"
|
||||
echo "title=$title" >> "$GITHUB_OUTPUT"
|
||||
echo "$title"
|
||||
cat release-notes.md
|
||||
|
||||
- name: Release
|
||||
uses: softprops/action-gh-release@v2
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
with:
|
||||
name: ${{ steps.notes.outputs.title }}
|
||||
body_path: release-notes.md
|
||||
files: |
|
||||
build/single-include/CLI11.hpp
|
||||
CLI11-Source/CLI11-*-Source.tar.gz
|
||||
CLI11-Source/CLI11-*-Source.zip
|
||||
+150
@@ -0,0 +1,150 @@
|
||||
name: Build Tests
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- v*
|
||||
pull_request:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
CTEST_OUTPUT_ON_FAILURE: "1"
|
||||
|
||||
jobs:
|
||||
cuda11-build:
|
||||
name: CUDA 11 build only
|
||||
runs-on: ubuntu-latest
|
||||
container: nvidia/cuda:11.8.0-devel-ubuntu22.04
|
||||
steps:
|
||||
- name: Add build tools
|
||||
run: apt-get update && apt-get install -y wget git cmake
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- name: Configure
|
||||
run: cmake -S . -B build -DCLI11_CUDA_TESTS=ON
|
||||
- name: Build
|
||||
run: cmake --build build -j4
|
||||
|
||||
cuda12-single-header-repro:
|
||||
name: CUDA 12.6 single-header repro
|
||||
runs-on: ubuntu-latest
|
||||
container: nvidia/cuda:12.6.1-devel-ubuntu24.04
|
||||
steps:
|
||||
- name: Add build tools
|
||||
run: apt-get update && apt-get install -y cmake git python3 time
|
||||
- uses: actions/checkout@v4
|
||||
- name: Configure single-header build
|
||||
run: cmake -S . -B build -DCLI11_SINGLE_FILE=ON -DCLI11_BUILD_TESTS=OFF -DCLI11_BUILD_EXAMPLES=OFF
|
||||
- name: Generate single header
|
||||
run: cmake --build build --target CLI11-generate-single-file
|
||||
- name: Create reproduction source
|
||||
run: |
|
||||
cat <<'EOF' > test.cu
|
||||
#include "CLI11.hpp"
|
||||
int main() { return 0; }
|
||||
EOF
|
||||
- name: Show reproduction source
|
||||
run: cat test.cu
|
||||
- name: Try reproducing nvcc single-header compile issue
|
||||
id: repro
|
||||
run: |
|
||||
/usr/bin/time -v timeout --signal=KILL 3m nvcc test.cu -I "${GITHUB_WORKSPACE}/build/single-include" -o test_cu -std=c++20
|
||||
- name: Summarize reproduction attempt
|
||||
if: always()
|
||||
run: |
|
||||
if [ "${{ steps.repro.outcome }}" = "success" ]; then
|
||||
echo "CUDA 12.6 single-header repro did not trigger; nvcc compiled the single header generated from this branch." >> "$GITHUB_STEP_SUMMARY"
|
||||
elif [ "${{ steps.repro.outcome }}" = "skipped" ]; then
|
||||
echo "CUDA 12.6 single-header repro did not run because an earlier setup step failed." >> "$GITHUB_STEP_SUMMARY"
|
||||
else
|
||||
echo "CUDA 12.6 single-header repro triggered or timed out while compiling the single header generated from this branch." >> "$GITHUB_STEP_SUMMARY"
|
||||
fi
|
||||
|
||||
cuda13-build:
|
||||
name: CUDA 13 build only
|
||||
runs-on: ubuntu-latest
|
||||
container: nvidia/cuda:13.0.0-devel-ubuntu22.04
|
||||
steps:
|
||||
- name: Add build tools
|
||||
run: apt-get update && apt-get install -y wget git cmake
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- name: Configure
|
||||
run: cmake -S . -B build -DCLI11_CUDA_TESTS=ON
|
||||
- name: Build
|
||||
run: cmake --build build -j4
|
||||
|
||||
boost-build:
|
||||
name: Boost build
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- name: Add boost
|
||||
run: sudo apt-get update && sudo apt-get install -y libboost-dev
|
||||
# NOTE: If a boost version matching all requirements cannot be found,
|
||||
# this build step will fail
|
||||
- name: Configure
|
||||
run: cmake -S . -B build -DCLI11_BOOST=ON
|
||||
- name: Build
|
||||
run: cmake --build build -j4
|
||||
- name: Run tests
|
||||
run: ctest --output-on-failure
|
||||
working-directory: build
|
||||
|
||||
sanitizer-build:
|
||||
name: sanitizer build
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
# this build step will fail
|
||||
- name: Configure
|
||||
run: cmake -S . -B build -DCLI11_SANITIZERS=ON
|
||||
- name: Build
|
||||
run: cmake --build build -j4
|
||||
- name: Run tests
|
||||
run: ctest --output-on-failure
|
||||
working-directory: build
|
||||
|
||||
meson-build:
|
||||
name: Meson build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Prepare commands
|
||||
run: |
|
||||
pipx install meson
|
||||
pipx install ninja
|
||||
|
||||
- name: Configure
|
||||
run: meson setup build-meson . -Dtests=enabled
|
||||
|
||||
- name: Build
|
||||
run: meson compile -C build-meson
|
||||
|
||||
- name: Test
|
||||
run: meson test -C build-meson
|
||||
|
||||
bazel-build:
|
||||
name: Bazel build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Build
|
||||
run: bazel build //...
|
||||
|
||||
- name: Test
|
||||
run: bazel test --test_output=errors //...
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
name: CIFuzz
|
||||
on:
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
fuzz:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Build Fuzzers (dry run)
|
||||
uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master
|
||||
with:
|
||||
oss-fuzz-project-name: cli11
|
||||
language: 'c++'
|
||||
dry-run: true
|
||||
|
||||
- name: Run Fuzzers (dry run)
|
||||
uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master
|
||||
with:
|
||||
oss-fuzz-project-name: cli11
|
||||
language: 'c++'
|
||||
fuzz-seconds: 600
|
||||
dry-run: true
|
||||
+159
@@ -0,0 +1,159 @@
|
||||
name: CMake Tests
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- v*
|
||||
pull_request:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
CTEST_OUTPUT_ON_FAILURE: "1"
|
||||
|
||||
jobs:
|
||||
cmake-config-ubuntu-2204:
|
||||
name: CMake config check (Ubuntu 22.04)
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Check CMake 3.14
|
||||
uses: ./.github/actions/quick_cmake
|
||||
with:
|
||||
cmake-version: "3.14"
|
||||
args: -DCLI11_SANITIZERS=ON -DCLI11_BUILD_EXAMPLES_JSON=ON
|
||||
|
||||
- name: Check CMake 3.15
|
||||
uses: ./.github/actions/quick_cmake
|
||||
with:
|
||||
cmake-version: "3.15.6"
|
||||
if: success() || failure()
|
||||
|
||||
- name: Check CMake 3.16
|
||||
uses: ./.github/actions/quick_cmake
|
||||
with:
|
||||
cmake-version: "3.16.2"
|
||||
if: success() || failure()
|
||||
|
||||
cmake-config-ubuntu-2404:
|
||||
name: CMake config check (Ubuntu 24.04)
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Check CMake 3.17
|
||||
uses: ./.github/actions/quick_cmake
|
||||
with:
|
||||
cmake-version: "3.17"
|
||||
|
||||
- name: Check CMake 3.18
|
||||
uses: ./.github/actions/quick_cmake
|
||||
with:
|
||||
cmake-version: "3.18"
|
||||
if: success() || failure()
|
||||
|
||||
- name: Check CMake 3.19
|
||||
uses: ./.github/actions/quick_cmake
|
||||
with:
|
||||
cmake-version: "3.19"
|
||||
if: success() || failure()
|
||||
|
||||
- name: Check CMake 3.20
|
||||
uses: ./.github/actions/quick_cmake
|
||||
with:
|
||||
cmake-version: "3.20"
|
||||
if: success() || failure()
|
||||
|
||||
- name: Check CMake 3.21
|
||||
uses: ./.github/actions/quick_cmake
|
||||
with:
|
||||
cmake-version: "3.21"
|
||||
if: success() || failure()
|
||||
|
||||
- name: Check CMake 3.22
|
||||
uses: ./.github/actions/quick_cmake
|
||||
with:
|
||||
cmake-version: "3.22"
|
||||
if: success() || failure()
|
||||
|
||||
- name: Check CMake 3.23
|
||||
uses: ./.github/actions/quick_cmake
|
||||
with:
|
||||
cmake-version: "3.23"
|
||||
if: success() || failure()
|
||||
|
||||
- name: Check CMake 3.24
|
||||
uses: ./.github/actions/quick_cmake
|
||||
with:
|
||||
cmake-version: "3.24"
|
||||
if: success() || failure()
|
||||
|
||||
- name: Check CMake 3.25
|
||||
uses: ./.github/actions/quick_cmake
|
||||
with:
|
||||
cmake-version: "3.25"
|
||||
if: success() || failure()
|
||||
|
||||
- name: Check CMake 3.26 (full)
|
||||
uses: ./.github/actions/quick_cmake
|
||||
with:
|
||||
cmake-version: "3.26"
|
||||
args: -DCLI11_SANITIZERS=ON -DCLI11_BUILD_EXAMPLES_JSON=ON
|
||||
if: success() || failure()
|
||||
|
||||
- name: Check CMake 3.27
|
||||
uses: ./.github/actions/quick_cmake
|
||||
with:
|
||||
cmake-version: "3.27"
|
||||
if: success() || failure()
|
||||
|
||||
- name: Check CMake 3.28 (full)
|
||||
uses: ./.github/actions/quick_cmake
|
||||
with:
|
||||
cmake-version: "3.28.X"
|
||||
args: -DCLI11_SANITIZERS=ON -DCLI11_BUILD_EXAMPLES_JSON=ON
|
||||
if: success() || failure()
|
||||
|
||||
- name: Check CMake 3.29
|
||||
uses: ./.github/actions/quick_cmake
|
||||
with:
|
||||
cmake-version: "3.29"
|
||||
if: success() || failure()
|
||||
|
||||
- name: Check CMake 3.30
|
||||
uses: ./.github/actions/quick_cmake
|
||||
with:
|
||||
cmake-version: "3.30"
|
||||
if: success() || failure()
|
||||
|
||||
- name: Check CMake 3.31
|
||||
uses: ./.github/actions/quick_cmake
|
||||
with:
|
||||
cmake-version: "3.31"
|
||||
args: -DCLI11_SANITIZERS=ON -DCLI11_BUILD_EXAMPLES_JSON=ON
|
||||
if: success() || failure()
|
||||
|
||||
- name: Check CMake 4.0
|
||||
uses: ./.github/actions/quick_cmake
|
||||
with:
|
||||
cmake-version: "4.0"
|
||||
args: -DCLI11_BUILD_EXAMPLES_JSON=ON
|
||||
if: success() || failure()
|
||||
|
||||
- name: Check CMake 4.1
|
||||
uses: ./.github/actions/quick_cmake
|
||||
with:
|
||||
cmake-version: "4.1"
|
||||
if: success() || failure()
|
||||
|
||||
- name: Check CMake 4.2
|
||||
uses: ./.github/actions/quick_cmake
|
||||
with:
|
||||
cmake-version: "4.2"
|
||||
if: success() || failure()
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
# For most projects, this workflow file will not need changing; you simply need
|
||||
# to commit it to your repository.
|
||||
#
|
||||
# You may wish to alter this file to override the set of languages analyzed,
|
||||
# or to provide custom queries or build logic.
|
||||
#
|
||||
# ******** NOTE ********
|
||||
# We have attempted to detect the languages in your repository. Please check
|
||||
# the `language` matrix defined below to confirm you have the correct set of
|
||||
# supported CodeQL languages.
|
||||
#
|
||||
name: "CodeQL Advanced"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
pull_request:
|
||||
branches: [ "main" ]
|
||||
schedule:
|
||||
- cron: '30 5 * * 5'
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze (${{ matrix.language }})
|
||||
# Runner size impacts CodeQL analysis time. To learn more, please see:
|
||||
# - https://gh.io/recommended-hardware-resources-for-running-codeql
|
||||
# - https://gh.io/supported-runners-and-hardware-resources
|
||||
# - https://gh.io/using-larger-runners (GitHub.com only)
|
||||
# Consider using larger runners or machines with greater resources for possible analysis time improvements.
|
||||
runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }}
|
||||
permissions:
|
||||
# required for all workflows
|
||||
security-events: write
|
||||
|
||||
# required to fetch internal or private CodeQL packs
|
||||
packages: read
|
||||
|
||||
# only required for workflows in private repositories
|
||||
actions: read
|
||||
contents: read
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- language: actions
|
||||
build-mode: none
|
||||
- language: c-cpp
|
||||
build-mode: autobuild
|
||||
- language: python
|
||||
build-mode: none
|
||||
# CodeQL supports the following values keywords for 'language': 'actions', 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'rust', 'swift'
|
||||
# Use `c-cpp` to analyze code written in C, C++ or both
|
||||
# Use 'java-kotlin' to analyze code written in Java, Kotlin or both
|
||||
# Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both
|
||||
# To learn more about changing the languages that are analyzed or customizing the build mode for your analysis,
|
||||
# see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning.
|
||||
# If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how
|
||||
# your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
# Add any setup steps before running the `github/codeql-action/init` action.
|
||||
# This includes steps like installing compilers or runtimes (`actions/setup-node`
|
||||
# or others). This is typically only required for manual builds.
|
||||
# - name: Setup runtime (example)
|
||||
# uses: actions/setup-example@v1
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v4
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
build-mode: ${{ matrix.build-mode }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
|
||||
# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
|
||||
# queries: security-extended,security-and-quality
|
||||
|
||||
# If the analyze step fails for one of the languages you are analyzing with
|
||||
# "We were unable to automatically build your code", modify the matrix above
|
||||
# to set the build mode to "manual" for that language. Then modify this step
|
||||
# to build your code.
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
||||
- name: Run manual build steps
|
||||
if: matrix.build-mode == 'manual'
|
||||
shell: bash
|
||||
run: |
|
||||
echo 'If you are using a "manual" build mode for one or more of the' \
|
||||
'languages you are analyzing, replace this with the commands to build' \
|
||||
'your code, for example:'
|
||||
echo ' make bootstrap'
|
||||
echo ' make release'
|
||||
exit 1
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v4
|
||||
with:
|
||||
category: "/language:${{matrix.language}}"
|
||||
@@ -0,0 +1,40 @@
|
||||
name: "Copilot Setup Steps"
|
||||
|
||||
# Automatically run the setup steps when they are changed to allow for easy validation, and
|
||||
# allow manual testing through the repository's "Actions" tab
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
paths:
|
||||
- .github/workflows/copilot-setup-steps.yml
|
||||
pull_request:
|
||||
paths:
|
||||
- .github/workflows/copilot-setup-steps.yml
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
# The job MUST be called `copilot-setup-steps` or it will not be picked up by Copilot.
|
||||
copilot-setup-steps:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
# Set the permissions to the lowest permissions possible needed for your steps.
|
||||
# Copilot will be given its own token for its operations.
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- name: Install system dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y \
|
||||
doxygen \
|
||||
clang-tidy
|
||||
|
||||
- name: Install pre-commit hooks
|
||||
run: |
|
||||
pipx install prek
|
||||
prek install-hooks
|
||||
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
name: Coverage Tests
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- v*
|
||||
pull_request:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
CTEST_OUTPUT_ON_FAILURE: "1"
|
||||
|
||||
jobs:
|
||||
coverage:
|
||||
name: Coverage
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
std: ["14", "17", "20", "23"]
|
||||
precompile: ["ON", "OFF"]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Get LCov
|
||||
run: |
|
||||
sudo apt-get install ca-certificates lcov
|
||||
#wget https://github.com/linux-test-project/lcov/releases/download/v1.16/lcov-1.16.tar.gz
|
||||
#tar -xzf lcov-1.16.tar.gz
|
||||
#cd lcov-1.16
|
||||
#sudo make install
|
||||
|
||||
- name: Configure
|
||||
run: |
|
||||
cmake -S . -B build \
|
||||
-DCMAKE_CXX_STANDARD=${{matrix.std}} \
|
||||
-DCLI11_SINGLE_FILE_TESTS=OFF \
|
||||
-DCLI11_BUILD_EXAMPLES=OFF \
|
||||
-DCLI11_PRECOMPILED=${{matrix.precompile}} \
|
||||
-DCMAKE_BUILD_TYPE=Coverage \
|
||||
-DCLI11_ENABLE_EXTRA_VALIDATORS=ON
|
||||
|
||||
- name: Build
|
||||
run: cmake --build build -j4
|
||||
|
||||
- name: Test
|
||||
run: cmake --build build --target CLI11_coverage
|
||||
|
||||
- name: Prepare coverage
|
||||
run: |
|
||||
lcov --ignore-errors gcov,mismatch --directory . --capture --output-file coverage.info
|
||||
lcov --remove coverage.info '*/tests/*' '/usr/*' --output-file coverage.info
|
||||
lcov --list coverage.info
|
||||
working-directory: build
|
||||
|
||||
- uses: codecov/codecov-action@v5
|
||||
with:
|
||||
files: build/coverage.info
|
||||
# Protected branches reject tokenless uploads; fork PRs still work without it.
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
Vendored
+80
@@ -0,0 +1,80 @@
|
||||
name: Docs
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: "pages"
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
apidocs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install Doxygen
|
||||
run: |
|
||||
curl -fsSL https://github.com/doxygen/doxygen/releases/download/Release_1_17_0/doxygen-1.17.0.linux.bin.tar.gz | tar -xz -C "$RUNNER_TEMP"
|
||||
echo "$RUNNER_TEMP/doxygen-1.17.0/bin" >> "$GITHUB_PATH"
|
||||
|
||||
- name: Get version from Version.hpp
|
||||
run: |
|
||||
version=$(sed -n 's/#define CLI11_VERSION[ \t]*"\(.*\)"/\1/p' include/CLI/Version.hpp)
|
||||
echo "CLI11_VERSION=$version" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Build API docs
|
||||
run: doxygen docs/Doxyfile
|
||||
|
||||
- name: Create redirects for old URLs
|
||||
run: |
|
||||
redirect() {
|
||||
printf '<!DOCTYPE html>\n<html lang="en"><head><meta charset="utf-8"><meta http-equiv="refresh" content="0; url=%s"><link rel="canonical" href="https://cliutils.github.io/CLI11/%s"><title>Page moved</title></head><body><p>This page moved to <a href="%s">%s</a>.</p></body></html>\n' "$1" "$2" "$1" "$2" > "$3"
|
||||
}
|
||||
mkdir -p html/book/chapters
|
||||
redirect ../book-installation.html book-installation.html html/book/index.html
|
||||
redirect book-installation.html book-installation.html html/book.html
|
||||
redirect "index.html#api-reference" "index.html#api-reference" html/api.html
|
||||
for f in book/chapters/*.md; do
|
||||
name=$(basename "$f" .md)
|
||||
redirect "../../book-$name.html" "book-$name.html" "html/book/chapters/$name.html"
|
||||
done
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: api-docs
|
||||
path: html
|
||||
|
||||
pages:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [apidocs]
|
||||
permissions:
|
||||
pages: write
|
||||
id-token: write
|
||||
environment:
|
||||
name: github-pages
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
if: >
|
||||
success()
|
||||
&& github.ref == 'refs/heads/main'
|
||||
&& github.repository == 'CLIUtils/CLI11'
|
||||
steps:
|
||||
- uses: actions/configure-pages@v5
|
||||
id: pages
|
||||
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: api-docs
|
||||
path: _site
|
||||
|
||||
- uses: actions/upload-pages-artifact@v3
|
||||
|
||||
- uses: actions/deploy-pages@v4
|
||||
id: deployment
|
||||
Vendored
+56
@@ -0,0 +1,56 @@
|
||||
name: Fuzz
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
quick_fuzz1:
|
||||
name: quickfuzz1
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Configure
|
||||
run: |
|
||||
cmake -S . -B build \
|
||||
-DCMAKE_CXX_STANDARD=20 \
|
||||
-DCLI11_SINGLE_FILE_TESTS=OFF \
|
||||
-DCLI11_BUILD_EXAMPLES=OFF \
|
||||
-DCLI11_FUZZ_TARGET=ON \
|
||||
-DCLI11_BUILD_TESTS=OFF \
|
||||
-DCLI11_BUILD_DOCS=OFF \
|
||||
-DCMAKE_CXX_COMPILER=clang++ -DCMAKE_CXX_COMPILER_FORCED=ON \
|
||||
-DCMAKE_CXX_FLAGS="-g -O1 -fsanitize=fuzzer,undefined,address"
|
||||
|
||||
- name: Build
|
||||
run: cmake --build build -j4
|
||||
|
||||
- name: Test_app
|
||||
run: |
|
||||
cd build
|
||||
make QUICK_CLI11_APP_FUZZ
|
||||
|
||||
- name: Test_file
|
||||
run: |
|
||||
cd build
|
||||
make QUICK_CLI11_FILE_FUZZ
|
||||
|
||||
|
||||
- name: artifacts
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: file_failure
|
||||
path: ./build/fuzz/cli11_*_fail_artifact.txt
|
||||
+201
@@ -0,0 +1,201 @@
|
||||
name: Install Tests
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- v*
|
||||
pull_request:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
CTEST_OUTPUT_ON_FAILURE: "1"
|
||||
|
||||
jobs:
|
||||
install:
|
||||
name: install tests
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- name: Configure
|
||||
run: cmake -S . -B build -DCLI11_INSTALL_PACKAGE_TESTS=ON -DCLI11_BUILD_EXAMPLES=OFF -DCMAKE_INSTALL_PREFIX=/home/runner/work/install
|
||||
- name: Build
|
||||
run: cmake --build build -j4
|
||||
- name: install
|
||||
run: cmake --install build
|
||||
- name: Run tests
|
||||
run: ctest --output-on-failure -L Packaging
|
||||
working-directory: build
|
||||
|
||||
install-precompiled:
|
||||
name: install tests precompiled
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- name: Configure
|
||||
run: cmake -S . -B build -DCLI11_INSTALL_PACKAGE_TESTS=ON -DCLI11_BUILD_EXAMPLES=OFF -DCLI11_ENABLE_EXTRA_VALIDATORS=ON -DCMAKE_INSTALL_PREFIX=/home/runner/work/install -DCLI11_PRECOMPILED=ON -DCLI11_DISABLE_IMPL_HEADERS_INSTALL=ON
|
||||
- name: Build
|
||||
run: cmake --build build -j4
|
||||
- name: install
|
||||
run: cmake --install build
|
||||
- name: Run tests
|
||||
run: ctest --output-on-failure -L Packaging
|
||||
working-directory: build
|
||||
|
||||
install-precompiled-shared:
|
||||
name: install tests precompiled shared
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- name: Configure
|
||||
run: cmake -S . -B build -DCLI11_INSTALL_PACKAGE_TESTS=ON -DCLI11_BUILD_EXAMPLES=OFF -DCLI11_ENABLE_EXTRA_VALIDATORS=ON -DCMAKE_INSTALL_PREFIX=/home/runner/work/install -DCLI11_PRECOMPILED=ON -DCLI11_DISABLE_IMPL_HEADERS_INSTALL=ON -DBUILD_SHARED_LIBS=ON
|
||||
- name: Build
|
||||
run: cmake --build build -j4
|
||||
- name: install
|
||||
run: cmake --install build
|
||||
- name: Run tests
|
||||
run: ctest --output-on-failure -L Packaging
|
||||
working-directory: build
|
||||
|
||||
install-precompiled-all:
|
||||
name: install tests precompiled full install
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- name: Configure
|
||||
run: cmake -S . -B build -DCLI11_INSTALL_PACKAGE_TESTS=ON -DCLI11_BUILD_EXAMPLES=OFF -DCLI11_ENABLE_EXTRA_VALIDATORS=ON -DCMAKE_INSTALL_PREFIX=/home/runner/work/install -DCLI11_PRECOMPILED=ON
|
||||
- name: Build
|
||||
run: cmake --build build -j4
|
||||
- name: install
|
||||
run: cmake --install build
|
||||
- name: Configure2
|
||||
run: cmake -S . -B build -DCLI11_INSTALL_PACKAGE_TESTS=ON -DCLI11_BUILD_EXAMPLES=OFF -DCMAKE_INSTALL_PREFIX=/home/runner/work/install
|
||||
- name: Run tests
|
||||
run: ctest --output-on-failure -L Packaging
|
||||
working-directory: build
|
||||
|
||||
install-precompiled-macos:
|
||||
# Shared covers dylib symbol export of the precompiled library
|
||||
name: install tests precompiled shared macos
|
||||
runs-on: macos-15
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- name: Configure
|
||||
run: cmake -S . -B build -DCLI11_INSTALL_PACKAGE_TESTS=ON -DCLI11_BUILD_EXAMPLES=OFF -DCLI11_ENABLE_EXTRA_VALIDATORS=ON -DCMAKE_INSTALL_PREFIX=/Users/runner/work/install -DCLI11_PRECOMPILED=ON -DBUILD_SHARED_LIBS=ON -DCMAKE_CXX_STANDARD=20
|
||||
- name: Build
|
||||
run: cmake --build build -j4
|
||||
- name: install
|
||||
run: cmake --install build
|
||||
- name: Run tests
|
||||
run: ctest --output-on-failure -L Packaging
|
||||
working-directory: build
|
||||
|
||||
install-precompiled-macos-no-validators:
|
||||
name: install tests precompiled macos no validators
|
||||
runs-on: macos-15
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- name: Configure
|
||||
run: cmake -S . -B build -DCLI11_INSTALL_PACKAGE_TESTS=ON -DCLI11_BUILD_EXAMPLES=OFF -DCLI11_ENABLE_EXTRA_VALIDATORS=OFF -DCMAKE_INSTALL_PREFIX=/Users/runner/work/install -DCLI11_PRECOMPILED=ON
|
||||
- name: Build
|
||||
run: cmake --build build -j4
|
||||
- name: install
|
||||
run: cmake --install build
|
||||
- name: Run tests
|
||||
run: ctest --output-on-failure -L Packaging
|
||||
working-directory: build
|
||||
|
||||
install-single_file:
|
||||
name: install tests single file
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- name: Configure
|
||||
run: cmake -S . -B build -DCLI11_INSTALL_PACKAGE_TESTS=ON -DCLI11_BUILD_EXAMPLES=OFF -DCLI11_ENABLE_EXTRA_VALIDATORS=ON -DCMAKE_INSTALL_PREFIX=/home/runner/work/install -DCLI11_SINGLE_FILE=ON
|
||||
- name: Build
|
||||
run: cmake --build build -j4
|
||||
- name: install
|
||||
run: cmake --install build
|
||||
- name: Run tests
|
||||
run: ctest --output-on-failure -L Packaging
|
||||
working-directory: build
|
||||
|
||||
install-module:
|
||||
name: install module include tests (gcc, C++${{ matrix.std }})
|
||||
runs-on: ubuntu-latest
|
||||
container: gcc:15
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
std: ["20", "23"]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- uses: ./.github/actions/quick_cmake
|
||||
with:
|
||||
cmake-version: "4.4"
|
||||
- uses: seanmiddleditch/gha-setup-ninja@master
|
||||
- name: Configure
|
||||
run: cmake -S . -B build -G Ninja -DCLI11_INSTALL_PACKAGE_TESTS=ON -DCLI11_BUILD_EXAMPLES=OFF -DCLI11_MODULE_TESTS=ON -DCLI11_MODULES=ON -DCMAKE_CXX_STANDARD=${{ matrix.std }} -DCMAKE_INSTALL_PREFIX=/home/runner/work/install
|
||||
- name: Build
|
||||
run: cmake --build build -j4
|
||||
- name: install
|
||||
run: cmake --install build
|
||||
- name: Run tests
|
||||
run: ctest --output-on-failure -L Packaging
|
||||
working-directory: build
|
||||
|
||||
install-module-clang:
|
||||
name: install cli11 module tests (C++${{ matrix.std }})
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
std: ["20", "23", "26"]
|
||||
steps:
|
||||
- name: Set up Clang
|
||||
uses: egor-tensin/setup-clang@v2
|
||||
with:
|
||||
version: 20
|
||||
platform: x64
|
||||
- name: Install specific libc++ version
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libc++-20-dev libc++abi-20-dev lld-20
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- uses: ./.github/actions/quick_cmake
|
||||
with:
|
||||
# 4.4+ compiles the imported-module BMI with the consumer's standard
|
||||
cmake-version: "4.4"
|
||||
- uses: seanmiddleditch/gha-setup-ninja@master
|
||||
- name: Configure
|
||||
run: cmake -S . -B build -G Ninja -DCLI11_INSTALL_PACKAGE_TESTS=ON -DCLI11_BUILD_EXAMPLES=OFF -DCLI11_MODULE_TESTS=ON -DCLI11_FORCE_LIBCXX=ON -DCLI11_MODULES=ON -DCMAKE_CXX_STANDARD=${{ matrix.std }} -DCMAKE_INSTALL_PREFIX=/home/runner/work/install -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_LINKER_TYPE=LLD -DCMAKE_CXX_FLAGS=-stdlib=libc++
|
||||
- name: Build
|
||||
run: cmake --build build -j4
|
||||
- name: install
|
||||
run: cmake --install build
|
||||
- name: Run tests
|
||||
run: ctest --output-on-failure -L Packaging
|
||||
working-directory: build
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
name: Platform Tests
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
arm64-gcc:
|
||||
name: ARM64 GCC
|
||||
runs-on: ubuntu-24.04-arm
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Configure
|
||||
run: |
|
||||
cmake -S . -B build \
|
||||
-DCMAKE_CXX_STANDARD=17
|
||||
|
||||
- name: Build
|
||||
run: cmake --build build -j4
|
||||
|
||||
- name: Test
|
||||
run: |
|
||||
cd build
|
||||
ctest --output-on-failure
|
||||
|
||||
freebsd:
|
||||
name: FreeBSD
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Test in FreeBSD
|
||||
id: test
|
||||
uses: vmactions/freebsd-vm@v1
|
||||
with:
|
||||
usesh: true
|
||||
prepare: |
|
||||
pkg install -y cmake pkgconf
|
||||
|
||||
run: |
|
||||
cmake -S . -B build \
|
||||
-DCMAKE_CXX_STANDARD=20
|
||||
cmake --build build -j4
|
||||
cd build
|
||||
ctest --output-on-failure
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
name: PR merged
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [closed]
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
label-merged:
|
||||
name: Changelog needed
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event.pull_request.merged == true
|
||||
steps:
|
||||
- uses: actions/labeler@main
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
configuration-path: .github/labeler_merged.yml
|
||||
+134
@@ -0,0 +1,134 @@
|
||||
name: Tests
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- v*
|
||||
pull_request:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
CTEST_OUTPUT_ON_FAILURE: "1"
|
||||
|
||||
jobs:
|
||||
preset:
|
||||
name: Preset
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Run CMake Workflow
|
||||
run: cmake --workflow default
|
||||
|
||||
|
||||
catch2-3:
|
||||
name: Catch 2 3.x
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Get Catch 2
|
||||
run: brew install catch2
|
||||
|
||||
- name: Configure
|
||||
run: |
|
||||
cmake -S . -B build \
|
||||
-DCMAKE_CXX_STANDARD=14 \
|
||||
-DCLI11_SINGLE_FILE_TESTS=OFF \
|
||||
-DCLI11_BUILD_EXAMPLES=OFF \
|
||||
-DCLI11_PRECOMPILED=ON
|
||||
|
||||
- name: Build
|
||||
run: cmake --build build -j4
|
||||
|
||||
- name: Test
|
||||
run: cmake --build build --target test
|
||||
|
||||
|
||||
docker:
|
||||
name: Docker ${{ matrix.name }}
|
||||
runs-on: ubuntu-latest
|
||||
container: ${{ matrix.container }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- name: gcc15 std23
|
||||
container: gcc:15
|
||||
std: 23
|
||||
precompile: OFF
|
||||
options: -DCMAKE_CXX_FLAGS="-Wstrict-overflow=5" -DCLI11_ENABLE_EXTRA_VALIDATORS=1
|
||||
- name: gcc15 std26
|
||||
container: gcc:15
|
||||
std: 26
|
||||
precompile: OFF
|
||||
options: -DCLI11_ENABLE_EXTRA_VALIDATORS=1
|
||||
- name: gcc12 std20
|
||||
container: gcc:12
|
||||
std: 20
|
||||
precompile: OFF
|
||||
options: -DCMAKE_CXX_FLAGS="-Wredundant-decls -Wconversion"
|
||||
-DCLI11_ENABLE_EXTRA_VALIDATORS=1
|
||||
- name: clang17 std23 precompiled
|
||||
container: silkeh/clang:17
|
||||
std: 23
|
||||
precompile: ON
|
||||
options: -DCMAKE_CXX_FLAGS="-std=c++23 -stdlib=libc++"
|
||||
-DCLI11_ENABLE_EXTRA_VALIDATORS=1 -DCMAKE_CXX_COMPILER=clang++
|
||||
-DCLI11_FORCE_LIBCXX=ON -DCMAKE_LINKER_TYPE=LLD
|
||||
- name: gcc15 std17 precompiled shared lto
|
||||
container: gcc:15
|
||||
std: 17
|
||||
precompile: ON
|
||||
options: -DBUILD_SHARED_LIBS=ON -DCMAKE_CXX_FLAGS="-O2 -flto=auto"
|
||||
- name: clang20 std26
|
||||
container: silkeh/clang:20
|
||||
std: 26
|
||||
precompile: OFF
|
||||
options: -DCMAKE_CXX_FLAGS="-std=c++2c -stdlib=libc++ -Wdocumentation"
|
||||
-DCLI11_ENABLE_EXTRA_VALIDATORS=1 -DCMAKE_CXX_COMPILER=clang++
|
||||
-DCLI11_FORCE_LIBCXX=ON -DCMAKE_LINKER_TYPE=LLD
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
# silkeh/clang does not include ca-certificates, so check the shasum for verification
|
||||
- name: Install CMake
|
||||
run: |
|
||||
wget --no-check-certificate "https://cmake.org/files/v3.31/cmake-3.31.1-linux-x86_64.tar.gz"
|
||||
echo "3b72fde0d40fa8be617667ea08d12c5ee47f6cf8950c2fbfcf2acfb5f83fb9de cmake-3.31.1-linux-x86_64.tar.gz" | sha256sum -c
|
||||
tar -xzf cmake-3.31.1-linux-x86_64.tar.gz
|
||||
echo "$PWD/cmake-3.31.1-linux-x86_64/bin" >> $GITHUB_PATH
|
||||
|
||||
- name: Configure
|
||||
run: cmake -S . -B build -DCLI11_WARNINGS_AS_ERRORS=ON
|
||||
-DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_STANDARD=${{ matrix.std }}
|
||||
-DCLI11_PRECOMPILED=${{ matrix.precompile }} ${{ matrix.options }}
|
||||
|
||||
- name: Build
|
||||
run: cmake --build build -j4
|
||||
|
||||
- name: Test
|
||||
run: ctest --output-on-failure
|
||||
working-directory: build
|
||||
|
||||
clang-tidy:
|
||||
name: Clang-Tidy
|
||||
runs-on: ubuntu-latest
|
||||
container: silkeh/clang:20
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install requirements
|
||||
run: apt-get update && apt-get install -y ninja-build
|
||||
|
||||
- name: Configure
|
||||
run: cmake --preset tidy
|
||||
|
||||
- name: Build
|
||||
run: cmake --build --preset tidy
|
||||
@@ -0,0 +1,28 @@
|
||||
a.out*
|
||||
*.swp
|
||||
/*build*
|
||||
/*out*
|
||||
/test_package/build
|
||||
/Makefile
|
||||
/CMakeFiles/*
|
||||
/cmake_install.cmake
|
||||
/*.kdev4
|
||||
/.vscode
|
||||
/.vs
|
||||
/html/*
|
||||
!/meson.build
|
||||
/CMakeUserPresets.json
|
||||
/bazel-*
|
||||
/MODULE.bazel.lock
|
||||
|
||||
/node_modules/*
|
||||
/package.json
|
||||
/yarn.lock
|
||||
/CLI11.hpp
|
||||
|
||||
/subprojects/Catch2-*
|
||||
/subprojects/packagecache
|
||||
|
||||
# Symlink to AGENTS.md
|
||||
CLAUDE.md
|
||||
/.claude
|
||||
@@ -0,0 +1,96 @@
|
||||
exclude: ^(.github/workflows/|docs/img/|docs/doxygen-awesome|docs/header.html|tests/fuzzFail)
|
||||
ci:
|
||||
autoupdate_commit_msg: "chore(deps): pre-commit.ci autoupdate"
|
||||
autofix_commit_msg: "style: pre-commit.ci fixes"
|
||||
autoupdate_schedule: "monthly"
|
||||
|
||||
repos:
|
||||
- repo: https://github.com/psf/black-pre-commit-mirror
|
||||
rev: 26.5.1
|
||||
hooks:
|
||||
- id: black
|
||||
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v6.0.0
|
||||
hooks:
|
||||
- id: check-added-large-files
|
||||
- id: check-case-conflict
|
||||
- id: check-merge-conflict
|
||||
- id: check-symlinks
|
||||
- id: check-yaml
|
||||
- id: check-toml
|
||||
- id: check-json
|
||||
- id: end-of-file-fixer
|
||||
- id: mixed-line-ending
|
||||
- id: trailing-whitespace
|
||||
- id: check-illegal-windows-names
|
||||
- id: check-shebang-scripts-are-executable
|
||||
- id: check-executables-have-shebangs
|
||||
- id: debug-statements
|
||||
|
||||
- repo: https://github.com/pre-commit/mirrors-clang-format
|
||||
rev: v22.1.8
|
||||
hooks:
|
||||
- id: clang-format
|
||||
types_or: [c++, c, cuda]
|
||||
|
||||
- repo: https://github.com/cheshirekow/cmake-format-precommit
|
||||
rev: v0.6.13
|
||||
hooks:
|
||||
- id: cmake-format
|
||||
additional_dependencies: [pyyaml]
|
||||
|
||||
- repo: https://github.com/rbubley/mirrors-prettier
|
||||
rev: v3.9.6
|
||||
hooks:
|
||||
- id: prettier
|
||||
types_or: [yaml, markdown, html, css, scss, javascript, json]
|
||||
args: [--prose-wrap=always]
|
||||
- repo: https://github.com/jumanjihouse/pre-commit-hooks
|
||||
rev: 3.0.0
|
||||
hooks:
|
||||
- id: script-must-have-extension
|
||||
- repo: https://github.com/DavidAnson/markdownlint-cli2
|
||||
rev: v0.23.2 # Use the latest stable version
|
||||
hooks:
|
||||
- id: markdownlint-cli2
|
||||
args: ["--fix", "--config", "./scripts/.markdownlint-cli2.jsonc"] # Optional: specify a custom config file
|
||||
# - repo: local
|
||||
# hooks:
|
||||
# - id: remarklint
|
||||
# name: remarklint
|
||||
# language: node
|
||||
# entry: remark
|
||||
# types: [markdown]
|
||||
# args: ["--frail", "--quiet"]
|
||||
# additional_dependencies:
|
||||
# [
|
||||
# remark,
|
||||
# remark-lint,
|
||||
# remark-cli,
|
||||
# remark-preset-lint-recommended,
|
||||
# remark-lint-list-item-indent,
|
||||
# remark-lint-no-undefined-references,
|
||||
# ]
|
||||
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: disallow-caps
|
||||
name: Disallow improper capitalization
|
||||
language: pygrep
|
||||
entry: PyBind|Numpy|Cmake|CCache|PyTest|Github
|
||||
exclude: .pre-commit-config.yaml
|
||||
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: avoid-msvc-macro
|
||||
name: Avoid MSVC <=2017 min/max macro (use extra parens)
|
||||
language: pygrep
|
||||
entry: \b(min|max)\(
|
||||
exclude: .pre-commit-config.yaml
|
||||
|
||||
- repo: https://github.com/codespell-project/codespell
|
||||
rev: v2.4.3
|
||||
hooks:
|
||||
- id: codespell
|
||||
args: ["-L", "atleast,ans,doub,inout,AtMost,hep"]
|
||||
@@ -0,0 +1,155 @@
|
||||
# CLI11 Agent Guide
|
||||
|
||||
Header-only C++11 command line parser library. CMake is the primary build
|
||||
system; Meson and Bazel are also supported.
|
||||
|
||||
## Quick Build & Test
|
||||
|
||||
Use presets. The `dev` workflow is the fastest for iteration; use `default`
|
||||
before a push to verify the primary header-only mode.
|
||||
|
||||
```bash
|
||||
# Fast iteration (precompiled lib, no examples, ccache; configure + build + test)
|
||||
cmake --workflow dev
|
||||
|
||||
# Full header-only build, matches CI (configure + build + test)
|
||||
cmake --workflow default
|
||||
|
||||
# Or step by step
|
||||
cmake --preset dev
|
||||
cmake --build --preset dev
|
||||
ctest --preset dev
|
||||
```
|
||||
|
||||
The `dev` preset uses `ccache`; install it (`brew install ccache`) or override
|
||||
with `cmake --preset dev -DCMAKE_CXX_COMPILER_LAUNCHER=`.
|
||||
|
||||
## Running a Single Test
|
||||
|
||||
Tests are individual Catch2 executables in `build-dev/tests/` (`dev` preset) or
|
||||
`build/tests/` (`default` preset).
|
||||
|
||||
```bash
|
||||
# Run one test executable directly
|
||||
./build-dev/tests/AppTest
|
||||
|
||||
# Or via CTest with a regex
|
||||
ctest --preset dev -R AppTest
|
||||
```
|
||||
|
||||
## Key CMake Options
|
||||
|
||||
| Option | Default | Purpose |
|
||||
| -------------------------- | ----------------------- | --------------------------------------- |
|
||||
| `CLI11_BUILD_TESTS` | `ON` (if top-level) | Build Catch2 test suite |
|
||||
| `CLI11_BUILD_EXAMPLES` | `ON` (if top-level) | Build `examples/` |
|
||||
| `CLI11_BUILD_DOCS` | `ON` (if Doxygen found) | Build Doxygen docs |
|
||||
| `CLI11_SINGLE_FILE` | `OFF` | Generate single `CLI11.hpp` header |
|
||||
| `CLI11_PRECOMPILED` | `OFF` | Build static lib instead of header-only |
|
||||
| `CLI11_WARNINGS_AS_ERRORS` | `OFF` | Turn warnings into errors |
|
||||
| `CLI11_SANITIZERS` | `OFF` | Enable ASan/TSan/UBSan |
|
||||
| `CLI11_BOOST` | `OFF` | Enable Boost.Optional tests |
|
||||
| `CLI11_CUDA_TESTS` | `OFF` | Compile tests with NVCC |
|
||||
|
||||
`CLI11_SINGLE_FILE` and `CLI11_PRECOMPILED` are mutually exclusive.
|
||||
|
||||
## Presets
|
||||
|
||||
- `default` — Debug, Ninja, `CLI11_WARNINGS_AS_ERRORS=ON`, export compile
|
||||
commands.
|
||||
- `dev` — Inherits `default`, adds `CLI11_PRECOMPILED=ON`,
|
||||
`CLI11_BUILD_EXAMPLES=OFF`, and `ccache`. An edit to `impl/*_inl.hpp` only
|
||||
rebuilds the static library, not every test.
|
||||
- `tidy` — Inherits `default`, adds `clang-tidy` with warnings-as-errors. Uses
|
||||
precompiled mode, so each `impl/*_inl.hpp` header is analyzed once (in
|
||||
`src/Precompile.cpp`) instead of in every test and example.
|
||||
- `iwyu` — Inherits `default`, runs `include-what-you-use`. Also precompiled,
|
||||
with tests and examples off, so `src/Precompile.cpp` is the only translation
|
||||
unit and each header is reported once.
|
||||
|
||||
```bash
|
||||
cmake --preset tidy
|
||||
cmake --build --preset tidy
|
||||
```
|
||||
|
||||
## Include-what-you-use
|
||||
|
||||
`brew install include-what-you-use`, then `cmake --preset iwyu` and
|
||||
`cmake --build --preset iwyu`. The build always succeeds and IWYU writes its
|
||||
advice to stderr. Nothing enforces it, so read the report and apply what is
|
||||
correct, with these exceptions:
|
||||
|
||||
- Only act on a removal that both standard libraries agree on; take an addition
|
||||
from either. macOS asks to remove the `<iterator>` includes that Linux needs.
|
||||
- Keep both `<filesystem>` includes. `Macros.hpp` needs it before the
|
||||
`__cpp_lib_filesystem` check, and `Validators.hpp` guards its one with
|
||||
`#if CLI11_HAS_FILESYSTEM`.
|
||||
- Ignore the "should add" lines for `CLI/CLI.hpp` (an artifact of the private
|
||||
pragma in each header), `<version>`, `<AvailabilityInternal.h>`, and `<math>`.
|
||||
|
||||
`scripts/iwyu.imp` maps the detail headers a standard library asks for to the
|
||||
C++ header CLI11 should use; read the comment at its top before you add an
|
||||
entry. It covers both standard libraries, so check Linux after a change:
|
||||
|
||||
```bash
|
||||
docker run --rm -v "$PWD:/src:ro" debian:trixie sh -c '
|
||||
apt-get update -qq && apt-get install -y -qq iwyu cmake ninja-build g++ &&
|
||||
cp -r /src /work && rm -rf /work/build* && cd /work &&
|
||||
cmake --preset iwyu >/dev/null && cmake --build --preset iwyu'
|
||||
```
|
||||
|
||||
## Single Header Generation
|
||||
|
||||
Requires Python. Enable with `CLI11_SINGLE_FILE=ON`:
|
||||
|
||||
```bash
|
||||
cmake -S . -B build -DCLI11_SINGLE_FILE=ON
|
||||
cmake --build build --target CLI11-generate-single-file
|
||||
# Output: build/single-include/CLI11.hpp
|
||||
```
|
||||
|
||||
Script: `scripts/MakeSingleHeader.py`.
|
||||
|
||||
## Library Structure
|
||||
|
||||
- `include/CLI/` — Public headers. The umbrella header is `CLI.hpp`.
|
||||
- `include/CLI/impl/` — `_inl.hpp` implementation headers included by the main
|
||||
headers.
|
||||
- `src/` — `.cpp` files used **only** when `CLI11_PRECOMPILED=ON`.
|
||||
- `single-include/` — CMake rules for the single-header build.
|
||||
- `tests/` — Catch2 tests. `main.cpp` + `catch.hpp` provide the test runner.
|
||||
- `tests/data/` — Test data files copied to the build dir automatically.
|
||||
- `examples/` — Standalone example programs.
|
||||
- `book/` — Extra documentation/examples built only when top-level.
|
||||
|
||||
## Testing Notes
|
||||
|
||||
- Catch2 is auto-downloaded (v2.13.10 header) if not found on the system. Both
|
||||
Catch2 v2 and v3 are supported.
|
||||
- Some tests launch helper applications (`ensure_utf8`, `ensure_utf8_twice`)
|
||||
built from `tests/applications/`.
|
||||
- `FuzzFailTest` requires C++17.
|
||||
- `WindowsTest` is only built on Windows.
|
||||
- `DeprecatedTest` compiles with `-Wno-deprecated-declarations`.
|
||||
- `TimerTest` is in `CLI11_MULTIONLY_TESTS` (exercises multi-threading).
|
||||
|
||||
## Code Style & Linting
|
||||
|
||||
Pre-commit hooks are configured in `.pre-commit-config.yaml`:
|
||||
|
||||
- `clang-format` for C++/C/CUDA
|
||||
- `cmake-format` for CMake
|
||||
- `black` for Python
|
||||
- `prettier` for YAML/Markdown/JSON/etc.
|
||||
- `codespell` for typos
|
||||
- `markdownlint-cli2`
|
||||
- Custom checks: disallow a few common mistakes Run locally:
|
||||
|
||||
```bash
|
||||
prek -a
|
||||
```
|
||||
|
||||
## Version Source of Truth
|
||||
|
||||
The version string is read from `include/CLI/Version.hpp` at configure time. Do
|
||||
not edit project version in `CMakeLists.txt`.
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,286 @@
|
||||
# Copyright (c) 2017-2026, University of Cincinnati, developed by Henry Schreiner
|
||||
# under NSF AWARD 1414736 and by the respective contributors.
|
||||
# All rights reserved.
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
cmake_minimum_required(VERSION 3.14...4.2)
|
||||
# Note: this is a header only library. If you have an older CMake than 3.14,
|
||||
# just add the CLI11/include directory and that's all you need to do.
|
||||
|
||||
set(VERSION_REGEX "#define CLI11_VERSION[ \t]+\"(.+)\"")
|
||||
|
||||
# Read in the line containing the version
|
||||
file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/include/CLI/Version.hpp" VERSION_STRING
|
||||
REGEX ${VERSION_REGEX})
|
||||
|
||||
# Pick out just the version
|
||||
string(REGEX REPLACE ${VERSION_REGEX} "\\1" VERSION_STRING "${VERSION_STRING}")
|
||||
|
||||
# Add the project
|
||||
project(
|
||||
CLI11
|
||||
LANGUAGES CXX
|
||||
VERSION ${VERSION_STRING})
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CLI11_SOURCE_DIR}/cmake")
|
||||
|
||||
# Print the version number of CMake if this is the main project
|
||||
if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
|
||||
message(STATUS "CMake ${CMAKE_VERSION}")
|
||||
|
||||
find_package(Doxygen)
|
||||
|
||||
if(NOT Doxygen_FOUND)
|
||||
message(STATUS "Doxygen not found, building docs has been disabled")
|
||||
endif()
|
||||
|
||||
include(CTest)
|
||||
else()
|
||||
if(NOT DEFINED BUILD_TESTING)
|
||||
set(BUILD_TESTING OFF)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
include(CMakeDependentOption)
|
||||
include(GNUInstallDirs)
|
||||
|
||||
include(FetchContent)
|
||||
|
||||
list(APPEND force-libcxx "CMAKE_CXX_COMPILER_ID STREQUAL \"Clang\"")
|
||||
list(APPEND force-libcxx "CMAKE_SYSTEM_NAME STREQUAL \"Linux\"")
|
||||
list(APPEND force-libcxx "CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME")
|
||||
|
||||
list(APPEND build-docs "CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME")
|
||||
list(APPEND build-docs "Doxygen_FOUND")
|
||||
|
||||
# Necessary to support paths with spaces, see #457
|
||||
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/docs")
|
||||
set(docs_EXIST TRUE)
|
||||
else()
|
||||
set(docs_EXIST FALSE)
|
||||
endif()
|
||||
list(APPEND build-docs "docs_EXIST")
|
||||
|
||||
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/examples")
|
||||
set(examples_EXIST TRUE)
|
||||
else()
|
||||
set(examples_EXIST FALSE)
|
||||
endif()
|
||||
|
||||
option(CLI11_WARNINGS_AS_ERRORS "Turn all warnings into errors (for CI)")
|
||||
option(CLI11_SINGLE_FILE "Generate a single header file")
|
||||
option(CLI11_PRECOMPILED "Generate a precompiled static library instead of a header-only" OFF)
|
||||
option(CLI11_MODULES "Build CLI11 as a C++ module (requires C++20)" OFF)
|
||||
|
||||
cmake_dependent_option(CLI11_SANITIZERS "Download the sanitizers CMake config" OFF
|
||||
"NOT CMAKE_VERSION VERSION_LESS 3.15" OFF)
|
||||
|
||||
cmake_dependent_option(CLI11_BUILD_DOCS "Build CLI11 documentation" ON "${build-docs}" OFF)
|
||||
|
||||
cmake_dependent_option(CLI11_BUILD_TESTS "Build CLI11 tests" ON
|
||||
"BUILD_TESTING;CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME" OFF)
|
||||
|
||||
cmake_dependent_option(CLI11_BUILD_EXAMPLES "Build CLI11 examples" ON
|
||||
"CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME;${examples_EXIST}" OFF)
|
||||
|
||||
cmake_dependent_option(CLI11_BUILD_EXAMPLES_JSON "Build CLI11 json example" OFF
|
||||
"CLI11_BUILD_EXAMPLES;NOT CMAKE_VERSION VERSION_LESS 3.15" OFF)
|
||||
|
||||
cmake_dependent_option(
|
||||
CLI11_FULL_INSTALL "Install all headers regardless of SINGLE_FILE OR PRECOMPILED OPTIONS" OFF
|
||||
"CLI11_SINGLE_FILE " OFF)
|
||||
|
||||
cmake_dependent_option(CLI11_SINGLE_FILE_TESTS "Duplicate all the tests for a single file build"
|
||||
OFF "BUILD_TESTING;CLI11_SINGLE_FILE" OFF)
|
||||
|
||||
cmake_dependent_option(CLI11_INSTALL "Install the CLI11 folder to include during install process"
|
||||
ON "CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME" OFF)
|
||||
|
||||
cmake_dependent_option(
|
||||
CLI11_FORCE_LIBCXX "Force clang to use libc++ instead of libstdc++ (Linux only)" OFF
|
||||
"${force-libcxx}" OFF)
|
||||
|
||||
cmake_dependent_option(
|
||||
CLI11_CUDA_TESTS "Build the tests with NVCC to check for warnings there - requires CMake 3.9+"
|
||||
OFF "CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME" OFF)
|
||||
|
||||
if(NOT CLI11_FULL_INSTALL
|
||||
AND CLI11_PRECOMPILED
|
||||
AND CLI11_SINGLE_FILE)
|
||||
# Sanity check
|
||||
message(FATAL_ERROR "CLI11_PRECOMPILE and CLI11_SINGLE_FILE are mutually exclusive")
|
||||
endif()
|
||||
|
||||
if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
|
||||
set(CMAKE_CXX_STANDARD
|
||||
11
|
||||
CACHE STRING "The C++ standard to use")
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED CMAKE_CXX_EXTENSIONS)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED CMAKE_CXX_STANDARD_REQUIRED)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
endif()
|
||||
|
||||
include(CLI11Warnings)
|
||||
|
||||
# Allow IDE's to group targets into folders
|
||||
if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
|
||||
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
|
||||
|
||||
set(CMAKE_CXX_STANDARD_LIBRARIES_INIT "")
|
||||
set(CMAKE_CXX_STANDARD_LIBRARIES "")
|
||||
endif()
|
||||
|
||||
# Sources
|
||||
|
||||
set(CLI11_headerLoc "${PROJECT_SOURCE_DIR}/include/CLI")
|
||||
|
||||
set(CLI11_headers
|
||||
${CLI11_headerLoc}/App.hpp
|
||||
${CLI11_headerLoc}/Config.hpp
|
||||
${CLI11_headerLoc}/ConfigFwd.hpp
|
||||
${CLI11_headerLoc}/Error.hpp
|
||||
${CLI11_headerLoc}/Formatter.hpp
|
||||
${CLI11_headerLoc}/FormatterFwd.hpp
|
||||
${CLI11_headerLoc}/Macros.hpp
|
||||
${CLI11_headerLoc}/Option.hpp
|
||||
${CLI11_headerLoc}/Split.hpp
|
||||
${CLI11_headerLoc}/StringTools.hpp
|
||||
${CLI11_headerLoc}/TypeTools.hpp
|
||||
${CLI11_headerLoc}/Validators.hpp
|
||||
${CLI11_headerLoc}/ExtraValidators.hpp
|
||||
${CLI11_headerLoc}/Version.hpp
|
||||
${CLI11_headerLoc}/Encoding.hpp
|
||||
${CLI11_headerLoc}/Argv.hpp)
|
||||
|
||||
set(CLI11_impl_headers
|
||||
${CLI11_headerLoc}/impl/App_inl.hpp
|
||||
${CLI11_headerLoc}/impl/Config_inl.hpp
|
||||
${CLI11_headerLoc}/impl/Formatter_inl.hpp
|
||||
${CLI11_headerLoc}/impl/Option_inl.hpp
|
||||
${CLI11_headerLoc}/impl/Split_inl.hpp
|
||||
${CLI11_headerLoc}/impl/StringTools_inl.hpp
|
||||
${CLI11_headerLoc}/impl/TypeTools_inl.hpp
|
||||
${CLI11_headerLoc}/impl/Validators_inl.hpp
|
||||
${CLI11_headerLoc}/impl/ExtraValidators_inl.hpp
|
||||
${CLI11_headerLoc}/impl/Encoding_inl.hpp
|
||||
${CLI11_headerLoc}/impl/Argv_inl.hpp)
|
||||
|
||||
set(CLI11_library_headers ${CLI11_headerLoc}/CLI.hpp ${CLI11_headerLoc}/Timer.hpp)
|
||||
|
||||
# build the fuzzing example or fuzz entry point
|
||||
add_subdirectory(fuzz)
|
||||
|
||||
add_subdirectory(src)
|
||||
add_subdirectory(single-include)
|
||||
|
||||
# Allow tests to be run on CUDA
|
||||
if(CLI11_CUDA_TESTS)
|
||||
enable_language(CUDA)
|
||||
|
||||
# Print out warning and error numbers
|
||||
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -Xcudafe --display_error_number")
|
||||
endif()
|
||||
|
||||
# This folder should be installed
|
||||
if(CLI11_INSTALL OR CLI11_FULL_INSTALL)
|
||||
|
||||
# Use find_package on the installed package: CLI11Config.cmake includes
|
||||
# the exported CLI11Targets.cmake and adds the CLI11::Module alias
|
||||
|
||||
# Add the version in a CMake readable way
|
||||
configure_file("cmake/CLI11ConfigVersion.cmake.in" "CLI11ConfigVersion.cmake" @ONLY)
|
||||
|
||||
# Make version available in the install; the config wrapper adds the
|
||||
# CLI11::Module alias when the module target is present
|
||||
install(FILES "${PROJECT_BINARY_DIR}/CLI11ConfigVersion.cmake" "cmake/CLI11Config.cmake"
|
||||
DESTINATION "${CMAKE_INSTALL_DATADIR}/cmake/CLI11")
|
||||
|
||||
# Install the export target as a file
|
||||
install(
|
||||
EXPORT CLI11Targets
|
||||
FILE CLI11Targets.cmake
|
||||
NAMESPACE CLI11::
|
||||
DESTINATION "${CMAKE_INSTALL_DATADIR}/cmake/CLI11")
|
||||
|
||||
# Use find_package on the installed package
|
||||
if(CLI11_MODULES)
|
||||
export(
|
||||
TARGETS CLI11 CLI11_Module
|
||||
NAMESPACE CLI11::
|
||||
FILE CLI11Targets.cmake)
|
||||
else()
|
||||
export(
|
||||
TARGETS CLI11
|
||||
NAMESPACE CLI11::
|
||||
FILE CLI11Targets.cmake)
|
||||
endif()
|
||||
|
||||
include(cmake/CLI11GeneratePkgConfig.cmake)
|
||||
|
||||
# Register in the user cmake package registry
|
||||
export(PACKAGE CLI11)
|
||||
endif()
|
||||
|
||||
if(CLI11_BUILD_TESTS)
|
||||
include(CTest)
|
||||
add_subdirectory(tests)
|
||||
endif()
|
||||
|
||||
if(CLI11_BUILD_EXAMPLES)
|
||||
add_subdirectory(examples)
|
||||
endif()
|
||||
|
||||
if(CLI11_BUILD_DOCS)
|
||||
add_subdirectory(docs)
|
||||
endif()
|
||||
|
||||
# From a build system, this might not be included.
|
||||
if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/book")
|
||||
add_subdirectory(book)
|
||||
endif()
|
||||
|
||||
# Packaging support
|
||||
if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
|
||||
set(CPACK_PACKAGE_VENDOR "github.com/CLIUtils/CLI11")
|
||||
set(CPACK_PACKAGE_CONTACT "https://${CPACK_PACKAGE_VENDOR}")
|
||||
set(CPACK_PACKAGE_VERSION_MAJOR ${PROJECT_VERSION_MAJOR}) # Automatic in CMake 3.12+
|
||||
set(CPACK_PACKAGE_VERSION_MINOR ${PROJECT_VERSION_MINOR}) # Automatic in CMake 3.12+
|
||||
set(CPACK_PACKAGE_VERSION_PATCH ${PROJECT_VERSION_PATCH}) # Automatic in CMake 3.12+
|
||||
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Command line parser with simple and intuitive interface")
|
||||
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE")
|
||||
set(CPACK_RESOURCE_FILE_README "${CMAKE_CURRENT_SOURCE_DIR}/README.md")
|
||||
set(CPACK_PACKAGE_DESCRIPTION_FILE
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/CLI11.CPack.Description.txt")
|
||||
set(CPACK_SOURCE_GENERATOR "TGZ;ZIP")
|
||||
|
||||
# CPack collects *everything* except what's listed here.
|
||||
set(CPACK_SOURCE_IGNORE_FILES
|
||||
/.git
|
||||
/dist
|
||||
/.*build.*
|
||||
/\\\\.DS_Store
|
||||
/.*\\\\.egg-info
|
||||
/var
|
||||
/azure-pipelines.yml
|
||||
/.ci
|
||||
/docs
|
||||
/examples
|
||||
/test_package
|
||||
/book
|
||||
/.travis.yml
|
||||
.swp
|
||||
/.all-contributorsrc
|
||||
/.pre-commit.*yaml)
|
||||
|
||||
set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE "all")
|
||||
set(CPACK_DEBIAN_COMPRESSION_TYPE "xz")
|
||||
set(CPACK_DEBIAN_PACKAGE_NAME "libcli11-dev")
|
||||
|
||||
include(CPack)
|
||||
endif()
|
||||
@@ -0,0 +1,121 @@
|
||||
{
|
||||
"version": 6,
|
||||
"configurePresets": [
|
||||
{
|
||||
"name": "default",
|
||||
"displayName": "Default",
|
||||
"binaryDir": "build",
|
||||
"generator": "Ninja",
|
||||
"errors": {
|
||||
"dev": true,
|
||||
"deprecated": true
|
||||
},
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Debug",
|
||||
"CMAKE_EXPORT_COMPILE_COMMANDS": true,
|
||||
"CLI11_WARNINGS_AS_ERRORS": true,
|
||||
"CMAKE_COLOR_DIAGNOSTICS": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "dev",
|
||||
"displayName": "Development (fast rebuilds)",
|
||||
"inherits": "default",
|
||||
"binaryDir": "build-dev",
|
||||
"cacheVariables": {
|
||||
"CLI11_PRECOMPILED": true,
|
||||
"CLI11_BUILD_EXAMPLES": false,
|
||||
"CMAKE_CXX_COMPILER_LAUNCHER": "ccache"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "tidy",
|
||||
"displayName": "Clang-tidy",
|
||||
"inherits": "default",
|
||||
"binaryDir": "build-tidy",
|
||||
"cacheVariables": {
|
||||
"CMAKE_CXX_CLANG_TIDY": "clang-tidy;--use-color;--warnings-as-errors=*",
|
||||
"CMAKE_CXX_STANDARD": "17",
|
||||
"CLI11_PRECOMPILED": true,
|
||||
"CLI11_ENABLE_EXTRA_VALIDATORS": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "iwyu",
|
||||
"displayName": "Include-what-you-use",
|
||||
"inherits": "default",
|
||||
"binaryDir": "build-iwyu",
|
||||
"cacheVariables": {
|
||||
"CMAKE_CXX_INCLUDE_WHAT_YOU_USE": "include-what-you-use;-Xiwyu;--mapping_file=${sourceDir}/scripts/iwyu.imp;-Xiwyu;--check_also=${sourceDir}/include/CLI/*.hpp;-Xiwyu;--check_also=${sourceDir}/include/CLI/impl/*.hpp;-Xiwyu;--keep=*/CLI/*.hpp;-Xiwyu;--no_fwd_decls",
|
||||
"CMAKE_CXX_STANDARD": "17",
|
||||
"CMAKE_OSX_SYSROOT": "macosx",
|
||||
"CLI11_PRECOMPILED": true,
|
||||
"CLI11_ENABLE_EXTRA_VALIDATORS": true,
|
||||
"CLI11_WARNINGS_AS_ERRORS": false,
|
||||
"CLI11_BUILD_TESTS": false,
|
||||
"CLI11_BUILD_EXAMPLES": false
|
||||
}
|
||||
}
|
||||
],
|
||||
"buildPresets": [
|
||||
{
|
||||
"name": "default",
|
||||
"displayName": "Default Build",
|
||||
"configurePreset": "default"
|
||||
},
|
||||
{
|
||||
"name": "dev",
|
||||
"displayName": "Development Build",
|
||||
"configurePreset": "dev"
|
||||
},
|
||||
{
|
||||
"name": "tidy",
|
||||
"displayName": "Clang-tidy Build",
|
||||
"configurePreset": "tidy",
|
||||
"nativeToolOptions": ["-k0"]
|
||||
},
|
||||
{
|
||||
"name": "iwyu",
|
||||
"displayName": "Include-what-you-use Build",
|
||||
"configurePreset": "iwyu"
|
||||
}
|
||||
],
|
||||
"testPresets": [
|
||||
{
|
||||
"name": "default",
|
||||
"displayName": "Default",
|
||||
"configurePreset": "default",
|
||||
"output": {
|
||||
"outputOnFailure": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "dev",
|
||||
"displayName": "Development",
|
||||
"configurePreset": "dev",
|
||||
"output": {
|
||||
"outputOnFailure": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"workflowPresets": [
|
||||
{
|
||||
"name": "default",
|
||||
"displayName": "Default Workflow",
|
||||
"steps": [
|
||||
{ "type": "configure", "name": "default" },
|
||||
{ "type": "build", "name": "default" },
|
||||
{ "type": "test", "name": "default" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "dev",
|
||||
"displayName": "Development Workflow",
|
||||
"steps": [
|
||||
{ "type": "configure", "name": "dev" },
|
||||
{ "type": "build", "name": "dev" },
|
||||
{ "type": "test", "name": "dev" }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
set noparent
|
||||
linelength=120 # As in .clang-format
|
||||
|
||||
# Unused filters
|
||||
filter=-build/c++11 # Reports e.g. chrono and thread, which overlap with Chromium's API. Not applicable to general C++ projects.
|
||||
filter=-build/c++17 # google only restrictions not relevant
|
||||
filter=-build/include_order # Requires unusual include order that encourages creating not self-contained headers
|
||||
filter=-build/include_subdir # Prevents including files in current directory for whatever reason
|
||||
filter=-readability/nolint # Conflicts with clang-tidy
|
||||
filter=-readability/check # Catch uses CHECK(a == b) (Tests only)
|
||||
filter=-build/namespaces # Currently using it for one test (Tests only)
|
||||
filter=-runtime/references # Requires fundamental change of API, don't see need for this
|
||||
filter=-runtime/string # Requires not using static const strings which makes thing really annoying
|
||||
filter=-whitespace/blank_line # Unnecessarily strict with blank lines that otherwise help with readability
|
||||
filter=-whitespace/indent # Requires strange 3-space indent of private/protected/public markers
|
||||
filter=-whitespace/parens,-whitespace/braces # Conflict with clang-format
|
||||
filter=-whitespace/newline # handled by clang-format
|
||||
@@ -0,0 +1,25 @@
|
||||
CLI11 2.7.2 Copyright (c) 2017-2026 University of Cincinnati, developed by Henry
|
||||
Schreiner under NSF AWARD 1414736. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms of CLI11, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
3. Neither the name of the copyright holder nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
@@ -0,0 +1,7 @@
|
||||
module(
|
||||
name = "cli11",
|
||||
bazel_compatibility = [">=7.4.0"],
|
||||
)
|
||||
|
||||
bazel_dep(name = "rules_cc", version = "0.2.17")
|
||||
bazel_dep(name = "catch2", version = "3.12.0", dev_dependency = True)
|
||||
+2099
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,179 @@
|
||||
# Copyright (c) 2017-2026, University of Cincinnati, developed by Henry Schreiner
|
||||
# under NSF AWARD 1414736 and by the respective contributors.
|
||||
# All rights reserved.
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
trigger:
|
||||
- main
|
||||
- "v*"
|
||||
|
||||
pr:
|
||||
- main
|
||||
|
||||
variables:
|
||||
# Single-file tests double the test build; only a few jobs turn them on
|
||||
cli11.single: OFF
|
||||
cli11.std: 14
|
||||
cli11.build_type: Debug
|
||||
cli11.options: -DCLI11_EXAMPLES_JSON=ON
|
||||
cli11.precompile: OFF
|
||||
CMAKE_BUILD_PARALLEL_LEVEL: 4
|
||||
|
||||
jobs:
|
||||
- job: CppLint
|
||||
pool:
|
||||
vmImage: "ubuntu-latest"
|
||||
container: helics/buildenv:cpplint
|
||||
steps:
|
||||
- bash: cpplint --counting=detailed --recursive examples include/CLI tests
|
||||
displayName: Checking against google style guide
|
||||
|
||||
- job: build_only
|
||||
strategy:
|
||||
matrix:
|
||||
visual_studio_arm64:
|
||||
vmImage: "windows-2025"
|
||||
cli11.std: 17
|
||||
cli11.build_type: Debug
|
||||
cli11.options:
|
||||
-G "Visual Studio 17 2022" -A ARM64
|
||||
-DCLI11_ENABLE_EXTRA_VALIDATORS=1
|
||||
pool:
|
||||
vmImage: $(vmImage)
|
||||
|
||||
steps:
|
||||
- template: .ci/azure-build.yml
|
||||
|
||||
- job: Native
|
||||
strategy:
|
||||
matrix:
|
||||
Linux14:
|
||||
vmImage: "ubuntu-latest"
|
||||
cli11.single: ON
|
||||
Linux14PC:
|
||||
vmImage: "ubuntu-latest"
|
||||
cli11.precompile: ON
|
||||
Linux14PC-Shared:
|
||||
vmImage: "ubuntu-latest"
|
||||
cli11.precompile: ON
|
||||
cli11.options: -DBUILD_SHARED_LIBS=ON
|
||||
macOS-15_20:
|
||||
vmImage: "macOS-15"
|
||||
cli11.std: 20
|
||||
cli11.options: -DCLI11_ENABLE_EXTRA_VALIDATORS=1
|
||||
macOS-latest_23:
|
||||
vmImage: "macos-latest"
|
||||
cli11.std: 23
|
||||
cli11.single: ON
|
||||
cli11.options: -DCLI11_ENABLE_EXTRA_VALIDATORS=1
|
||||
macOS-14_11:
|
||||
vmImage: "macOS-14"
|
||||
cli11.std: 11
|
||||
macOS-14_14_PC:
|
||||
vmImage: "macOS-14"
|
||||
cli11.std: 14
|
||||
cli11.precompile: ON
|
||||
Windows17:
|
||||
vmImage: "windows-2022"
|
||||
cli11.std: 17
|
||||
cli11.options: -DCLI11_ENABLE_EXTRA_VALIDATORS=1
|
||||
Windows17PC:
|
||||
vmImage: "windows-2022"
|
||||
cli11.std: 17
|
||||
cli11.precompile: ON
|
||||
cli11.options: -DCLI11_ENABLE_EXTRA_VALIDATORS=1
|
||||
Windows11:
|
||||
vmImage: "windows-2022"
|
||||
cli11.std: 11
|
||||
cli11.single: ON
|
||||
Windows20:
|
||||
vmImage: "windows-2025"
|
||||
cli11.std: 20
|
||||
cli11.options:
|
||||
-DCMAKE_CXX_FLAGS="/EHsc /GR-" -DCLI11_DISABLE_EXTRA_VALIDATORS=1
|
||||
WindowsLatest:
|
||||
# CMake compiles MSVC std 23 with /std:c++latest; 26 is not supported yet
|
||||
vmImage: "windows-2025"
|
||||
cli11.std: 23
|
||||
cli11.options: -DCMAKE_CXX_FLAGS="/EHsc /GR-"
|
||||
Linux17nortti:
|
||||
vmImage: "ubuntu-latest"
|
||||
cli11.std: 17
|
||||
cli11.options:
|
||||
-DCMAKE_CXX_FLAGS="-fno-rtti" -DCLI11_ENABLE_EXTRA_VALIDATORS=1
|
||||
pool:
|
||||
vmImage: $(vmImage)
|
||||
steps:
|
||||
- template: .ci/azure-build.yml
|
||||
- template: .ci/azure-test.yml
|
||||
|
||||
- job: Meson
|
||||
pool:
|
||||
vmImage: "ubuntu-latest"
|
||||
steps:
|
||||
- task: UsePythonVersion@0
|
||||
inputs:
|
||||
versionSpec: "3.11"
|
||||
- script: python3 -m pip install meson ninja
|
||||
displayName: install meson
|
||||
- script: mkdir tests/mesonTest/subprojects
|
||||
displayName: generate test directories
|
||||
- script: ln -s "$(pwd)" tests/mesonTest/subprojects/CLI11
|
||||
displayName: generate CLI11 symlink
|
||||
# Ensure that Meson doesn't use cmake or pkgconfig to find CLI11
|
||||
- script: meson setup build --force-fallback-for=CLI11
|
||||
displayName: Run meson to generate build
|
||||
workingDirectory: tests/mesonTest
|
||||
- script: ninja -C tests/mesonTest/build
|
||||
displayName: Build with Ninja
|
||||
- script: ./tests/mesonTest/build/main --help
|
||||
displayName: Run help
|
||||
|
||||
- job: Docker
|
||||
variables:
|
||||
cli11.single: OFF
|
||||
pool:
|
||||
vmImage: "ubuntu-latest"
|
||||
strategy:
|
||||
matrix:
|
||||
gcc9:
|
||||
containerImage: gcc:9
|
||||
cli11.std: 17
|
||||
cli11.options:
|
||||
-DCMAKE_CXX_FLAGS="-Wstrict-overflow=5"
|
||||
-DCLI11_ENABLE_EXTRA_VALIDATORS=1
|
||||
gcc11:
|
||||
containerImage: gcc:11
|
||||
cli11.std: 20
|
||||
cli11.options:
|
||||
-DCMAKE_CXX_FLAGS="-Wredundant-decls -Wconversion"
|
||||
-DCLI11_DISABLE_EXTRA_VALIDATORS=1
|
||||
gcc7:
|
||||
containerImage: gcc:7
|
||||
cli11.std: 14
|
||||
cli11.options: -DCMAKE_CXX_FLAGS="-Wconversion"
|
||||
gcc4.8:
|
||||
containerImage: helics/buildenv:gcc4-8-builder
|
||||
cli11.std: 11
|
||||
clang3.4:
|
||||
containerImage: silkeh/clang:3.4
|
||||
cli11.std: 11
|
||||
cli11.options:
|
||||
-DCLI11_WARNINGS_AS_ERRORS=OFF -DCLI11_DISABLE_EXTRA_VALIDATORS=1
|
||||
clang8_17:
|
||||
containerImage: silkeh/clang:8
|
||||
cli11.std: 17
|
||||
cli11.options:
|
||||
-DCLI11_FORCE_LIBCXX=ON -DCLI11_ENABLE_EXTRA_VALIDATORS=1
|
||||
clang10_20:
|
||||
containerImage: silkeh/clang:10
|
||||
cli11.std: 20
|
||||
cli11.options:
|
||||
-DCLI11_FORCE_LIBCXX=ON -DCMAKE_CXX_FLAGS=-std=c++20
|
||||
-DCLI11_ENABLE_EXTRA_VALIDATORS=1
|
||||
container: $[ variables['containerImage'] ]
|
||||
steps:
|
||||
- template: .ci/azure-cmake.yml
|
||||
- template: .ci/azure-build.yml
|
||||
- template: .ci/azure-test.yml
|
||||
@@ -0,0 +1,20 @@
|
||||
# Node rules:
|
||||
## Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
## Dependency directory
|
||||
## Commenting this out is preferred by some people, see
|
||||
## https://docs.npmjs.com/misc/faq#should-i-check-my-node_modules-folder-into-git
|
||||
node_modules
|
||||
|
||||
# Book build output
|
||||
_book
|
||||
|
||||
# eBook build output
|
||||
*.epub
|
||||
*.mobi
|
||||
*.pdf
|
||||
|
||||
a.out
|
||||
|
||||
*build*
|
||||
@@ -0,0 +1,13 @@
|
||||
# Copyright (c) 2017-2026, University of Cincinnati, developed by Henry Schreiner
|
||||
# under NSF AWARD 1414736 and by the respective contributors.
|
||||
# All rights reserved.
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
set(book_sources README.md)
|
||||
|
||||
file(
|
||||
GLOB book_chapters
|
||||
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
chapters/*.md)
|
||||
add_custom_target(cli_book SOURCES ${book_sources} ${book_chapters})
|
||||
@@ -0,0 +1,35 @@
|
||||
# Guide
|
||||
|
||||
This guide walks through CLI11 chapter by chapter, from a first simple program
|
||||
to subcommands, configuration files, and the library internals. Read it in
|
||||
order, or jump to the chapter you need:
|
||||
|
||||
- [Installation](chapters/installation.md)
|
||||
- [The Basics](chapters/basics.md)
|
||||
- [Adding Flags](chapters/flags.md)
|
||||
- [Options](chapters/options.md)
|
||||
- [Validators](chapters/validators.md)
|
||||
- [Subcommands and the App](chapters/subcommands.md)
|
||||
- [Option groups](chapters/option-groups.md)
|
||||
- [Accepting configure files](chapters/config.md)
|
||||
- [Formatting help output](chapters/formatting.md)
|
||||
- [Unicode support](chapters/unicode.md)
|
||||
- [Using CLI11 in a Toolkit](chapters/toolkits.md)
|
||||
- [Advanced topics](chapters/advanced-topics.md)
|
||||
- [CLI11 Internals](chapters/internals.md)
|
||||
|
||||
## Examples
|
||||
|
||||
- [Making a git clone](chapters/an-advanced-example.md) — a walkthrough of a
|
||||
larger program
|
||||
- [Using CLI11 as a C++20 module](chapters/modules-example.md) — complete
|
||||
`import cli11;` programs, with an `import std;` variant
|
||||
- [Example programs](../examples) — small, complete programs you can copy
|
||||
|
||||
A rendered version is part of the
|
||||
[CLI11 documentation](https://cliutils.github.io/CLI11/).
|
||||
|
||||
> Feel free to contribute to [this documentation here][cli11guide] if something
|
||||
> can be improved!
|
||||
|
||||
[cli11guide]: https://github.com/CLIUtils/CLI11/tree/main/book
|
||||
@@ -0,0 +1,148 @@
|
||||
# Advanced topics {#book-advanced-topics}
|
||||
|
||||
## Environment variables
|
||||
|
||||
Environment variables can be used to fill in the value of an option:
|
||||
|
||||
```cpp
|
||||
std::string opt;
|
||||
app.add_option("--my_option", opt)->envname("MY_OPTION");
|
||||
```
|
||||
|
||||
If not given on the command line, the environment variable will be checked and
|
||||
read from if it exists. All the standard tools, like default and required, work
|
||||
as expected. If passed on the command line, this will ignore the environment
|
||||
variable.
|
||||
|
||||
## Needs/excludes
|
||||
|
||||
You can set a network of requirements. For example, if flag a needs flag b but
|
||||
cannot be given with flag c, that would be:
|
||||
|
||||
```cpp
|
||||
auto a = app.add_flag("-a");
|
||||
auto b = app.add_flag("-b");
|
||||
auto c = app.add_flag("-c");
|
||||
|
||||
a->needs(b);
|
||||
a->excludes(c);
|
||||
```
|
||||
|
||||
CLI11 will make sure your network of requirements makes sense, and will throw an
|
||||
error immediately if it does not.
|
||||
|
||||
## Custom option callbacks
|
||||
|
||||
You can make a completely generic option with a custom callback. For example, if
|
||||
you wanted to add a complex number (already exists, so please don't actually do
|
||||
this):
|
||||
|
||||
```cpp
|
||||
CLI::Option *
|
||||
add_option(CLI::App &app, std::string name, cx &variable, std::string description = "", bool defaulted = false) {
|
||||
CLI::callback_t fun = [&variable](CLI::results_t res) {
|
||||
double x, y;
|
||||
bool worked = CLI::detail::lexical_cast(res[0], x) && CLI::detail::lexical_cast(res[1], y);
|
||||
if(worked)
|
||||
variable = cx(x, y);
|
||||
return worked;
|
||||
};
|
||||
|
||||
CLI::Option *opt = app.add_option(name, fun, description, defaulted);
|
||||
opt->type_name("COMPLEX");
|
||||
opt->type_size(2);
|
||||
if(defaulted) {
|
||||
std::stringstream out;
|
||||
out << variable;
|
||||
opt->default_str(out.str());
|
||||
}
|
||||
return opt;
|
||||
}
|
||||
```
|
||||
|
||||
Then you could use it like this:
|
||||
|
||||
```cpp
|
||||
std::complex<double> comp{0, 0};
|
||||
add_option(app, "-c,--complex", comp);
|
||||
```
|
||||
|
||||
## Timers
|
||||
|
||||
`CLI/Timer.hpp` is independent of the rest of the library and is not part of
|
||||
`CLI11.hpp` single file header. It times a block of code:
|
||||
|
||||
```cpp
|
||||
#include <CLI/Timer.hpp>
|
||||
|
||||
{
|
||||
CLI::AutoTimer timer{"My Long Process", CLI::Timer::Big};
|
||||
some_long_running_process();
|
||||
}
|
||||
```
|
||||
|
||||
An `AutoTimer` prints the elapsed time when it is destroyed at the end of the
|
||||
block. A plain `Timer` does not; use `to_string()` or stream it when you want
|
||||
the value. The first argument is the title, which defaults to `Timer`. The
|
||||
second is the print function, either the provided `CLI::Timer::Simple` (the
|
||||
default) or `CLI::Timer::Big`, or any function that takes the title and the time
|
||||
as strings and returns the line to print. `time_it(func)` runs a function
|
||||
repeatedly and reports the average.
|
||||
|
||||
## Custom converters
|
||||
|
||||
You can add your own converters to allow CLI11 to accept more option types in
|
||||
the standard calls. These can only be used for "single" size options (so
|
||||
complex, vector, etc. are a separate topic). If you set up a custom
|
||||
`istringstream& operator>>` overload before include CLI11, you can support
|
||||
different conversions. If you place this in the CLI namespace, you can even keep
|
||||
this from affecting the rest of your code. Note that `std::optional` is
|
||||
supported natively and does not need a custom converter. The next section shows
|
||||
a complete example.
|
||||
|
||||
## Custom converters and type names: std::chrono example
|
||||
|
||||
An example of adding a custom converter and typename for `std::chrono` follows:
|
||||
|
||||
```cpp
|
||||
namespace CLI
|
||||
{
|
||||
template <typename T, typename R>
|
||||
std::istringstream &operator>>(std::istringstream &in, std::chrono::duration<T,R> &val)
|
||||
{
|
||||
T v;
|
||||
in >> v;
|
||||
val = std::chrono::duration<T,R>(v);
|
||||
return in;
|
||||
}
|
||||
|
||||
template <typename T, typename R>
|
||||
std::stringstream &operator<<(std::stringstream &in, std::chrono::duration<T,R> &val)
|
||||
{
|
||||
in << val.count();
|
||||
return in;
|
||||
}
|
||||
}
|
||||
|
||||
#include <CLI/CLI.hpp>
|
||||
|
||||
namespace CLI
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
template <>
|
||||
constexpr const char *type_name<std::chrono::hours>()
|
||||
{
|
||||
return "TIME [H]";
|
||||
}
|
||||
|
||||
template <>
|
||||
constexpr const char *type_name<std::chrono::minutes>()
|
||||
{
|
||||
return "TIME [MIN]";
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Thanks to Olivier Hartmann for the example.
|
||||
@@ -0,0 +1,39 @@
|
||||
# Making a git clone {#book-an-advanced-example}
|
||||
|
||||
Let's try our hand at a little `git` clone, called `geet`. It will just print
|
||||
it's intent, rather than running actual code, since it's just a demonstration.
|
||||
Let's start by adding an app and requiring 1 subcommand to run:
|
||||
|
||||
\snippet geet.cpp Intro
|
||||
|
||||
Now, let's define the first subcommand, `add`, along with a few options:
|
||||
|
||||
\snippet geet.cpp Add
|
||||
|
||||
Now, let's add `commit`:
|
||||
|
||||
\snippet geet.cpp Commit
|
||||
|
||||
All that's need now is the parse call. We'll print a little message after the
|
||||
code runs, and then return:
|
||||
|
||||
\snippet geet.cpp Parse
|
||||
|
||||
[Source code](https://github.com/CLIUtils/CLI11/tree/main/book/code/geet.cpp)
|
||||
|
||||
If you compile and run:
|
||||
|
||||
```text
|
||||
c++ -std=c++11 geet.cpp -o geet
|
||||
```
|
||||
|
||||
You'll see it behaves pretty much like `git`.
|
||||
|
||||
## Multi-file App parse code
|
||||
|
||||
This example could be made much nicer if it was split into files, one per
|
||||
subcommand. If you simply use shared pointers instead of raw values in the
|
||||
lambda capture, you can tie the lifetime to the lambda function lifetime. CLI11
|
||||
has a
|
||||
[multifile example](https://github.com/CLIUtils/CLI11/tree/main/examples/subcom_in_files)
|
||||
in its example folder.
|
||||
@@ -0,0 +1,41 @@
|
||||
# The Basics {#book-basics}
|
||||
|
||||
The simplest CLI11 program looks like this:
|
||||
|
||||
\include simplest.cpp
|
||||
|
||||
The first line includes the library; this uses the normal headers of the full
|
||||
edition (see [Selecting an edition](@ref book-installation)). With the single
|
||||
file edition you would include `CLI11.hpp` instead.
|
||||
|
||||
After entering the main function, you'll see that a `CLI::App` object is
|
||||
created. This is the basis for all interactions with the library. You could
|
||||
optionally provide a description for your app here.
|
||||
|
||||
A normal CLI11 application would define some flags and options next. This is a
|
||||
simplest possible example, so we'll go on.
|
||||
|
||||
The macro `CLI11_PARSE` just runs five simple lines. This internally runs
|
||||
`app.parse(argc, argv)`, which takes the command line info from C++ and parses
|
||||
it. If there is an error, it throws a `ParseError`; if you catch it, you can use
|
||||
`app.exit` with the error as an argument to print a nice message and produce the
|
||||
correct return code for your application.
|
||||
|
||||
If you just use `app.parse` directly, your application will still work, but the
|
||||
stack will not be correctly unwound since you have an uncaught exception, and
|
||||
the command line output will be cluttered, especially for help.
|
||||
|
||||
For this (and most of the examples in this book) we will assume that the CLI11
|
||||
`include` directory is on the compiler include path and that we are creating an
|
||||
output executable `a.out` on a macOS or Linux system. (With the single file
|
||||
edition, you could instead place `CLI11.hpp` next to your source code and skip
|
||||
the include path.) The commands to compile and test this example would be:
|
||||
|
||||
```text
|
||||
$ g++ -std=c++11 simplest.cpp -I/path/to/CLI11/include
|
||||
$ ./a.out -h
|
||||
Usage: ./a.out [OPTIONS]
|
||||
|
||||
Options:
|
||||
-h,--help Print this help message and exit
|
||||
```
|
||||
@@ -0,0 +1,548 @@
|
||||
# Accepting configure files {#book-config}
|
||||
|
||||
## Reading a configure file
|
||||
|
||||
You can tell your app to allow configure files with `set_config("--config")`.
|
||||
There are four arguments: the first is the option name. If empty, it will clear
|
||||
the config flag. The second item is the default file name. If that is specified,
|
||||
the config will try to read that file. The third item is the help string, with a
|
||||
reasonable default, and the final argument is a boolean (default: false) that
|
||||
indicates that the configuration file is required and an error will be thrown if
|
||||
the file is not found and this is set to true. The option pointer returned by
|
||||
`set_config` is the same type as returned by `add_option` and all modifiers
|
||||
including validators, and checks are valid.
|
||||
|
||||
### Adding a default path
|
||||
|
||||
if it is desired that config files be searched for a in a default path the
|
||||
`CLI::FileOnDefaultPath` transform can be used.
|
||||
|
||||
```cpp
|
||||
app.set_config("--config")->transform(CLI::FileOnDefaultPath("/default_path/"));
|
||||
```
|
||||
|
||||
This will allow specified files to either exist as given or on a specified
|
||||
default path.
|
||||
|
||||
```cpp
|
||||
app.set_config("--config")
|
||||
->transform(CLI::FileOnDefaultPath("/default_path/"))
|
||||
->transform(CLI::FileOnDefaultPath("/default_path2/",false));
|
||||
```
|
||||
|
||||
Multiple default paths can be specified through this mechanism. The last
|
||||
transform given is executed first so the error return must be disabled so it can
|
||||
be chained to the first. The same effect can be achieved though the or(`|`)
|
||||
operation with validators
|
||||
|
||||
```cpp
|
||||
app.set_config("--config")
|
||||
->transform(CLI::FileOnDefaultPath("/default_path2/") | CLI::FileOnDefaultPath("/default_path/"));
|
||||
```
|
||||
|
||||
### Extra fields
|
||||
|
||||
Sometimes configuration files are used for multiple purposes so CLI11 allows
|
||||
options on how to deal with extra fields
|
||||
|
||||
```cpp
|
||||
app.allow_config_extras(true);
|
||||
```
|
||||
|
||||
will allow capture the extras in the extras field of the app. (NOTE: This also
|
||||
sets the `allow_extras` in the app to true)
|
||||
|
||||
```cpp
|
||||
app.allow_config_extras(false);
|
||||
```
|
||||
|
||||
will generate an error if there are any extra fields for slightly finer control
|
||||
there is a scoped enumeration of the modes or
|
||||
|
||||
```cpp
|
||||
app.allow_config_extras(CLI::config_extras_mode::ignore);
|
||||
```
|
||||
|
||||
will completely ignore extra parameters in the config file. This mode is the
|
||||
default.
|
||||
|
||||
```cpp
|
||||
app.allow_config_extras(CLI::config_extras_mode::capture);
|
||||
```
|
||||
|
||||
will store the unrecognized options in the app extras fields. This option is the
|
||||
closest equivalent to `app.allow_config_extras(true);` with the exception that
|
||||
it does not also set the `allow_extras` flag so using this option without also
|
||||
setting `allow_extras(true)` will generate an error which may or may not be the
|
||||
desired behavior.
|
||||
|
||||
```cpp
|
||||
app.allow_config_extras(CLI::config_extras_mode::error);
|
||||
```
|
||||
|
||||
is equivalent to `app.allow_config_extras(false);`
|
||||
|
||||
```cpp
|
||||
app.allow_config_extras(CLI::config_extras_mode::ignore_all);
|
||||
```
|
||||
|
||||
will completely ignore any mismatches, extras, or other issues with the config
|
||||
file
|
||||
|
||||
Config file extras are stored in the remaining output as two components. The
|
||||
first is the name of the field including subcommands using dot notation the
|
||||
second (or more) are the argument fields.
|
||||
|
||||
### Getting the used configuration file name
|
||||
|
||||
If it is needed to get the configuration file name used this can be obtained via
|
||||
`app.get_config_ptr()->as<std::string>()` or
|
||||
`app["--config"]->as<std::string>()` assuming `--config` was the configuration
|
||||
option name.
|
||||
|
||||
### Order of precedence
|
||||
|
||||
By default if multiple configuration files are given they are read in reverse
|
||||
order. With the last one given taking precedence over the earlier ones. This
|
||||
behavior can be changed through the `multi_option_policy`. For example:
|
||||
|
||||
```cpp
|
||||
app.set_config("--config")
|
||||
->multi_option_policy(CLI::MultiOptionPolicy::TakeAll);
|
||||
```
|
||||
|
||||
will read the files in the order given, which may be useful in some
|
||||
circumstances. Using `CLI::MultiOptionPolicy::TakeLast` would work similarly
|
||||
getting the last `N` files given. The default policy for config options is
|
||||
`CLI::MultiOptionPolicy::Reverse` which takes the last expected `N` and reverses
|
||||
them so the last option given is given precedence.
|
||||
|
||||
## Configure file format
|
||||
|
||||
Here is an example configuration file, in
|
||||
[TOML](https://github.com/toml-lang/toml) format:
|
||||
|
||||
```toml
|
||||
# Comments are supported, using a #
|
||||
# The default section is [default], case-insensitive
|
||||
|
||||
value = 1
|
||||
str = "A string"
|
||||
vector = [1,2,3]
|
||||
|
||||
# Section map to subcommands
|
||||
[subcommand]
|
||||
in_subcommand = "Wow"
|
||||
[subcommand.sub]
|
||||
subcommand = true # could also be give as sub.subcommand=true
|
||||
```
|
||||
|
||||
Spaces before and after the name and argument are ignored. Multiple arguments
|
||||
are separated by spaces. One set of quotes will be removed, preserving spaces
|
||||
(the same way the command line works). Boolean options can be `true`, `on`, `1`,
|
||||
`y`, `t`, `+`, `yes`, `enable`; or `false`, `off`, `0`, `no`, `n`, `f`, `-`,
|
||||
`disable`, (case-insensitive). Sections (and `.` separated names) are treated as
|
||||
subcommands (note: this does not necessarily mean that subcommand was passed, it
|
||||
just sets the "defaults". If a subcommand is set to `configurable` then passing
|
||||
the subcommand using `[sub]` in a configuration file will trigger the
|
||||
subcommand.)
|
||||
|
||||
CLI11 also supports configuration file in INI format.
|
||||
|
||||
```ini
|
||||
; Comments are supported, using a ;
|
||||
; The default section is [default], case-insensitive
|
||||
|
||||
value = 1
|
||||
str = "A string"
|
||||
vector = 1 2 3
|
||||
|
||||
; Section map to subcommands
|
||||
[subcommand]
|
||||
in_subcommand = Wow
|
||||
sub.subcommand = true
|
||||
```
|
||||
|
||||
The main differences are in vector notation and comment character. Note: CLI11
|
||||
is not a full TOML parser as it just reads values as strings. It is possible
|
||||
(but not recommended) to mix notation.
|
||||
|
||||
### Multi-line strings
|
||||
|
||||
The default config file parser supports multi-line strings like the toml
|
||||
standard [TOML](https://toml.io/en/). It also supports multiline comments like
|
||||
python doc strings.
|
||||
|
||||
```toml
|
||||
"""
|
||||
this is a multiline
|
||||
comment
|
||||
"""
|
||||
|
||||
""" this is also
|
||||
a multiline comment"""
|
||||
|
||||
''' and so is
|
||||
this
|
||||
'''
|
||||
|
||||
value = 1
|
||||
str = """
|
||||
this is a multiline string value
|
||||
the first \n is removed and so is the last
|
||||
"""
|
||||
|
||||
str2 = ''' this is also a mu-
|
||||
ltiline value '''
|
||||
|
||||
str3 = """\
|
||||
a line continuation \
|
||||
will skip \
|
||||
all white space between the '\' \
|
||||
and the next non-whitespace character \
|
||||
making this into a single line
|
||||
"""
|
||||
|
||||
```
|
||||
|
||||
The key is that the closing of the multiline string must be at the end of a line
|
||||
and match the starting 3 quote sequence. Multiline sequences using `"""` allow
|
||||
escape sequences. Following [TOML](https://toml.io/en/v1.0.0#string) with the
|
||||
addition of allowing '\0' for a null character, and binary Strings described in
|
||||
the next section. This same formatting also applies to single line strings.
|
||||
Multiline strings are not allowed as part of an array.
|
||||
|
||||
### Binary Strings
|
||||
|
||||
Config files have a binary conversion capability, this is mainly to support
|
||||
writing config files but can be used by user generated files as well. Strings
|
||||
with the form `B"(XXXXX)"` will convert any characters inside the parenthesis
|
||||
with the form `\xHH` to the equivalent binary value. The HH are hexadecimal
|
||||
characters. Characters not in this form will be translated as given. If argument
|
||||
values with unprintable characters are used to generate a config file this
|
||||
binary form will be used in the output string.
|
||||
|
||||
### multiline vector
|
||||
|
||||
Vector arguments can also be multiline
|
||||
|
||||
```toml
|
||||
#multiline vector format
|
||||
vector = [
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
6
|
||||
]
|
||||
```
|
||||
|
||||
### vector of vector inputs
|
||||
|
||||
It is possible to specify vector of vector inputs in config file. This can be
|
||||
done in a few different ways
|
||||
|
||||
```toml
|
||||
# Examples of vector of vector inputs in config
|
||||
|
||||
# this example is how config_to_str writes it out
|
||||
vector1 = [1,2,3,"",4,5,6]
|
||||
|
||||
# alternative with vector separator sequence
|
||||
vector2 = [1,2,3,"%%",4,5,6]
|
||||
|
||||
# multiline format
|
||||
vector3 = [1,2,3]
|
||||
vector3 = [4,5,6]
|
||||
|
||||
|
||||
```
|
||||
|
||||
The `%%` is ignored in multiline format if the inject_separator modifier on the
|
||||
option is set to false, thus for vector 3 if the option is storing to a single
|
||||
vector all the elements will be in that vector.
|
||||
|
||||
For config file multiple sequential duplicate variable names are treated as if
|
||||
they are a vector input, with possible separator insertion in the case of
|
||||
multiple input vectors.
|
||||
|
||||
The config parser has a modifier
|
||||
|
||||
```C++
|
||||
app.get_config_formatter_base()->allowDuplicateFields();
|
||||
```
|
||||
|
||||
This modification will insert the separator between each line even if not
|
||||
sequential. This allows an input option to be configured with multiple lines.
|
||||
|
||||
```toml
|
||||
# Examples of vector of vector inputs in config
|
||||
|
||||
# this example is how config_to_str writes it out
|
||||
vector1 = [a,v,"[]"]
|
||||
```
|
||||
|
||||
The field insertion has a special processing for duplicate characters starting
|
||||
with "[[" in which case the `"[]"` gets translated to `[[]]` before getting
|
||||
passed into the option which converts it back into the correct string. This can
|
||||
also be used on the command line to handle unusual parsing situation with
|
||||
brackets.
|
||||
|
||||
### Argument With Brackets
|
||||
|
||||
There is an edge case with actual strings that are surrounded by brackets. For
|
||||
example if the string "[]" needed to be passed. this would normally trigger the
|
||||
bracket processing and result in an empty vector. In this case it can be
|
||||
enclosed in quotes and should be handled correctly.
|
||||
|
||||
## Multiple configuration files
|
||||
|
||||
If it is desired that multiple configuration be allowed. Use
|
||||
|
||||
```cpp
|
||||
app.set_config("--config")->expected(1, X);
|
||||
```
|
||||
|
||||
Where X is some positive integer and will allow up to `X` configuration files to
|
||||
be specified by separate `--config` arguments.
|
||||
|
||||
## Writing out a configure file
|
||||
|
||||
To print a configuration file from the passed arguments, use one of the
|
||||
following overloads:
|
||||
|
||||
- `.config_to_str()`: Print active values only.
|
||||
- `.config_to_str(bool default_also, bool write_description = false)`: Print
|
||||
active values, or include defaulted arguments if `default_also` is `true`.
|
||||
This overload will likely be deprecated in a future release in favor of the
|
||||
`CLI::ConfigOutputMode` overload.
|
||||
- `.config_to_str(CLI::ConfigOutputMode, bool write_description = false)`: 🆕
|
||||
Specify how configuration output should be generated.
|
||||
- `CLI::ConfigOutputMode::Active`: print active values only. Same as
|
||||
`.config_to_str()`.
|
||||
- `CLI::ConfigOutputMode::AllDefaults`: include defaulted arguments for the
|
||||
app and all subcommands. Same as `.config_to_str(true, ...)`.
|
||||
- `CLI::ConfigOutputMode::ActiveSubcommandDefaults`: include defaulted
|
||||
arguments for the app and active subcommands, while omitting defaults from
|
||||
inactive subcommands.
|
||||
|
||||
The `write_description` argument will include option descriptions and the App
|
||||
description.
|
||||
|
||||
Subcommands are written in one of two forms. A subcommand set to
|
||||
`configurable()` gets its own section, such as `[sub]`, if the subcommand was
|
||||
passed on the command line or the mode is `CLI::ConfigOutputMode::AllDefaults`.
|
||||
In all other conditions, the options of the subcommand are written with dotted
|
||||
names, such as `sub.value=1`. Nested subcommands use the parent names as a
|
||||
prefix, such as `[sub.subsub]` or `sub.subsub.value=1`.
|
||||
|
||||
```cpp
|
||||
CLI::App app;
|
||||
int a{1}, b{1};
|
||||
app.add_subcommand("plain")->add_option("--value", a)->capture_default_str();
|
||||
app.add_subcommand("configurable")->configurable()
|
||||
->add_option("--value", b)->capture_default_str();
|
||||
std::cout<<app.config_to_str(CLI::ConfigOutputMode::AllDefaults);
|
||||
```
|
||||
|
||||
```toml
|
||||
plain.value=1
|
||||
[configurable]
|
||||
value=1
|
||||
```
|
||||
|
||||
```cpp
|
||||
CLI::App app;
|
||||
app.add_option(...);
|
||||
// several other options
|
||||
CLI11_PARSE(app, argc, argv);
|
||||
//the config printout should be after the parse to capture the given arguments
|
||||
std::cout<<app.config_to_str(true,true);
|
||||
```
|
||||
|
||||
if a prefix is needed to print before the options, for example to print a config
|
||||
for just a subcommand, the config formatter can be obtained directly.
|
||||
|
||||
```cpp
|
||||
auto fmtr=app.get_config_formatter();
|
||||
//std::string to_config(const App *app, bool default_also, bool write_description, std::string prefix)
|
||||
fmtr->to_config(&app,true,true,"sub.");
|
||||
//prefix can be used to set a prefix before each argument, like "sub."
|
||||
```
|
||||
|
||||
The formatter also has an overload taking `CLI::ConfigOutputMode`:
|
||||
|
||||
```cpp
|
||||
auto fmtr=app.get_config_formatter();
|
||||
//std::string to_config(const App *app, CLI::ConfigOutputMode mode, bool write_description, std::string prefix)
|
||||
fmtr->to_config(&app,CLI::ConfigOutputMode::ActiveSubcommandDefaults,true,"sub.");
|
||||
```
|
||||
|
||||
### Customization of configure file output
|
||||
|
||||
The default config parser/generator has some customization points that allow
|
||||
variations on the TOML format. The default formatter has a base configuration
|
||||
that matches the TOML format. It defines 5 characters that define how different
|
||||
aspects of the configuration are handled. You must use
|
||||
`get_config_formatter_base()` to have access to these fields
|
||||
|
||||
```cpp
|
||||
/// the character used for comments
|
||||
char commentChar = '#';
|
||||
/// the character used to start an array '\0' is a default to not use
|
||||
char arrayStart = '[';
|
||||
/// the character used to end an array '\0' is a default to not use
|
||||
char arrayEnd = ']';
|
||||
/// the character used to separate elements in an array
|
||||
char arraySeparator = ',';
|
||||
/// the character used separate the name from the value
|
||||
char valueDelimiter = '=';
|
||||
/// the character to use around strings
|
||||
char stringQuote = '"';
|
||||
/// the character to use around single characters and literal strings
|
||||
char literalQuote = '\'';
|
||||
/// the maximum number of layers to allow
|
||||
uint8_t maximumLayers{255};
|
||||
/// the separator used to separator parent layers
|
||||
char parentSeparatorChar{'.'};
|
||||
/// comment default values
|
||||
bool commentDefaultsBool = false;
|
||||
/// specify the config reader should collapse repeated field names to a single vector
|
||||
bool allowMultipleDuplicateFields{false};
|
||||
/// Specify the configuration index to use for arrayed sections
|
||||
int16_t configIndex{-1};
|
||||
/// Specify the configuration section that should be used
|
||||
std::string configSection;
|
||||
```
|
||||
|
||||
These can be modified via setter functions
|
||||
|
||||
- `ConfigBase *comment(char cchar)`: Specify the character to start a comment
|
||||
block
|
||||
- `ConfigBase *arrayBounds(char aStart, char aEnd)`: Specify the start and end
|
||||
characters for an array
|
||||
- `ConfigBase *arrayDelimiter(char aSep)`: Specify the delimiter character for
|
||||
an array
|
||||
- `ConfigBase *valueSeparator(char vSep)`: Specify the delimiter between a name
|
||||
and value
|
||||
- `ConfigBase *quoteCharacter(char qString, char literalChar)` :specify the
|
||||
characters to use around strings and single characters
|
||||
- `ConfigBase *commentDefaults(bool comDef)` : set to true to comment lines with
|
||||
a default value
|
||||
- `ConfigBase *allowDuplicateFields(bool value)` :set to true to allow duplicate
|
||||
fields to be merged even if not sequential
|
||||
- `ConfigBase *maxLayers(uint8_t layers)` : specify the maximum number of parent
|
||||
layers to process. This is useful to limit processing for larger config files
|
||||
- `ConfigBase *parentSeparator(char sep)` : specify the character to separate
|
||||
parent layers from options
|
||||
- `ConfigBase *section(const std::string §ionName)` : specify the section
|
||||
name to use to get the option values, only this section will be processed
|
||||
- `ConfigBase *index(int16_t sectionIndex)` : specify an index section to use
|
||||
for processing if multiple TOML sections of the same name are present
|
||||
`[[section]]`
|
||||
|
||||
For example, to specify reading a configure file that used `:` to separate name
|
||||
and values:
|
||||
|
||||
```cpp
|
||||
auto config_base=app.get_config_formatter_base();
|
||||
config_base->valueSeparator(':');
|
||||
```
|
||||
|
||||
The default configuration file will read INI files, but will write out files in
|
||||
the TOML format. To specify outputting INI formatted files use
|
||||
|
||||
```cpp
|
||||
app.config_formatter(std::make_shared<CLI::ConfigINI>());
|
||||
```
|
||||
|
||||
which makes use of a predefined modification of the ConfigBase class which TOML
|
||||
also uses. If a custom formatter is used that is not inheriting from the from
|
||||
ConfigBase class `get_config_formatter_base()` will return a nullptr if RTTI is
|
||||
on (usually the default), or garbage if RTTI is off, so some care must be
|
||||
exercised in its use with custom configurations.
|
||||
|
||||
## Custom formats
|
||||
|
||||
You can invent a custom format and set that instead of the default INI
|
||||
formatter. You need to inherit from `CLI::Config` and implement the following
|
||||
two functions:
|
||||
|
||||
```cpp
|
||||
std::string to_config(const CLI::App *app, bool default_also, bool, std::string) const;
|
||||
std::vector<CLI::ConfigItem> from_config(std::istream &input) const;
|
||||
```
|
||||
|
||||
The `CLI::ConfigItem`s that you return are simple structures with a name, a
|
||||
vector of parents, and a vector of results. A optionally customizable `to_flag`
|
||||
method on the formatter lets you change what happens when a ConfigItem turns
|
||||
into a flag.
|
||||
|
||||
Finally, set your new class as new config formatter:
|
||||
|
||||
```cpp
|
||||
app.config_formatter(std::make_shared<NewConfig>());
|
||||
```
|
||||
|
||||
See
|
||||
[`examples/json.cpp`](https://github.com/CLIUtils/CLI11/blob/main/examples/json.cpp)
|
||||
for a complete JSON config example.
|
||||
|
||||
### Trivial JSON configuration example
|
||||
|
||||
```JSON
|
||||
{
|
||||
"test": 56,
|
||||
"testb": "test",
|
||||
"flag": true
|
||||
}
|
||||
```
|
||||
|
||||
The parser can handle these structures with only a minor tweak
|
||||
|
||||
```cpp
|
||||
app.get_config_formatter_base()->valueSeparator(':');
|
||||
```
|
||||
|
||||
The open and close brackets must be on a separate line and the comma gets
|
||||
interpreted as an array separator but since no values are after the comma they
|
||||
get ignored as well. This will not support multiple layers or sections or any
|
||||
other moderately complex JSON, but can work if the input file is simple.
|
||||
|
||||
## Triggering Subcommands
|
||||
|
||||
Configuration files can be used to trigger subcommands if a subcommand is set to
|
||||
configure. By default configuration file just set the default values of a
|
||||
subcommand. But if the `configure()` option is set on a subcommand then the if
|
||||
the subcommand is utilized via a `[subname]` block in the configuration file it
|
||||
will act as if it were called from the command line. Subsubcommands can be
|
||||
triggered via `[subname.subsubname]`. Using the `[[subname]]` will be as if the
|
||||
subcommand were triggered multiple times from the command line. This
|
||||
functionality can allow the configuration file to act as a scripting file.
|
||||
|
||||
For custom configuration files this behavior can be triggered by specifying the
|
||||
parent subcommands in the structure and `++` as the name to open a new
|
||||
subcommand scope and `--` to close it. These names trigger the different
|
||||
callbacks of configurable subcommands.
|
||||
|
||||
## Stream parsing
|
||||
|
||||
In addition to the regular parse functions a
|
||||
`parse_from_stream(std::istream &input)` is available to directly parse a stream
|
||||
operator. For example to process some arguments in an already open file stream.
|
||||
The stream is fed directly in the config parser so bypasses the normal command
|
||||
line parsing.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The config file input works with any form of the option given: Long, short,
|
||||
positional, or the environment variable name. When generating a config file it
|
||||
will create an option name in following priority.
|
||||
|
||||
1. First long name
|
||||
2. First short name
|
||||
3. Positional name
|
||||
4. Environment name
|
||||
|
||||
In config files the name will be enclosed in quotes if there is any potential
|
||||
ambiguities in parsing the name.
|
||||
@@ -0,0 +1,166 @@
|
||||
# Adding Flags {#book-flags}
|
||||
|
||||
The most basic addition to a command line program is a flag. This is simply
|
||||
something that does not take any arguments. Adding a flag in CLI11 is done in
|
||||
one of three ways.
|
||||
|
||||
## Boolean flags
|
||||
|
||||
The simplest way to add a flag is probably a boolean flag:
|
||||
|
||||
```cpp
|
||||
bool my_flag{false};
|
||||
app.add_flag("-f", my_flag, "Optional description");
|
||||
```
|
||||
|
||||
This will bind the flag `-f` to the boolean `my_flag`. After the parsing step,
|
||||
`my_flag` will be `false` if the flag was not found on the command line, or
|
||||
`true` if it was. The flag is allowed any number of times, and the last value
|
||||
given wins; bool flags always use `CLI::MultiOptionPolicy::TakeLast` (this is
|
||||
not inherited from the parent defaults, since allowing repeats is often useful
|
||||
even if you don't want other options to allow multiple values). If you want
|
||||
passing something like `./my_app -f -f` or `./my_app -ff` to be an error, use
|
||||
`->multi_option_policy(CLI::MultiOptionPolicy::Throw)`. A flag name may start
|
||||
with any character except ('-', ' ', '\n', and '!'). For long flags, after the
|
||||
first character all characters are allowed except ('=',':','{',' ', '\n'). Names
|
||||
are given as a comma separated string, with the dash or dashes. A flag can have
|
||||
as many names as you want, and afterward, using `count`, you can use any of the
|
||||
names, with dashes as needed.
|
||||
|
||||
## Integer flags
|
||||
|
||||
If you want to allow multiple flags and count their value, simply use any
|
||||
integral variables instead of a bool:
|
||||
|
||||
```cpp
|
||||
int my_flag{0};
|
||||
app.add_flag("-f", my_flag, "Optional description");
|
||||
```
|
||||
|
||||
After the parsing step, `my_flag` will contain the number of times this flag was
|
||||
found on the command line, including 0 if not found.
|
||||
|
||||
This behavior can also be controlled manually via
|
||||
`->multi_option_policy(CLI::MultiOptionPolicy::Sum)` as of version 2.2.
|
||||
|
||||
## Arbitrary type flags
|
||||
|
||||
CLI11 allows the type of the variable to assign to in the `add_flag` function to
|
||||
be any supported type. This is particularly useful in combination with
|
||||
specifying default values for flags. The allowed types include bool, int, float,
|
||||
vector, enum, or string-like.
|
||||
|
||||
### Default Flag Values
|
||||
|
||||
Flag options specified through the `add_flag*` functions allow a syntax for the
|
||||
option names to default particular options to a false value or any other value
|
||||
if some flags are passed. For example:
|
||||
|
||||
```cpp
|
||||
app.add_flag("--flag,!--no-flag",result,"help for flag");
|
||||
```
|
||||
|
||||
specifies that if `--flag` is passed on the command line result will be true or
|
||||
contain a value of 1. If `--no-flag` is passed `result` will contain false or -1
|
||||
if `result` is a signed integer type, or 0 if it is an unsigned type. An
|
||||
alternative form of the syntax is more explicit: `"--flag,--no-flag{false}"`;
|
||||
this is equivalent to the previous example. This also works for short form
|
||||
options `"-f,!-n"` or `"-f,-n{false}"`. If `variable_to_bind_to` is anything but
|
||||
an integer value the default behavior is to take the last value given, while if
|
||||
`variable_to_bind_to` is an integer type the behavior will be to sum all the
|
||||
given arguments and return the result. This can be modified if needed by
|
||||
changing the `multi_option_policy` on each flag (this is not inherited). The
|
||||
default value can be any value. For example if you wished to define a numerical
|
||||
flag:
|
||||
|
||||
```cpp
|
||||
app.add_flag("-1{1},-2{2},-3{3}",result,"numerical flag")
|
||||
```
|
||||
|
||||
using any of those flags on the command line will result in the specified number
|
||||
in the output. Similar things can be done for string values, and enumerations,
|
||||
as long as the default value can be converted to the given type.
|
||||
|
||||
## Pure flags
|
||||
|
||||
Every command that starts with `add_`, such as the flag commands, return a
|
||||
pointer to the internally stored `CLI::Option` that describes your addition. If
|
||||
you prefer, you can capture this pointer and use it, and that allows you to skip
|
||||
adding a variable to bind to entirely:
|
||||
|
||||
```cpp
|
||||
CLI::Option* my_flag = app.add_flag("-f", "Optional description");
|
||||
```
|
||||
|
||||
After parsing, you can use `my_flag->count()` to count the number of times this
|
||||
was found. You can also directly use the value (`*my_flag`) as a bool.
|
||||
`CLI::Option` will be discussed in more detail later.
|
||||
|
||||
## Callback flags
|
||||
|
||||
If you want to define a callback that runs when you make a flag, you can use
|
||||
`add_flag_function` (C++11 or newer) or `add_flag` (C++14 or newer only) to add
|
||||
a callback function. The function should have the signature
|
||||
`void(std::int64_t)`. This could be useful for a version printout, etc.
|
||||
|
||||
```cpp
|
||||
auto callback = [](std::int64_t count){std::cout << "This was called " << count << " times";};
|
||||
app.add_flag_function("-c", callback, "Optional description");
|
||||
```
|
||||
|
||||
## Aliases
|
||||
|
||||
The name string, the first item of every `add_` method, can contain as many
|
||||
short and long names as you want, separated by commas. For example,
|
||||
`"-a,--alpha,-b,--beta"` would allow any of those to be recognized on the
|
||||
command line. If you use the same name twice, or if you use the same name in
|
||||
multiple flags, CLI11 will immediately throw a `CLI::ConstructionError`
|
||||
describing your problem (it will not wait until the parsing step).
|
||||
|
||||
If you want to make an option case-insensitive, you can use the
|
||||
`->ignore_case()` method on the `CLI::Option` to do that. For example,
|
||||
|
||||
```cpp
|
||||
bool flag{false};
|
||||
app.add_flag("--flag", flag)
|
||||
->ignore_case();
|
||||
```
|
||||
|
||||
would allow the following to count as passing the flag:
|
||||
|
||||
```text
|
||||
./my_app --fLaG
|
||||
```
|
||||
|
||||
## Example
|
||||
|
||||
The following program will take several flags:
|
||||
|
||||
\snippet flags.cpp define
|
||||
|
||||
The values would be used like this:
|
||||
|
||||
\snippet flags.cpp usage
|
||||
|
||||
[Source code](https://github.com/CLIUtils/CLI11/tree/main/book/code/flags.cpp)
|
||||
|
||||
If you compile and run:
|
||||
|
||||
```text
|
||||
$ g++ -std=c++11 flags.cpp
|
||||
$ ./a.out -h
|
||||
Flag example program
|
||||
Usage: ./a.out [OPTIONS]
|
||||
|
||||
Options:
|
||||
-h,--help Print this help message and exit
|
||||
-b,--bool This is a bool flag
|
||||
-i,--int This is an int flag
|
||||
-p,--plain This is a plain flag
|
||||
|
||||
$ ./a.out -bii --plain -i
|
||||
The flags program
|
||||
Bool flag passed
|
||||
Flag int: 3
|
||||
Flag plain: 1
|
||||
```
|
||||
@@ -0,0 +1,167 @@
|
||||
# Formatting help output {#book-formatting}
|
||||
|
||||
## Customizing an existing formatter
|
||||
|
||||
In CLI11, you can control the output of the help printout in full or in part.
|
||||
The default formatter was written in such a way as to be customizable. You can
|
||||
use `app.get_formatter()` to get the current formatter. The formatter you set
|
||||
will be inherited by subcommands that are created after you set the formatter.
|
||||
|
||||
There are several configuration options that you can set:
|
||||
|
||||
| Set method | Description | Availability |
|
||||
| ------------------------------------------ | -------------------------------------------------------------------- | ------------ |
|
||||
| `column_width(width)` | The width of the columns (30) | Both |
|
||||
| `label(key, value)` | Set a label to a different value | Both |
|
||||
| `long_option_alignment_ratio(float)` | Set the alignment ratio for long options within the left column(1/3) | Both |
|
||||
| `right_column_width(std::size_t)` | Set the right column width(65) | Both |
|
||||
| `description_paragraph_width(std::size_t)` | Set the description paragraph width at the top of help(80) | Both |
|
||||
| `footer_paragraph_width(std::size_t)` | Set the footer paragraph width (80) | Both |
|
||||
| `enable_description_formatting(bool)` | enable/disable description paragraph formatting (true) | Both |
|
||||
| `enable_footer_formatting(bool)` | enable/disable footer paragraph formatting (true) | Both |
|
||||
| `enable_option_defaults(bool)` | enable/disable printing of option defaults (true) | Both |
|
||||
| `enable_option_type_names(bool)` | enable/disable printing of option types (true) | Both |
|
||||
| `enable_default_flag_values(bool)` | enable/disable printing of default flag values (true) | Both |
|
||||
|
||||
Labels will map the built in names and type names from key to value if present.
|
||||
For example, if you wanted to change the width of the columns to 40 and the
|
||||
`REQUIRED` label from `(REQUIRED)` to `(MUST HAVE)`:
|
||||
|
||||
```cpp
|
||||
app.get_formatter()->column_width(40);
|
||||
app.get_formatter()->label("REQUIRED", "(MUST HAVE)");
|
||||
```
|
||||
|
||||
Used labels are `REQUIRED`, `POSITIONALS`, `Usage`, `OPTIONS`, `SUBCOMMAND`,
|
||||
`SUBCOMMANDS`, `Env`, `Needs`,`Excludes`, and any type name such as `TEXT`,
|
||||
`INT`,`FLOAT` and others. Replacing these labels with new ones will use the
|
||||
specified words in place of the label.
|
||||
|
||||
### Customization Option Descriptions
|
||||
|
||||
Some of the control parameters are visualized in Figure 1. They manage the
|
||||
column widths and ratios of the different sections of the help
|
||||
|
||||

|
||||
|
||||
### long option alignment ratio
|
||||
|
||||
The long option alignment ratio controls the relative proportion of short to
|
||||
long option names. It must be a number between 0 and 1. values entered outside
|
||||
this range are converted into the range by absolute value or inversion. It
|
||||
defines where in the left column long options are aligned. It is a ratio of the
|
||||
column width property.
|
||||
|
||||
### formatting options
|
||||
|
||||
There are occasions where it is necessary to disable the formatting for headers
|
||||
and footers the two options `enable_description_formatting(false)` and
|
||||
`enable_footer_formatting(false)` turn off any formatting on the description and
|
||||
footer. This allows things like word art or external management of alignment and
|
||||
width. With formatting enabled the width is enforced and the paragraphs
|
||||
reflowed.
|
||||
|
||||
### Option output control
|
||||
|
||||
Additional control options manage printing of specific aspects of an option
|
||||
|
||||
```text
|
||||
OPTIONS:
|
||||
-h, --help Print this help message and exit
|
||||
--opt TEXT [DEFFFF]
|
||||
-o, --opt2 INT this is a description for opt2
|
||||
-f, -n, --opt3, --option-double FLOAT
|
||||
this is a description for option3
|
||||
--flag, --no_flag{false}
|
||||
a flag option with a negative flag as well
|
||||
```
|
||||
|
||||
The `[DEFFFF]` portion, which is the default value for options if specified can
|
||||
be turned off in the help output through `enable_option_defaults(false)`. The
|
||||
`TEXT`, `INT`, `FLOAT` or other type names can be turned off via
|
||||
`enable_option_type_names(false)`. and the `{false}` or flag default values can
|
||||
be turned off using `enable_default_flag_values(false)`.
|
||||
|
||||
An option with a delimiter shows it on the type name, so `--opt TEXT,...` takes
|
||||
values such as `a,b,c`. A trailing `...` is separate, it means the option also
|
||||
takes values separated by spaces. The delimiter is part of the type name, so
|
||||
`enable_option_type_names(false)` hides it as well.
|
||||
|
||||
## Subclassing
|
||||
|
||||
You can further configure pieces of the code while still keeping most of the
|
||||
formatting intact by subclassing either formatter and replacing any of the
|
||||
methods with your own. The formatters use virtual functions most places, so you
|
||||
are free to add or change anything about them. For example, if you wanted to
|
||||
remove the info that shows up between the option name and the description:
|
||||
|
||||
```cpp
|
||||
class MyFormatter : public CLI::Formatter {
|
||||
public:
|
||||
std::string make_option_opts(const CLI::Option *) const override {return "";}
|
||||
};
|
||||
app.formatter(std::make_shared<MyFormatter>());
|
||||
```
|
||||
|
||||
Look at the class definitions in `FormatterFwd.hpp` or the method definitions in
|
||||
`impl/Formatter_inl.hpp` to see what methods you have access to and how they are
|
||||
put together.
|
||||
|
||||
## Anatomy of a help message
|
||||
|
||||
This is a normal printout, with `<>` indicating the methods used to produce each
|
||||
line.
|
||||
|
||||
```text
|
||||
<make_description(app)>
|
||||
<make_usage(app, name)>
|
||||
<make_positionals(app)>
|
||||
<make_group("POSITIONALS", true, opts)>
|
||||
<make_groups(app, mode)>
|
||||
<make_group("Option Group 1", false, opts)>
|
||||
<make_group("Option Group 2", false, opts)>
|
||||
...
|
||||
<make_subcommands(app, mode)>
|
||||
<make_subcommand(sub1)>
|
||||
<make_subcommand(sub2)>
|
||||
<make_footer(app)>
|
||||
```
|
||||
|
||||
`make_usage` calls `make_option_usage(opt)` on all the positionals to build that
|
||||
part of the line. `make_subcommand` passes the subcommand as the app pointer.
|
||||
|
||||
The `make_groups` print the group name then call `make_option(o)` on the options
|
||||
listed in that group. The normal printout for an option looks like this:
|
||||
|
||||
```text
|
||||
make_option_opts(o)
|
||||
┌───┴────┐
|
||||
-n,--name (REQUIRED) This is a description
|
||||
└────┬────┘ └──────────┬──────────┘
|
||||
make_option_name(o,p) make_option_desc(o)
|
||||
```
|
||||
|
||||
Notes:
|
||||
|
||||
- `o` is opt pointer, `p` is true if positional.
|
||||
|
||||
## formatting callback
|
||||
|
||||
For certain cases it is useful to use a callback for the help formatting
|
||||
|
||||
```c++
|
||||
app.formatter_fn(
|
||||
[](const CLI::App *, std::string, CLI::AppFormatMode) { return std::string("This is really simple"); });
|
||||
```
|
||||
|
||||
This callback replaces the make_help call in the formatter with the callback.
|
||||
This is a wrapper around a custom formatter that just needs the main call. All
|
||||
configuration options are available but are ignored as the output is purely
|
||||
driven by the callback. The first argument is a const pointer to the App in
|
||||
question. The formatter will get a std::string usage name as the second option,
|
||||
and a AppFormatMode mode for the final option. It should return a std::string.
|
||||
The `AppFormatMode` can be `Normal`, `All`, or `Sub`, and it indicates the
|
||||
situation the help was called in. `Sub` is optional, but the default formatter
|
||||
uses it to make sure expanded subcommands are called with their own formatter
|
||||
since you can't access anything but the call operator once a formatter has been
|
||||
set.
|
||||
@@ -0,0 +1,374 @@
|
||||
# Installation {#book-installation}
|
||||
|
||||
## Single file edition
|
||||
|
||||
```cpp
|
||||
#include <CLI11.hpp>
|
||||
```
|
||||
|
||||
This example uses the single file edition of CLI11. You can download `CLI11.hpp`
|
||||
from the latest release and put it into the same folder as your source code,
|
||||
then compile this with C++ enabled. For a larger project, you can just put this
|
||||
in an include folder and you are set. This is the simplest and most
|
||||
straightforward means of including CLI11 with a project.
|
||||
|
||||
## Full edition
|
||||
|
||||
```cpp
|
||||
#include <CLI/CLI.hpp>
|
||||
```
|
||||
|
||||
If you want to use CLI11 in its full form, you can also use the original
|
||||
multiple file edition. This has an extra utility (`Timer`), and is does not
|
||||
require that you use a release. The only change to your code would be the
|
||||
include shown above.
|
||||
|
||||
### CMake support for the full edition
|
||||
|
||||
If you use CMake 3.14+ for your project (highly recommended), CLI11 comes with a
|
||||
powerful CMakeLists.txt file that was designed to also be used with
|
||||
`add_subdirectory`. You can add the repository to your code (preferably as a git
|
||||
submodule), then add the following line to your project (assuming your folder is
|
||||
called CLI11):
|
||||
|
||||
```cmake
|
||||
add_subdirectory(CLI11)
|
||||
```
|
||||
|
||||
Then, you will have a target `CLI11::CLI11` that you can link to with
|
||||
`target_link_libraries`. It will provide the include paths you need for the
|
||||
library. This is the way [GooFit](https://github.com/GooFit/GooFit) uses CLI11,
|
||||
for example.
|
||||
|
||||
You can also configure and optionally install CLI11, and CMake will create the
|
||||
necessary `lib/cmake/CLI11/CLI11Config.cmake` files, so
|
||||
`find_package(CLI11 CONFIG REQUIRED)` also works.
|
||||
|
||||
If you use conan.io, CLI11 supports that too. CLI11 also supports Meson and
|
||||
pkg-config if you are not using CMake.
|
||||
|
||||
#### Precompiled mode
|
||||
|
||||
CLI11 is header-only by default: every function is `inline`, so each translation
|
||||
unit that includes CLI11 compiles the whole library again. In a large project
|
||||
that includes CLI11 in many places, this is slow.
|
||||
|
||||
Set the CMake option `CLI11_PRECOMPILED` to compile the library one time into a
|
||||
static library instead:
|
||||
|
||||
```bash
|
||||
cmake -S . -B build -DCLI11_PRECOMPILED=ON
|
||||
```
|
||||
|
||||
The target you link against does not change. `CLI11::CLI11` is a static library
|
||||
instead of an interface library, and it applies the `CLI11_COMPILE` definition
|
||||
to your code for you:
|
||||
|
||||
```cmake
|
||||
target_link_libraries(MyTarget PRIVATE CLI11::CLI11)
|
||||
```
|
||||
|
||||
The mechanism is the `CLI11_INLINE` macro. Each public header includes a
|
||||
matching `CLI/impl/*_inl.hpp` implementation header at the end. Without
|
||||
`CLI11_COMPILE`, `CLI11_INLINE` expands to `inline` and the implementation is
|
||||
part of the header. With `CLI11_COMPILE`, `CLI11_INLINE` expands to nothing, the
|
||||
headers stop including the implementation, and the definitions must come from
|
||||
somewhere else.
|
||||
|
||||
If you do not use CLI11's CMake, you can do the same thing yourself. Define
|
||||
`CLI11_COMPILE` for all of your code, then compile one source file that includes
|
||||
the implementation headers:
|
||||
|
||||
```cpp
|
||||
// cli11_impl.cpp - compiled one time
|
||||
#include <CLI/impl/App_inl.hpp>
|
||||
#include <CLI/impl/Argv_inl.hpp>
|
||||
#include <CLI/impl/Config_inl.hpp>
|
||||
#include <CLI/impl/Encoding_inl.hpp>
|
||||
#include <CLI/impl/ExtraValidators_inl.hpp>
|
||||
#include <CLI/impl/Formatter_inl.hpp>
|
||||
#include <CLI/impl/Option_inl.hpp>
|
||||
#include <CLI/impl/Split_inl.hpp>
|
||||
#include <CLI/impl/StringTools_inl.hpp>
|
||||
#include <CLI/impl/Validators_inl.hpp>
|
||||
```
|
||||
|
||||
This is exactly what `src/Precompile.cpp` does in the CLI11 repository.
|
||||
|
||||
Two limits apply. `CLI11_PRECOMPILED` and `CLI11_SINGLE_FILE` are mutually
|
||||
exclusive, because the single header is header-only by construction. Also, the
|
||||
`impl` headers must be installed for a precompiled build to be usable from an
|
||||
install tree; set `CLI11_DISABLE_IMPL_HEADERS_INSTALL` only if you do not need
|
||||
them.
|
||||
|
||||
#### Global Headers
|
||||
|
||||
Use `CLI/*.hpp` files stored in a shared folder. You could check out the git
|
||||
repository to a system-wide folder, for example `/opt/`. With CMake, you could
|
||||
add to the include path via:
|
||||
|
||||
```bash
|
||||
if(NOT DEFINED CLI11_DIR)
|
||||
set (CLI11_DIR "/opt/CLI11" CACHE STRING "CLI11 git repository")
|
||||
endif()
|
||||
include_directories(${CLI11_DIR}/include)
|
||||
```
|
||||
|
||||
And then in the source code (adding several headers might be needed to prevent
|
||||
linker errors):
|
||||
|
||||
```cpp
|
||||
#include "CLI/App.hpp"
|
||||
#include "CLI/Formatter.hpp"
|
||||
#include "CLI/Config.hpp"
|
||||
```
|
||||
|
||||
#### Global Headers with Target
|
||||
|
||||
Configuring and installing the project is required for linking CLI11 to your
|
||||
project in the same way as you would do with any other external library. With
|
||||
CMake, this step allows using `find_package(CLI11 CONFIG REQUIRED)` and then
|
||||
using the `CLI11::CLI11` target when linking. If `CMAKE_INSTALL_PREFIX` was
|
||||
changed during install to a specific folder like `/opt/CLI11`, then you have to
|
||||
pass `-DCLI11_DIR=/opt/CLI11` when building your current project. You can also
|
||||
use [Conan.io](https://conan.io/center/cli11) or
|
||||
[Hunter](https://docs.hunter.sh/en/latest/packages/pkg/CLI11.html). (These are
|
||||
just conveniences to allow you to use your favorite method of managing packages;
|
||||
it's just header only so including the correct path and using C++11 is all you
|
||||
really need.)
|
||||
|
||||
#### Modules
|
||||
|
||||
Module support is experimental. To use modules, you must use C++20 or later,
|
||||
CMake 3.28 or later, and the Ninja or Visual Studio generator (Makefiles do not
|
||||
work). Build and install CLI11 with `-DCLI11_MODULES=ON`, then link the target
|
||||
`CLI11::Module` (the older name `CLI11::CLI11_Module` stays supported). The
|
||||
module library contains the precompiled implementation, so an `import cli11;`
|
||||
client usually compiles several times faster than one that includes the headers.
|
||||
Macros such as `CLI11_PARSE` are not available through `import cli11;` — use
|
||||
`app.parse()` and catch `CLI::ParseError`, or include the headers as well. For
|
||||
complete programs, including an `import std;` variant, see [Using CLI11 as a
|
||||
C++20 module](@ref book-modules-example).
|
||||
|
||||
#### Using Fetchcontent
|
||||
|
||||
If you do not want to add cmake as a submodule or include it with your code the
|
||||
project can be added using `FetchContent`. This capability requires CMake 3.14+
|
||||
(or 3.11+ with more work).
|
||||
|
||||
An example CMake file would include:
|
||||
|
||||
```cmake
|
||||
include(FetchContent)
|
||||
FetchContent_Declare(
|
||||
cli11_proj
|
||||
QUIET
|
||||
GIT_REPOSITORY https://github.com/CLIUtils/CLI11.git
|
||||
GIT_TAG v2.7.2
|
||||
)
|
||||
|
||||
FetchContent_MakeAvailable(cli11_proj)
|
||||
|
||||
# And now you can use it
|
||||
target_link_libraries(<your project> PRIVATE CLI11::CLI11)
|
||||
```
|
||||
|
||||
And use
|
||||
|
||||
```c++
|
||||
#include <CLI/CLI.hpp>
|
||||
```
|
||||
|
||||
in your project. It is highly recommended that you use the git hash for
|
||||
`GIT_TAG` instead of a tag or branch, as that will both be more secure, as well
|
||||
as faster to reconfigure - CMake will not have to reach out to the internet to
|
||||
see if the tag moved. You can also download just the single header file from the
|
||||
releases using `file(DOWNLOAD)`.
|
||||
|
||||
### Running tests on the full edition
|
||||
|
||||
CLI11 has examples and tests that can be accessed using a CMake build on any
|
||||
platform. Simply build and run ctest to run the 200+ tests to ensure CLI11 works
|
||||
on your system.
|
||||
|
||||
The test build uses Catch2. If Catch2 is not available as a CMake package, CLI11
|
||||
will download the required Catch2 header during configuration. On systems
|
||||
without internet access, or where TLS connections to GitHub releases are
|
||||
blocked, install Catch2 separately or configure with `CLI11_BUILD_TESTS=OFF`
|
||||
until the dependency is available.
|
||||
|
||||
As an example of the build system, the following commands will download and test
|
||||
CLI11 in a simple Alpine Linux docker container. (Docker is being used to create
|
||||
a pristine disposable environment; there is nothing special about this
|
||||
container. Alpine is being used because it is small, modern, and fast. Commands
|
||||
are similar on any other platform.)
|
||||
|
||||
```bash
|
||||
docker run -it alpine
|
||||
apk add --no-cache g++ cmake make git
|
||||
git clone https://github.com/CLIUtils/CLI11.git
|
||||
cd CLI11
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
make
|
||||
make test
|
||||
```
|
||||
|
||||
For the curious, the CMake options and defaults are listed below. Most options
|
||||
default to off if CLI11 is used as a subdirectory in another project.
|
||||
|
||||
| Option | Description |
|
||||
| ------------------------------------ | ---------------------------------------------------------------- |
|
||||
| `CLI11_SINGLE_FILE=OFF` | Build the `CLI11.hpp` file from the sources. Requires Python. |
|
||||
| `CLI11_PRECOMPILED=OFF` | Generate a precompiled library instead of header-only |
|
||||
| `CLI11_MODULES=OFF` | Build CLI11 as a module (requires C++20 or later) |
|
||||
| `CLI11_INSTALL_PACKAGE_TESTS=OFF` | Run tests checking the installation |
|
||||
| `CLI11_MODULE_TESTS=OFF` | Run a test checking that CLI11 works with modules |
|
||||
| `CLI11_SINGLE_FILE_TESTS=OFF` | Run the tests on the generated single file version as well |
|
||||
| `CLI11_BUILD_DOCS=ON` | Build CLI11 documentation and book |
|
||||
| `CLI11_BUILD_EXAMPLES=ON` | Build the example programs. |
|
||||
| `CLI11_BUILD_EXAMPLES_JSON=ON` | Build some additional example using json libraries |
|
||||
| `CLI11_INSTALL=ON` | Install CLI11 to the install folder during the install process |
|
||||
| `CLI11_FULL_INSTALL=OFF` | Install all CLI11 headers/libraries regardless of other settings |
|
||||
| `CLI11_FORCE_LIBCXX=OFF` | Use libc++ instead of libstdc++ if building with clang on linux |
|
||||
| `CLI11_DISABLE_IMPL_HEADERS_INSTALL` | Don't install the impl headers if the CLI11_PRECOMPILED is ON |
|
||||
| `CLI11_CUDA_TESTS=OFF` | Build the tests with NVCC |
|
||||
| `CLI11_BUILD_TESTS=ON` | Build the tests. |
|
||||
| `CLI11_ENABLE_EXTRA_VALIDATORS` | Set to 1 to enable the extra validators, 0 to disable them |
|
||||
| `CLI11_DISABLE_EXTRA_VALIDATORS` | Set to 1 to disable the extra validators |
|
||||
|
||||
The last two options set the macro of the same name on the CLI11 target. See
|
||||
[Validators](@ref book-validators) for what they control.
|
||||
|
||||
## Meson support
|
||||
|
||||
### Global Headers from pkg-config
|
||||
|
||||
If CLI11 is installed globally, then nothing more than `dependency('CLI11')` is
|
||||
required. If it installed in a non-default search path, then setting the
|
||||
`PKG_CONFIG_PATH` environment variable of the `--pkg-config-path` option to
|
||||
`meson setup` is all that's required.
|
||||
|
||||
### Using Meson's subprojects
|
||||
|
||||
Meson has a system called
|
||||
[wraps](https://mesonbuild.com/Wrap-dependency-system-manual.html), which allow
|
||||
Meson to fetch sources, configure, and build dependencies as part of a main
|
||||
project. This is the mechanism that Meson recommends for projects to use, as it
|
||||
allows updating the dependency transparently, and allows packagers to have fine
|
||||
grained control on the use of subprojects vs system provided dependencies.
|
||||
Simply run `meson wrap install cli11` to install the `cli11.wrap` file, and
|
||||
commit it, if desired.
|
||||
|
||||
It is also possible to use git submodules. This is generally discouraged by
|
||||
Meson upstream, but may be appropriate if a project needs to build with multiple
|
||||
build systems and wishes to share subprojects between them. As long as the
|
||||
submodule is in the parent project's subproject directory nothing additional is
|
||||
needed.
|
||||
|
||||
## Bazel support
|
||||
|
||||
CLI11 is a Bazel module. Add it to your `MODULE.bazel`:
|
||||
|
||||
```python
|
||||
bazel_dep(name = "cli11", version = "2.6.2")
|
||||
```
|
||||
|
||||
Then depend on the `@cli11//:cli11` target:
|
||||
|
||||
```python
|
||||
cc_binary(
|
||||
name = "my_app",
|
||||
srcs = ["my_app.cpp"],
|
||||
deps = ["@cli11//:cli11"],
|
||||
)
|
||||
```
|
||||
|
||||
The module builds CLI11 in precompiled mode. Bazel 7.4 or later is required.
|
||||
|
||||
## Installing cli11 using vcpkg
|
||||
|
||||
You can download and install cli11 using the
|
||||
[vcpkg](https://github.com/Microsoft/vcpkg) dependency manager:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/Microsoft/vcpkg.git
|
||||
cd vcpkg
|
||||
./bootstrap-vcpkg.sh
|
||||
./vcpkg integrate install
|
||||
./vcpkg install cli11
|
||||
```
|
||||
|
||||
The cli11 port in vcpkg is kept up to date by Microsoft team members and
|
||||
community contributors. If the version is out of date, please
|
||||
[create an issue or pull request](https://github.com/Microsoft/vcpkg) on the
|
||||
vcpkg repository.
|
||||
|
||||
## Installing CLI11 using Conan
|
||||
|
||||
You can install pre-built binaries for CLI11 or build it from source using
|
||||
[Conan](https://conan.io/). Use the following command:
|
||||
|
||||
```bash
|
||||
conan install --requires="cli11/[*]" --build=missing
|
||||
```
|
||||
|
||||
The CLI11 Conan recipe is kept up to date by Conan maintainers and community
|
||||
contributors. If the version is out of date, please
|
||||
[create an issue or pull request](https://github.com/conan-io/conan-center-index)
|
||||
on the ConanCenterIndex repository.
|
||||
|
||||
## Special instructions for GCC 8, Some clang, and WASI
|
||||
|
||||
If you are using GCC 8 and using it in C++17 mode with CLI11. CLI11 makes use of
|
||||
the `<filesystem>` header if available, but specifically for this compiler, the
|
||||
`filesystem` library is separate from the standard library and needs to be
|
||||
linked separately. So it is available but CLI11 doesn't use it by default.
|
||||
|
||||
Specifically `libstdc++fs` needs to be added to the linking list and
|
||||
`CLI11_HAS_FILESYSTEM=1` has to be defined. Then the filesystem variant of the
|
||||
Validators could be used on GCC 8. GCC 9+ does not have this issue so the
|
||||
`<filesystem>` is used by default.
|
||||
|
||||
There may also be other cases where a specific library needs to be linked.
|
||||
|
||||
Defining `CLI11_HAS_FILESYSTEM=0` which will remove the usage and hence any
|
||||
linking issue.
|
||||
|
||||
In some cases certain clang compilations may require linking against `libc++fs`.
|
||||
These situations have not been encountered so the specific situations requiring
|
||||
them are unknown yet.
|
||||
|
||||
If building with WASI it is necessary to add the flag
|
||||
`-lc-printscan-long-double` to the build to allow long double support. See #841
|
||||
for more details.
|
||||
|
||||
## Default system packages on Linux
|
||||
|
||||
If you are not worried about latest features or recent bug fixes, you can
|
||||
install a stable version of CLI11 using:
|
||||
|
||||
`sudo apt install libcli11-dev` for Ubuntu, or: `sudo dnf install cli11-devel`
|
||||
on Fedora/Almalinux.
|
||||
|
||||
Then, in your CMake project, just call:
|
||||
|
||||
```cmake
|
||||
find_package(CLI11 CONFIG REQUIRED)
|
||||
target_link_libraries(MyTarget PRIVATE CLI11::CLI11)
|
||||
```
|
||||
|
||||
and in your C++ file:
|
||||
|
||||
```cpp
|
||||
#include "CLI/App.hpp"
|
||||
#include "CLI/Formatter.hpp"
|
||||
#include "CLI/Config.hpp"
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
CLI::App app{"MyApp"};
|
||||
// Here your flags / options
|
||||
CLI11_PARSE(app, argc, argv);
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,97 @@
|
||||
# CLI11 Internals {#book-internals}
|
||||
|
||||
## Callbacks
|
||||
|
||||
The library was designed to bind to existing variables without requiring typed
|
||||
classes or inheritance. This is accomplished through lambda functions.
|
||||
|
||||
This looks like:
|
||||
|
||||
```cpp
|
||||
Option* add_option(string name, T &item) {
|
||||
this->function = [&item](string value){
|
||||
return lexical_cast(value, item);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Obviously, you can't access `T` after the `add_` method is over. To store the
|
||||
string representation of the default value, call `capture_default_str()` on the
|
||||
option (or use `always_capture_default()` to do this for every option). Only the
|
||||
low-level `add_option` overload taking a raw `callback_t` still accepts a
|
||||
`defaulted` bool argument.
|
||||
|
||||
## Parsing
|
||||
|
||||
Parsing follows the following procedure:
|
||||
|
||||
1. `_validate`: Make sure the defined options and subcommands are self
|
||||
consistent.
|
||||
2. `_parse`: Main parsing routine. See below.
|
||||
3. `_run_callback`: Run an App callback if present.
|
||||
|
||||
The parsing phase is the most interesting:
|
||||
|
||||
1. `_parse_single`: Run on each entry on the command line and fill the
|
||||
options/subcommands.
|
||||
2. `_process`: Run the procedure listed below.
|
||||
3. `_process_extras`: This throws an error if needed on extra arguments that
|
||||
didn't fit in the parse.
|
||||
|
||||
The `_process` procedure runs the following steps; each step is recursive and
|
||||
completes all subcommands before moving to the next step. This ensures that
|
||||
interactions between options and subcommand options is consistent.
|
||||
|
||||
```c++
|
||||
CLI11_INLINE void App::_process() {
|
||||
// help takes precedence over other potential errors and config and environment shouldn't be processed if help
|
||||
// throws
|
||||
_process_callbacks(CallbackPriority::FirstPreHelp);
|
||||
_process_help_flags(CallbackPriority::First);
|
||||
_process_callbacks(CallbackPriority::First);
|
||||
|
||||
std::exception_ptr config_exception;
|
||||
try {
|
||||
// the config file might generate a FileError but that should not be processed until later in the process
|
||||
// to allow for help, version and other errors to generate first.
|
||||
_process_config_file();
|
||||
|
||||
// process env shouldn't throw but no reason to process it if config generated an error
|
||||
_process_env();
|
||||
} catch(const CLI::FileError &) {
|
||||
config_exception = std::current_exception();
|
||||
}
|
||||
// callbacks and requirements processing can generate exceptions which should take priority
|
||||
// over the config file error if one exists.
|
||||
_process_callbacks(CallbackPriority::PreRequirementsCheckPreHelp);
|
||||
_process_help_flags(CallbackPriority::PreRequirementsCheck);
|
||||
_process_callbacks(CallbackPriority::PreRequirementsCheck);
|
||||
|
||||
_process_requirements();
|
||||
|
||||
_process_callbacks(CallbackPriority::NormalPreHelp);
|
||||
_process_help_flags(CallbackPriority::Normal);
|
||||
_process_callbacks(CallbackPriority::Normal);
|
||||
|
||||
if(config_exception) {
|
||||
std::rethrow_exception(config_exception);
|
||||
}
|
||||
|
||||
_process_callbacks(CallbackPriority::LastPreHelp);
|
||||
_process_help_flags(CallbackPriority::Last);
|
||||
_process_callbacks(CallbackPriority::Last);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
Option callbacks can be executed at many different stages depending on the
|
||||
priority specified. The default is `Normal` so they will execute after
|
||||
processing requirements. The default for help and version flags is to execute
|
||||
`First`. Both can be changed to execute in different steps of the process.
|
||||
|
||||
## Exceptions
|
||||
|
||||
The library immediately returns a C++ exception when it detects a problem, such
|
||||
as an incorrect construction or a malformed command line. Errors from config
|
||||
processing are delayed until after other processing, to give priority to any
|
||||
help or version flags, or other types of callback errors.
|
||||
@@ -0,0 +1,102 @@
|
||||
# Using CLI11 as a C++20 module {#book-modules-example}
|
||||
|
||||
Module support is experimental. This page shows two complete programs that use
|
||||
`import cli11;`. For the build options, targets, and requirements, see [Modules
|
||||
in the installation chapter](@ref book-installation).
|
||||
|
||||
## A complete minimal project
|
||||
|
||||
Build and install CLI11 with `-DCLI11_MODULES=ON` first, then use this project:
|
||||
|
||||
```cmake
|
||||
cmake_minimum_required(VERSION 3.28)
|
||||
project(myapp LANGUAGES CXX)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
find_package(CLI11 REQUIRED)
|
||||
|
||||
add_executable(myapp myapp.cpp)
|
||||
target_link_libraries(myapp PRIVATE CLI11::Module)
|
||||
```
|
||||
|
||||
```cpp
|
||||
// myapp.cpp
|
||||
// Keep the includes before the import; GCC rejects the reverse order.
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
import cli11;
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
CLI::App app{"MyApp"};
|
||||
|
||||
std::string file;
|
||||
app.add_option("-f,--file", file, "The file name")->required();
|
||||
|
||||
try {
|
||||
app.parse(argc, argv);
|
||||
} catch(const CLI::ParseError &e) {
|
||||
return app.exit(e);
|
||||
}
|
||||
|
||||
std::cout << "file=" << file << '\n';
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
Use the same compiler for the CLI11 library and the application. Macros such as
|
||||
`CLI11_PARSE` are not available through `import cli11;` — use `app.parse()` and
|
||||
catch `CLI::ParseError`, or include the headers as well.
|
||||
|
||||
## Use with import std
|
||||
|
||||
You can combine `import cli11;` with `import std;`. This needs C++23 and a
|
||||
standard library that ships the `std` module (recent libc++, MSVC, or libstdc++
|
||||
from GCC 15+). CMake support for `import std` (`CMAKE_CXX_MODULE_STD`, CMake
|
||||
3.30+) is experimental and is behind the `CMAKE_EXPERIMENTAL_CXX_IMPORT_STD`
|
||||
gate, so this example invokes clang and libc++ directly:
|
||||
|
||||
```cpp
|
||||
// myapp.cpp
|
||||
import cli11;
|
||||
import std;
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
CLI::App app{"MyApp"};
|
||||
|
||||
std::string file;
|
||||
app.add_option("-f,--file", file, "The file name")->required();
|
||||
|
||||
try {
|
||||
app.parse(argc, argv);
|
||||
} catch(const CLI::ParseError &e) {
|
||||
return app.exit(e);
|
||||
}
|
||||
|
||||
std::println("file={}", file);
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
```sh
|
||||
# CLI11 = path to the CLI11 sources, LIBCXX = path that contains std.cppm
|
||||
# (for example <llvm prefix>/share/libc++/v1)
|
||||
clang++ -std=c++23 -O2 -Wno-reserved-module-identifier --precompile \
|
||||
$LIBCXX/std.cppm -o std.pcm
|
||||
clang++ -std=c++23 -O2 -Wno-reserved-module-identifier -c std.pcm -o std.o
|
||||
clang++ -std=c++23 -O2 -DCLI11_COMPILE -I $CLI11/include --precompile \
|
||||
$CLI11/src/modules/CLI11.cppm -o cli11.pcm
|
||||
clang++ -std=c++23 -O2 -c cli11.pcm -o cli11.o
|
||||
clang++ -std=c++23 -O2 -DCLI11_COMPILE -I $CLI11/include \
|
||||
-c $CLI11/src/Precompile.cpp -o impl.o
|
||||
clang++ -std=c++23 -O2 -fmodule-file=std=std.pcm \
|
||||
-fmodule-file=cli11=cli11.pcm -c myapp.cpp -o myapp.o
|
||||
clang++ myapp.o cli11.o impl.o std.o -o myapp
|
||||
```
|
||||
|
||||
With both modules prebuilt, only the last two commands run again when
|
||||
`myapp.cpp` changes, and they are fast; in our tests the application compiled
|
||||
several times faster than an equivalent header-only build. Exact times depend on
|
||||
the compiler and the application.
|
||||
@@ -0,0 +1,142 @@
|
||||
# Option groups {#book-option-groups}
|
||||
|
||||
The `->group("name")` modifier on an option only changes where the option is
|
||||
printed in the help. An option _group_ is a stronger tool: it is a real
|
||||
container that collects options and can carry requirements of its own.
|
||||
|
||||
```cpp
|
||||
auto *format = app.add_option_group("output_format", "formatting type for output");
|
||||
```
|
||||
|
||||
`add_option_group` returns a pointer to the group. The description is optional.
|
||||
An option group is a specialization of `App`, so everything that works on an app
|
||||
or a subcommand works on a group as well: `require_option`, `excludes`, `needs`,
|
||||
`disabled`, callbacks, and nesting.
|
||||
|
||||
## Adding options to a group
|
||||
|
||||
Options can be created directly on the group, exactly as on an app:
|
||||
|
||||
```cpp
|
||||
std::string filename;
|
||||
format->add_option("--file", filename, "output file");
|
||||
format->add_flag("--binary", "write binary output");
|
||||
```
|
||||
|
||||
An option that already exists on the parent app can be moved into the group:
|
||||
|
||||
```cpp
|
||||
auto *opt = app.add_option("--file", filename);
|
||||
format->add_option(opt);
|
||||
format->add_options(opt1, opt2, opt3);
|
||||
```
|
||||
|
||||
The option pointers must belong to the parent application of the group, or an
|
||||
error is generated. A subcommand can be moved in the same way, which removes it
|
||||
from its parent:
|
||||
|
||||
```cpp
|
||||
format->add_subcommand(subcom_pointer);
|
||||
```
|
||||
|
||||
## Requirements between groups
|
||||
|
||||
An app counts a whole option group as a single option for the purpose of
|
||||
`require_option`, and the group counts as used if any option or subcommand
|
||||
inside it was used. That is what makes groups useful: you can require one of
|
||||
three options in one group and one of three in another.
|
||||
|
||||
```cpp
|
||||
CLI::App app("data output specification");
|
||||
|
||||
auto *format = app.add_option_group("output_format", "formatting type for output");
|
||||
format->add_flag("--csv", "write csv output");
|
||||
format->add_flag("--binary", "write binary output");
|
||||
format->require_option(1); // exactly one of the two
|
||||
|
||||
auto *target = app.add_option_group("output_target", "target for the output");
|
||||
target->add_option("--file", filename, "output file");
|
||||
target->add_flag("--stdout", "write to stdout");
|
||||
target->require_option(1);
|
||||
```
|
||||
|
||||
`require_option(N)` requires exactly `N` if `N` is positive, up to `N` if `N` is
|
||||
negative, and resets to "0 or more" for `N=0`. `require_option(min, max)` sets
|
||||
both ends, with a `max` of 0 meaning unlimited. `require_option()` with no
|
||||
argument requires 1 or more.
|
||||
|
||||
Disabling a group with `->disabled()` turns off every option inside it. Groups
|
||||
can also contain other groups.
|
||||
|
||||
See the
|
||||
[option_groups.cpp](https://github.com/CLIUtils/CLI11/blob/main/examples/option_groups.cpp)
|
||||
and
|
||||
[ranges.cpp](https://github.com/CLIUtils/CLI11/blob/main/examples/ranges.cpp)
|
||||
examples.
|
||||
|
||||
## Triggering one group from another
|
||||
|
||||
`CLI::TriggerOn` and `CLI::TriggerOff` connect groups, so that use of one group
|
||||
enables or disables another:
|
||||
|
||||
```cpp
|
||||
CLI::TriggerOn(group1_pointer, triggered_group);
|
||||
CLI::TriggerOff(group2_pointer, disabled_group);
|
||||
```
|
||||
|
||||
The second argument can also be a `std::vector<App *>`. These helpers are built
|
||||
from `preparse_callback`, `enabled_by_default()`, and `disabled_by_default()`;
|
||||
see [Subcommands](@ref book-subcommands). Use them one time per group, since
|
||||
they replace any earlier use of those three functions. For anything more
|
||||
complex, write the `preparse_callback` yourself.
|
||||
|
||||
## Group names and the help output
|
||||
|
||||
The group name controls how the help print treats the group:
|
||||
|
||||
- A normal name prints the options under that heading.
|
||||
- An empty name hides the whole group, which is a quick way to hide a set of
|
||||
options: `app.add_option_group("")`.
|
||||
- A name that starts with `+` prints the options as if they were not in a group
|
||||
at all, and `get_options` treats them the same way:
|
||||
`app.add_option_group("+sub")`.
|
||||
|
||||
Group names may not contain newlines or null characters.
|
||||
|
||||
## Nameless subcommands
|
||||
|
||||
A subcommand with an empty name behaves much like an option group:
|
||||
|
||||
```cpp
|
||||
auto *shared = app.add_subcommand();
|
||||
shared->add_option("--shared", value);
|
||||
```
|
||||
|
||||
If an option is not found in the main app, all nameless subcommands are searched
|
||||
as well. Because `add_subcommand` also accepts a `std::shared_ptr<App>`, a set
|
||||
of options can be defined in one component and merged into one or more apps.
|
||||
Multiple nameless subcommands are allowed, and their callbacks run only if some
|
||||
option from the group was parsed.
|
||||
|
||||
## Deprecating and retiring options
|
||||
|
||||
Two helpers manage the life cycle of an option that is on its way out:
|
||||
|
||||
```cpp
|
||||
CLI::deprecate_option(option_pointer, "replacement_name");
|
||||
CLI::deprecate_option(app, "--option_name", "replacement_name");
|
||||
```
|
||||
|
||||
A deprecated option keeps working. The help print marks it, and the first use
|
||||
prints a warning that names the replacement, if you gave one.
|
||||
|
||||
```cpp
|
||||
CLI::retire_option(app, option_pointer);
|
||||
CLI::retire_option(app, "--option_name");
|
||||
```
|
||||
|
||||
A retired option does nothing. If the option exists it is replaced with a dummy
|
||||
option that takes the same arguments, so old command lines still parse, and the
|
||||
first use prints a warning. See the
|
||||
[retired.cpp](https://github.com/CLIUtils/CLI11/blob/main/examples/retired.cpp)
|
||||
example.
|
||||
@@ -0,0 +1,575 @@
|
||||
# Options {#book-options}
|
||||
|
||||
## Simple options
|
||||
|
||||
The most versatile addition to a command line program is an option. This is like
|
||||
a flag, but it takes an argument. CLI11 handles all the details for many types
|
||||
of options for you, based on their type. To add an option:
|
||||
|
||||
```cpp
|
||||
int int_option{0};
|
||||
app.add_option("-i", int_option, "Optional description");
|
||||
```
|
||||
|
||||
This will bind the option `-i` to the integer `int_option`. On the command line,
|
||||
a single value that can be converted to an integer will be expected. Non-integer
|
||||
results will fail. If that option is not given, CLI11 will not touch the initial
|
||||
value. This allows you to set up defaults by simply setting your value
|
||||
beforehand. If you want CLI11 to display your default value, you can add
|
||||
`->capture_default_str()` after the option.
|
||||
|
||||
```cpp
|
||||
int int_option{0};
|
||||
app.add_option("-i", int_option, "Optional description")->capture_default_str();
|
||||
```
|
||||
|
||||
You can use any C++ int-like type, not just `int`. CLI11 understands the
|
||||
following categories of types:
|
||||
|
||||
| Type | CLI11 |
|
||||
| -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| number like | Integers, floats, bools, or any type that can be constructed from an integer or floating point number. Accepts common numerical strings like `0xFF` as well as octal (`0755` or `0o755`), decimal, and binary(0b011111100), supports value separators including `_` and `'` |
|
||||
| string-like | std::string, or anything that can be constructed from or assigned a std::string |
|
||||
| char | For a single char, single string values are accepted, otherwise longer strings are treated as integral values and a conversion is attempted |
|
||||
| complex-number | std::complex or any type which has a real(), and imag() operations available, will allow 1 or 2 string definitions like "1+2j" or two arguments "1","2" |
|
||||
| enumeration | any enum or enum class type is supported through conversion from the underlying type(typically int, though it can be specified otherwise) |
|
||||
| container-like | a container(like vector) of any available types including other containers |
|
||||
| wrapper | any other object with a `value_type` static definition where the type specified by `value_type` is one of the type in this list, including `std::atomic<>` |
|
||||
| tuple | a tuple, pair, or array, or other type with a tuple size and tuple_type operations defined and the members being a type contained in this list |
|
||||
| function | A function that takes an array of strings and returns a string that describes the conversion failure or empty for success. May be the empty function. (`{}`) |
|
||||
| streamable | any other type with a `<<` operator will also work |
|
||||
|
||||
By default, CLI11 will assume that an option is optional, and one value is
|
||||
expected if you do not use a vector. You can change this on a specific option
|
||||
using option modifiers. An option name may start with any character except ('-',
|
||||
' ', '\n', and '!'). For long options, after the first character all characters
|
||||
are allowed except ('=',':','{',' ', '\n'). Names are given as a comma separated
|
||||
string, with the dash or dashes. An option can have as many names as you want,
|
||||
and afterward, using `count`, you can use any of the names, with dashes as
|
||||
needed, to count the options. One of the names is allowed to be given without
|
||||
proceeding dash(es); if present the option is a positional option, and that name
|
||||
will be used on the help line for its positional form.
|
||||
|
||||
## Positional options and aliases
|
||||
|
||||
When you give an option on the command line without a name, that is a positional
|
||||
option. Positional options are accepted in the same order they are defined. So,
|
||||
for example:
|
||||
|
||||
```text
|
||||
./a.out one --two three four
|
||||
```
|
||||
|
||||
The string `one` would have to be the first positional option. If `--two` is a
|
||||
flag, then the remaining two strings are positional. If `--two` is a
|
||||
one-argument option, then `four` is the second positional. If `--two` accepts
|
||||
two or more arguments, then there are no more positionals.
|
||||
|
||||
To make a positional option, you simply give CLI11 one name that does not start
|
||||
with a dash. You can have as many (non-overlapping) names as you want for an
|
||||
option, but only one positional name. So the following name string is valid:
|
||||
|
||||
```cpp
|
||||
"-a,-b,--alpha,--beta,mypos"
|
||||
```
|
||||
|
||||
This would make two short option aliases, two long option alias, and the option
|
||||
would be also be accepted as a positional.
|
||||
|
||||
## Containers of options
|
||||
|
||||
If you use a vector or other container instead of a plain option, you can accept
|
||||
more than one value on the command line. By default, a container accepts as many
|
||||
options as possible, until the next value that could be a valid option name. You
|
||||
can specify a set number using an option modifier `->expected(N)`. (The default
|
||||
unlimited behavior on vectors is restored with `N=-1`) CLI11 does not
|
||||
differentiate between these two methods for unlimited acceptance options.
|
||||
|
||||
| Separate names | Combined names |
|
||||
| ----------------- | -------------- |
|
||||
| `--vec 1 --vec 2` | `--vec 1 2` |
|
||||
|
||||
It is also possible to specify a minimum and maximum number through
|
||||
`->expected(Min,Max)`. It is also possible to specify a min and max type size
|
||||
for the elements of the container. It most cases these values will be
|
||||
automatically determined but a user can manually restrict them.
|
||||
|
||||
An example of setting up a vector option:
|
||||
|
||||
```cpp
|
||||
std::vector<int> int_vec;
|
||||
app.add_option("--vec", int_vec, "My vector option");
|
||||
```
|
||||
|
||||
Vectors will be replaced by the parsed content if the option is given on the
|
||||
command line.
|
||||
|
||||
A definition of a container for purposes of CLI11 is a type with a `end()`,
|
||||
`insert(...)`, `clear()` and `value_type` definitions. This includes `vector`,
|
||||
`set`, `deque`, `list`, `forward_list`, `map`, `unordered_map` and a few others
|
||||
from the standard library, and many other containers from the boost library.
|
||||
|
||||
### Empty containers
|
||||
|
||||
By default a container will never return an empty container. If it is desired to
|
||||
allow an empty container to be returned, then the option must be modified with a
|
||||
0 as the minimum expected value
|
||||
|
||||
```cpp
|
||||
std::vector<int> int_vec;
|
||||
app.add_option("--vec", int_vec, "Empty vector allowed")->expected(0,-1);
|
||||
```
|
||||
|
||||
An empty vector can than be specified on the command line as `--vec {}`
|
||||
|
||||
To allow an empty vector from config file, the default must be set in addition
|
||||
to the above modification.
|
||||
|
||||
```cpp
|
||||
std::vector<int> int_vec;
|
||||
app.add_option("--vec", int_vec, "Empty vector allowed")->expected(0,-1)->default_str("{}");
|
||||
```
|
||||
|
||||
Then in the file
|
||||
|
||||
```toml
|
||||
vec={}
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```toml
|
||||
vec=[]
|
||||
```
|
||||
|
||||
will generate an empty vector in `int_vec`.
|
||||
|
||||
### Containers of containers
|
||||
|
||||
Containers of containers are also supported.
|
||||
|
||||
```cpp
|
||||
std::vector<std::vector<int>> int_vec;
|
||||
app.add_option("--vec", int_vec, "My vector of vectors option");
|
||||
```
|
||||
|
||||
CLI11 inserts a separator sequence at the start of each argument call to
|
||||
separate the vectors. So unless the separators are injected as part of the
|
||||
command line each call of the option on the command line will result in a
|
||||
separate element of the outer vector. This can be manually controlled via
|
||||
`inject_separator(true|false)` but in nearly all cases this should be left to
|
||||
the defaults. To insert of a separator from the command line add a `%%` where
|
||||
the separation should occur.
|
||||
|
||||
```bash
|
||||
cmd --vec 1 2 3 4 %% 1 2
|
||||
```
|
||||
|
||||
would then result in a container of size 2 with the first element containing 4
|
||||
values and the second 2.
|
||||
|
||||
This separator is also the only way to get values into something like
|
||||
|
||||
```cpp
|
||||
std::pair<std::vector<int>,std::vector<int>> two_vecs;
|
||||
app.add_option("--vec", two_vecs, "pair of vectors");
|
||||
```
|
||||
|
||||
without calling the argument twice.
|
||||
|
||||
Further levels of nesting containers should compile but intermediate layers will
|
||||
only have a single element in the container, so is probably not that useful.
|
||||
|
||||
### Nested types
|
||||
|
||||
Types can be nested. For example:
|
||||
|
||||
```cpp
|
||||
std::map<int, std::pair<int,std::string>> map;
|
||||
app.add_option("--dict", map, "map of pairs");
|
||||
```
|
||||
|
||||
will require 3 arguments for each invocation, and multiple sets of 3 arguments
|
||||
can be entered for a single invocation on the command line.
|
||||
|
||||
```cpp
|
||||
std::map<int, std::pair<int,std::vector<std::string>>> map;
|
||||
app.add_option("--dict", map, "map of pairs");
|
||||
```
|
||||
|
||||
will result in a requirement for 2 integers on each invocation and absorb an
|
||||
unlimited number of strings including 0.
|
||||
|
||||
## Option modifiers
|
||||
|
||||
When you call `add_option`, you get a pointer to the added option. You can use
|
||||
that to add option modifiers. A full listing of the option modifiers:
|
||||
|
||||
| Modifier | Description |
|
||||
| ------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `->required()` | The program will quit if this option is not present. This is `mandatory` in Plumbum, but required options seems to be a more standard term. For compatibility, `->mandatory()` also works. |
|
||||
| `->expected(N)` | Take `N` values instead of as many as possible, mainly for vector args. |
|
||||
| `->expected(Nmin,Nmax)` | Take between `Nmin` and `Nmax` values. |
|
||||
| `->type_size(N)` | specify that each block of values would consist of N elements |
|
||||
| `->type_size(Nmin,Nmax)` | specify that each block of values would consist of between Nmin and Nmax elements |
|
||||
| `->needs(opt)` | This option requires another option to also be present, opt is an `Option` pointer or a string with the name of the option. Can be removed with `->remove_needs(opt)` |
|
||||
| `->excludes(opt)` | This option cannot be given with `opt` present, opt is an `Option` pointer or a string with the name of the option. Can be removed with `->remove_excludes(opt)` |
|
||||
| `->envname(name)` | Gets the value from the environment if present and not passed on the command line and passes any validators. |
|
||||
| `->group(name)` | The help group to put the option in. No effect for positional options. Defaults to `"Options"`. Options given an empty string for the group name will not show up in the help print. |
|
||||
| `->description(string)` | Set/change the description |
|
||||
| `->ignore_case()` | Ignore the case on the command line (also works on subcommands, does not affect arguments). |
|
||||
| `->ignore_underscore()` | Ignore any underscores on the command line (also works on subcommands, does not affect arguments). |
|
||||
| `->allow_extra_args()` | Allow extra argument values to be included when an option is passed. Enabled by default for vector options. Use `->allow_extra_args(false)` to take only the argument values the type requires, which keeps the arguments that follow available for positionals. |
|
||||
| `->disable_flag_override()` | specify that flag options cannot be overridden on the command line use `=<newval>` |
|
||||
| `->delimiter('<CH>')` | specify a character that can be used to separate elements in a command line argument, default is `<none>`, common values are ',', and ';'. A delimiter does not limit the option to a single argument; combine it with `->allow_extra_args(false)` for that. |
|
||||
| `->multi_option_policy( CLI::MultiOptionPolicy::Throw)` | Sets the policy for handling multiple arguments if the option was received on the command line several times. `Throw`ing an error is the default, but `TakeLast`, `TakeFirst`, `TakeAll`, `Join`, `Reverse`, and `Sum` are also available. See the next four lines for shortcuts to set this more easily. |
|
||||
| `->take_last()` | Only use the last option if passed several times. This is always true by default for bool options, regardless of the app default, but can be set to false explicitly with `->multi_option_policy()`. |
|
||||
| `->take_first()` | sets `->multi_option_policy(CLI::MultiOptionPolicy::TakeFirst)` |
|
||||
| `->take_all()` | sets `->multi_option_policy(CLI::MultiOptionPolicy::TakeAll)` |
|
||||
| `->join()` | sets `->multi_option_policy(CLI::MultiOptionPolicy::Join)`, which uses newlines or the specified delimiter to join all arguments into a single string output. |
|
||||
| `->join(delim)` | sets `->multi_option_policy(CLI::MultiOptionPolicy::Join)`, which uses `delim` to join all arguments into a single string output. this also sets the delimiter |
|
||||
| `->check(Validator)` | perform a check on the returned results to verify they meet some criteria. See [Validators](@ref book-validators) for more info |
|
||||
| `->transform(Validator)` | Run a transforming validator on each value passed. See [Validators](@ref book-validators) for more info |
|
||||
| `->each(void(std::string))` | Run a function on each parsed value, _in order_. |
|
||||
| `->default_str(string)` | set a default string for use in the help and as a default value if no arguments are passed and a value is requested |
|
||||
| `->default_function(std::string())` | Advanced: Change the function that `capture_default_str()` uses. |
|
||||
| `->default_val(value)` | Generate the default string from a value and validate that the value is also valid. For options that assign directly to a value type the value in that type is also updated. Value must be convertible to a string(one of known types or have a stream operator). |
|
||||
| `->capture_default_str()` | Store the current value attached and display it in the help string. |
|
||||
| `->always_capture_default()` | Always run `capture_default_str()` when creating new options. Only useful on an App's `option_defaults`. |
|
||||
| `->run_callback_for_default()` | Force the option callback to be executed or the variable set when the `default_val` is used. |
|
||||
| `->force_callback()` | Force the option callback to be executed regardless of whether the option was used or not. Will use the default_str if available, if no default is given the callback will be executed with an empty string as an argument, which will translate to a default initialized value, which can be compiler dependent |
|
||||
| `->trigger_on_parse()` | Have the option callback be triggered when the value is parsed vs. at the end of all parsing, the option callback can potentially be executed multiple times. Generally only useful if you have a user defined callback or validation check. Or potentially if a vector input is given multiple times as it will clear the results when a repeat option is given via command line. It will trigger the callbacks once per option call on the command line |
|
||||
| `->option_text(string)` | Sets the text between the option name and description. |
|
||||
|
||||
The `->check(...)` and `->transform(...)` modifiers can also take a callback
|
||||
function that runs on every value that the option receives and returns an error
|
||||
message as a `std::string`; an empty string means the value passed. A `check`
|
||||
function receives the value by `const` reference, while a `transform` function
|
||||
may modify it.
|
||||
|
||||
### Multi Option policy
|
||||
|
||||
The Multi option policy can be used to instruct CLI11 what to do when an option
|
||||
is called multiple times and how to return those values in a meaningful way.
|
||||
There are several options can be set through the
|
||||
`->multi_option_policy( CLI::MultiOptionPolicy::Throw)` option modifier.
|
||||
`Throw`ing an error is the default, but `TakeLast`, `TakeFirst`, `TakeAll`,
|
||||
`Join`, `Reverse`, and `Sum`
|
||||
|
||||
| Value | Description |
|
||||
| --------- | --------------------------------------------------------------------------------- |
|
||||
| Throw | Throws an error if more values are given then expected |
|
||||
| TakeLast | Selects the last expected number of values given |
|
||||
| TakeFirst | Selects the first expected number of values given |
|
||||
| Join | Joins the strings together using the `delimiter` given |
|
||||
| TakeAll | Takes all the values |
|
||||
| Sum | If the values are numeric, it sums them and returns the result |
|
||||
| Reverse | Selects the last expected number of values given and return them in reverse order |
|
||||
|
||||
NOTE: For reverse, the index used for an indexed validator (a validator with an
|
||||
[application index](@ref book-validators)) is also applied in reverse order
|
||||
index 1 will be the last element and 2 second from last and so on.
|
||||
|
||||
## Using the `CLI::Option` pointer
|
||||
|
||||
Each of the option creation mechanisms returns a pointer to the internally
|
||||
stored option. If you save that pointer, you can continue to access the option,
|
||||
and change setting on it later. The Option object can also be converted to a
|
||||
bool to see if it was passed, or `->count()` can be used to see how many times
|
||||
the option was passed. Since flags are also options, the same methods work on
|
||||
them.
|
||||
|
||||
```cpp
|
||||
CLI::Option* opt = app.add_flag("--opt");
|
||||
|
||||
CLI11_PARSE(app, argc, argv);
|
||||
|
||||
if(* opt)
|
||||
std::cout << "Flag received " << opt->count() << " times." << '\n';
|
||||
```
|
||||
|
||||
## Getting results without a bound variable
|
||||
|
||||
Binding a variable or a callback in the `add_*` call is the fastest way to get a
|
||||
result, and is what you should reach for first. When that is not possible, the
|
||||
values can be pulled back out of the option. These calls do the type conversion
|
||||
and processing at the point of the call, so keep them out of performance
|
||||
critical code.
|
||||
|
||||
```cpp
|
||||
CLI::Option *opt = app.add_option("--opt");
|
||||
|
||||
CLI11_PARSE(app, argc, argv);
|
||||
|
||||
std::vector<std::string> raw = opt->results(); // every value, in order
|
||||
int value = opt->as<int>(); // converted, one value
|
||||
std::vector<int> values = opt->as<std::vector<int>>(); // converted, all values
|
||||
opt->results(value); // same as as<int>(), without a copy
|
||||
```
|
||||
|
||||
`as<T>()` and `results(T&)` apply the multi option policy for a single value,
|
||||
and return everything for a vector type. If you know that the results will be
|
||||
needed as a vector, tell CLI11 when you create the option, with
|
||||
`->expected(CLI::detail::expected_max_vector_size)` or `->allow_extra_args()`.
|
||||
|
||||
You do not need to keep the pointer. `app["--opt"]` and
|
||||
`app.get_option("--opt")` find it again by any of its names:
|
||||
|
||||
```cpp
|
||||
int value = app["--opt"]->as<int>();
|
||||
```
|
||||
|
||||
## Options with a callback
|
||||
|
||||
Instead of a variable, an option can take a function. `add_option_function<T>`
|
||||
converts the arguments to `T` first and hands you the finished value:
|
||||
|
||||
```cpp
|
||||
app.add_option_function<int>("--count", [](int count) {
|
||||
std::cout << "got " << count << '\n';
|
||||
});
|
||||
```
|
||||
|
||||
The type argument is required, since it is what tells CLI11 how to parse. This
|
||||
pairs well with `->trigger_on_parse()` when the callback has to run while
|
||||
parsing rather than at the end; see the
|
||||
[custom_validator.cpp](https://github.com/CLIUtils/CLI11/blob/main/examples/custom_validator.cpp)
|
||||
example. `add_flag_function` is the flag counterpart, and takes a
|
||||
`void(std::int64_t)` function. For full control over the raw strings, see
|
||||
[Custom option callbacks](@ref book-advanced-topics).
|
||||
|
||||
## Inheritance of defaults
|
||||
|
||||
One of CLI11's systems to allow customizability without high levels of verbosity
|
||||
is the inheritance system. You can set default values on the parent `App`, and
|
||||
all options and subcommands created from it remember the default values at the
|
||||
point of creation. The default value for Options, specifically, are accessible
|
||||
through the `option_defaults()` method. There are a number of settings that can
|
||||
be set and inherited:
|
||||
|
||||
- `group`: The group name starts as "Options"
|
||||
- `required`: If the option must be given. Defaults to `false`. Is ignored for
|
||||
flags.
|
||||
- `multi_option_policy`: What to do if several copies of an option are passed
|
||||
and one value is expected. Defaults to `CLI::MultiOptionPolicy::Throw`. This
|
||||
is also used for bool flags, but they always are created with the value
|
||||
`CLI::MultiOptionPolicy::TakeLast` or `CLI::MultiOptionPolicy::Sum` regardless
|
||||
of the default, so that multiple bool flags does not cause an error. But you
|
||||
can override that setting by calling the `multi_option_policy` directly.
|
||||
- `ignore_case`: Allow any mixture of cases for the option or flag name
|
||||
- `ignore_underscore`: Allow any number of underscores in the option or flag
|
||||
name
|
||||
- `configurable`: Specify whether an option can be configured through a config
|
||||
file
|
||||
- `disable_flag_override`: do not allow flag values to be overridden on the
|
||||
command line
|
||||
- `always_capture_default`: specify that the default values should be
|
||||
automatically captured.
|
||||
- `delimiter`: A delimiter to use for capturing multiple values in a single
|
||||
command line string (e.g. --flag="flag,-flag2,flag3")
|
||||
|
||||
An example of usage:
|
||||
|
||||
```cpp
|
||||
app.option_defaults()->ignore_case()->group("Required");
|
||||
|
||||
CLI::Option* opt = app.add_flag("--CaSeLeSs");
|
||||
opt->get_group() // is "Required"
|
||||
```
|
||||
|
||||
Groups are mostly for visual organization, but an empty string for a group name
|
||||
will hide the option.
|
||||
|
||||
### Windows style options
|
||||
|
||||
You can also set the app setting `app->allow_windows_style_options()` to allow
|
||||
windows style options to also be recognized on the command line:
|
||||
|
||||
- `/a` (flag)
|
||||
- `/f filename` (option)
|
||||
- `/long` (long flag)
|
||||
- `/file filename` (space)
|
||||
- `/file:filename` (colon)
|
||||
- `/long_flag:false` (long flag with : to override the default value)
|
||||
|
||||
Windows style options do not allow combining short options or values not
|
||||
separated from the short option like with `-` options. You still specify option
|
||||
names in the same manner as on Linux with single and double dashes when you use
|
||||
the `add_*` functions, and the Linux style on the command line will still work.
|
||||
If a long and a short option share the same name, the option will match on the
|
||||
first one defined.
|
||||
|
||||
## Parse configuration
|
||||
|
||||
How an option and its arguments are parsed depends on a set of controls that are
|
||||
part of the option structure. In most circumstances these controls are set
|
||||
automatically based on the type or function used to create the option and the
|
||||
type the arguments are parsed into. The variables define the size of the
|
||||
underlying type (essentially how many strings make up the type), the expected
|
||||
size (how many groups are expected) and a flag indicating if multiple groups are
|
||||
allowed with a single option. And these interact with the `multi_option_policy`
|
||||
when it comes time to parse.
|
||||
|
||||
### Examples
|
||||
|
||||
How options manage this is best illustrated through some examples.
|
||||
|
||||
```cpp
|
||||
std::string val;
|
||||
app.add_option("--opt",val,"description");
|
||||
```
|
||||
|
||||
creates an option that assigns a value to a `std::string` When this option is
|
||||
constructed it sets a type_size min and max of 1. Meaning that the assignment
|
||||
uses a single string. The Expected size is also set to 1 by default, and
|
||||
`allow_extra_args` is set to false. meaning that each time this option is called
|
||||
1 argument is expected. This would also be the case if val were a `double`,
|
||||
`int` or any other single argument types. The modifier `allow_extra_args` should
|
||||
be set to true if the option output will ever be needed as a vector.
|
||||
|
||||
now for example
|
||||
|
||||
```cpp
|
||||
std::pair<int, std::string> val;
|
||||
app.add_option("--opt",val,"description");
|
||||
```
|
||||
|
||||
In this case the typesize is automatically detected to be 2 instead of 1, so the
|
||||
parsing would expect 2 arguments associated with the option.
|
||||
|
||||
```cpp
|
||||
std::vector<int> val;
|
||||
app.add_option("--opt",val,"description");
|
||||
```
|
||||
|
||||
detects a type size of 1, since the underlying element type is a single string,
|
||||
so the minimum number of strings is 1. But since it is a vector the expected
|
||||
number can be very big. The default for a vector is (1<<30), and the
|
||||
allow_extra_args is set to true. This means that at least 1 argument is expected
|
||||
to follow the option, but arbitrary numbers of arguments may follow. These are
|
||||
checked if they have the form of an option but if not they are added to the
|
||||
argument.
|
||||
|
||||
```cpp
|
||||
std::vector<std::tuple<int, double, std::string>> val;
|
||||
app.add_option("--opt",val,"description");
|
||||
```
|
||||
|
||||
gets into the complicated cases where the type size is now 3. and the expected
|
||||
max is set to a large number and `allow_extra_args` is set to true. In this case
|
||||
at least 3 arguments are required to follow the option, and subsequent groups
|
||||
must come in groups of three, otherwise an error will result.
|
||||
|
||||
```cpp
|
||||
bool val{false};
|
||||
app.add_flag("--opt",val,"description");
|
||||
```
|
||||
|
||||
Using the add_flag methods for creating options creates an option with an
|
||||
expected size of 0, implying no arguments can be passed.
|
||||
|
||||
```cpp
|
||||
std::complex<double> val;
|
||||
app.add_option("--opt",val,"description");
|
||||
```
|
||||
|
||||
triggers the complex number type which has a min of 1 and max of 2, so 1 or 2
|
||||
strings can be passed. Complex number conversion supports arguments of the form
|
||||
"1+2j" or "1","2", or "1" "2i". The imaginary number symbols `i` and `j` are
|
||||
interchangeable in this context.
|
||||
|
||||
```cpp
|
||||
std::vector<std::vector<int>> val;
|
||||
app.add_option("--opt",val,"description");
|
||||
```
|
||||
|
||||
has a type size of 1 to (1<<30).
|
||||
|
||||
### Customization
|
||||
|
||||
The `type_size(N)`, `type_size(Nmin, Nmax)`, `expected(N)`,
|
||||
`expected(Nmin,Nmax)`, and `allow_extra_args()` can be used to customize an
|
||||
option. For example
|
||||
|
||||
```cpp
|
||||
std::string val;
|
||||
auto opt=app.add_flag("--opt{vvv}",val,"description");
|
||||
opt->expected(0,1);
|
||||
```
|
||||
|
||||
will create a hybrid option, that can exist on its own in which case the value
|
||||
"vvv" is used or if a value is given that value will be used.
|
||||
|
||||
A container option takes as many argument values as it can, even if a delimiter
|
||||
is set. Set `allow_extra_args(false)` to take only one argument value, which is
|
||||
then split on the delimiter:
|
||||
|
||||
```cpp
|
||||
std::vector<std::string> val;
|
||||
app.add_option("--opt",val)->delimiter(',')->allow_extra_args(false);
|
||||
```
|
||||
|
||||
`--opt a,b file` now gives `{"a", "b"}` to `--opt` and leaves `file` for a
|
||||
positional. Without `allow_extra_args(false)`, `file` becomes a third element of
|
||||
`val`.
|
||||
|
||||
There are some additional options that can be specified to modify an option for
|
||||
specific cases:
|
||||
|
||||
- `->run_callback_for_default()` will specify that the callback should be
|
||||
executed when a default_val is set. This is set automatically when appropriate
|
||||
though it can be turned on or off and any user specified callback for an
|
||||
option will be executed when the default value for an option is set.
|
||||
|
||||
- `->force_callback()` will for the callback/value assignment to run at the
|
||||
conclusion of parsing regardless of whether the option was supplied or not.
|
||||
This can be used to force the default or execute some code.
|
||||
|
||||
- `->trigger_on_parse()` will trigger the callback or value assignment each time
|
||||
the argument is passed. The value is reset if the option is supplied multiple
|
||||
times.
|
||||
|
||||
- `->callback_priority(CallbackPriority priority)`: changes the order in which
|
||||
the option callback is executed. Four principal callback call-points are
|
||||
available. `CallbackPriority::First` executes at the very beginning of
|
||||
processing, before configuration files are read and environment variables are
|
||||
interpreted. `CallbackPriority::PreRequirementsCheck` executes after
|
||||
configuration and environment processing but before requirements checking.
|
||||
`CallbackPriority::Normal` executes after the requirements check but before
|
||||
any previously potentially raised exceptions are re-thrown.
|
||||
`CallbackPriority::Last` executes after exception handling is completed. For
|
||||
each position, both ordinary option callbacks and help callbacks are invoked.
|
||||
The relative order between them can be controlled using the corresponding
|
||||
`PreHelp` variants. `CallbackPriority::FirstPreHelp` executes ordinary option
|
||||
callbacks before help callbacks at the very beginning of processing.
|
||||
`CallbackPriority::PreRequirementsCheckPreHelp` executes ordinary option
|
||||
callbacks before help callbacks after configuration and environment processing
|
||||
but before requirements checking. `CallbackPriority::NormalPreHelp` executes
|
||||
ordinary option callbacks before help callbacks after the requirements check
|
||||
but before exception re-throwing. `CallbackPriority::LastPreHelp` executes
|
||||
ordinary option callbacks before help callbacks after exception handling has
|
||||
completed. When using the standard priorities (`CallbackPriority::First`,
|
||||
`CallbackPriority::PreRequirementsCheck`, `CallbackPriority::Normal`,
|
||||
`CallbackPriority::Last`), help callbacks are executed before ordinary option
|
||||
callbacks. By default, help callbacks use `CallbackPriority::First`, and
|
||||
ordinary option callbacks use `CallbackPriority::Normal`. This mechanism
|
||||
provides fine-grained control over when option values are set and when help or
|
||||
requirement checks occur, enabling precise customization of the processing
|
||||
sequence.
|
||||
|
||||
## Unusual circumstances
|
||||
|
||||
There are a few cases where some things break down in the type system managing
|
||||
options and definitions. Using the `add_option` method defines a lambda function
|
||||
to extract a default value if required. In most cases this is either
|
||||
straightforward or a failure is detected automatically and handled. But in a few
|
||||
cases a streaming template is available that several layers down may not
|
||||
actually be defined. This results in CLI11 not being able to detect this
|
||||
circumstance automatically and will result in compile error. One specific known
|
||||
case is `boost::optional` if the boost optional_io header is included. This
|
||||
header defines a template for all boost optional values even if they do not
|
||||
actually have a streaming operator. For example `boost::optional<std::vector>`
|
||||
does not have a streaming operator but one is detected since it is part of a
|
||||
template. For these cases a secondary method `app->add_option_no_stream(...)` is
|
||||
provided that bypasses this operation completely and should compile in these
|
||||
cases.
|
||||
@@ -0,0 +1,355 @@
|
||||
# Subcommands and the App {#book-subcommands}
|
||||
|
||||
Subcommands are keyword that invoke a new set of options and features. For
|
||||
example, the `git` command has a long series of subcommands, like `add` and
|
||||
`commit`. Each can have its own options and implementations. This chapter will
|
||||
focus on implementations that are contained in the same C++ application, though
|
||||
the system git uses to extend the main command by calling other commands in
|
||||
separate executables is supported too; that's called "Prefix commands" and is
|
||||
included at the end of this chapter.
|
||||
|
||||
## The parent App
|
||||
|
||||
We'll start by discussing the parent `App`. You've already used it quite a bit,
|
||||
to create options and set option defaults. There are several other things you
|
||||
can do with an `App`, however.
|
||||
|
||||
You are given a lot of control the help output. You can set a footer with
|
||||
`app.footer("My Footer")`. You can replace the default help print when a
|
||||
`ParseError` is thrown with `app.failure_message(CLI::FailureMessage::help)`.
|
||||
The default is `CLI::FailureMessage::simple`, and you can easily define a new
|
||||
one. Just make a (lambda) function that takes an App pointer and a reference to
|
||||
an error code (even if you don't use them), and returns a string.
|
||||
|
||||
### Inspecting the app
|
||||
|
||||
An app can be queried after, or during, the parse:
|
||||
|
||||
- `app.get_option("--name")` returns the option pointer, and throws
|
||||
`CLI::OptionNotFound` if there is none. `app["--name"]` is the same thing on a
|
||||
const app, and `get_option_no_throw` returns `nullptr` instead of throwing.
|
||||
- `app.get_options()` returns all options; `app.get_subcommands()` returns the
|
||||
subcommands that were parsed, in order.
|
||||
- `app.count("--name")` counts one option, and `app.count_all()` counts every
|
||||
option and subcommand use in the app.
|
||||
- `app.parse_order()` returns the options in the order they appeared on the
|
||||
command line, which is how you recover the relative order of two different
|
||||
options. See the
|
||||
[inter_argument_order.cpp](https://github.com/CLIUtils/CLI11/blob/main/examples/inter_argument_order.cpp)
|
||||
example.
|
||||
- `app.get_parent()` returns the parent app of a subcommand, or `nullptr` for
|
||||
the main app.
|
||||
|
||||
Options and subcommands can also be taken back out, which is mostly useful when
|
||||
you build an app from reusable pieces:
|
||||
|
||||
```cpp
|
||||
app.remove_option(opt_pointer);
|
||||
app.remove_subcommand(sub_pointer);
|
||||
```
|
||||
|
||||
Both return `true` if the item was found and removed.
|
||||
|
||||
## Adding a subcommand
|
||||
|
||||
Subcommands can be added just like an option:
|
||||
|
||||
```cpp
|
||||
CLI::App* sub = app.add_subcommand("sub", "This is a subcommand");
|
||||
```
|
||||
|
||||
The subcommand should have a name as the first argument, and a little
|
||||
description for the second argument. A pointer to the internally stored
|
||||
subcommand is provided; you usually will be capturing that pointer and using it
|
||||
later (though you can use callbacks if you prefer). As always, feel free to use
|
||||
`auto sub = ...` instead of naming the type.
|
||||
|
||||
You can check to see if the subcommand was received on the command line several
|
||||
ways:
|
||||
|
||||
```cpp
|
||||
if(*sub) ...
|
||||
if(sub->parsed()) ...
|
||||
if(app.got_subcommand(sub)) ...
|
||||
if(app.got_subcommand("sub")) ...
|
||||
```
|
||||
|
||||
You can also get a list of subcommands with `get_subcommands()`, and they will
|
||||
be in parsing order.
|
||||
|
||||
There are a lot of options that you can set on a subcommand; in fact,
|
||||
subcommands have exactly the same options as your main app, since they are
|
||||
actually the same class of object (as you may have guessed from the type above).
|
||||
This has the pleasant side affect of making subcommands infinitely nestable.
|
||||
|
||||
## Required subcommands
|
||||
|
||||
Each App has controls to set the number of subcommands you expect. This is
|
||||
controlled by:
|
||||
|
||||
```cpp
|
||||
app.require_subcommand(/* min */ 0, /* max */ 1);
|
||||
```
|
||||
|
||||
If you set the max to 0, CLI11 will allow an unlimited number of subcommands.
|
||||
After the (non-unlimited) maximum is reached, CLI11 will stop trying to match
|
||||
subcommands. So the if you pass "`one two`" to a command, and both `one` and
|
||||
`two` are subcommands, it will depend on the maximum number as to whether the
|
||||
"`two`" is a subcommand or an argument to the "`one`" subcommand.
|
||||
|
||||
As a shortcut, you can also call the `require_subcommand` method with one
|
||||
argument; that will be the fixed number of subcommands if positive, it will be
|
||||
the maximum number if negative. Calling it without an argument will set the
|
||||
required subcommands to 1 or more.
|
||||
|
||||
The maximum number of subcommands is inherited by subcommands. This allows you
|
||||
to set the maximum to 1 once at the beginning on the parent app if you only want
|
||||
single subcommands throughout your app. You should keep this in mind, if you are
|
||||
dealing with lots of nested subcommands.
|
||||
|
||||
## Using callbacks
|
||||
|
||||
You've already seen how to check to see what subcommands were given. It's often
|
||||
much easier, however, to just define the code you want to run when you are
|
||||
making your parser, and not run a bunch of code after `CLI11_PARSE` to analyse
|
||||
the state (Procedural! Yuck!). You can do that with lambda functions. A
|
||||
`std::function<void()>` callback `.callback()` is provided, and CLI11 ensures
|
||||
that all options are prepared and usable by reference capture before entering
|
||||
the callback. An example is shown below in the `geet` program.
|
||||
|
||||
### The three callbacks
|
||||
|
||||
`.callback()` sets the final callback. There are three call points in total, and
|
||||
each one answers a different need:
|
||||
|
||||
| Callback | When it runs |
|
||||
| ---------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
|
||||
| `preparse_callback(f)` | One time, after the first argument of the app or subcommand is seen. `f` takes the number of arguments left to process. |
|
||||
| `parse_complete_callback(f)` | As soon as the subcommand finishes parsing. Runs one time per use, so it can run several times. |
|
||||
| `final_callback(f)` | One time, after all processing is complete. This is what `callback()` sets. |
|
||||
|
||||
The order for a whole app is: every subcommand `parse_complete_callback`, then
|
||||
the main app `parse_complete_callback`, then the used subcommand
|
||||
`final_callback`s, then the option group `final_callback`s, and last the main
|
||||
app `final_callback`.
|
||||
|
||||
Configuration files matter here. A `parse_complete_callback` on a named
|
||||
subcommand sees no data from a config file, because it runs first. A
|
||||
`final_callback` runs after config processing. For option groups the
|
||||
`parse_complete_callback` runs after the config file is read.
|
||||
|
||||
A subcommand is finished, and so its `parse_complete_callback` fires, when any
|
||||
of these happen:
|
||||
|
||||
1. There are no more arguments.
|
||||
2. Another subcommand appears that does not fit an optional positional slot.
|
||||
3. The positional mark `--` appears and no positional slots are left.
|
||||
4. The subcommand terminator `++` appears.
|
||||
|
||||
Calling a subcommand a second time resets its options and can trigger the
|
||||
callback again. `.immediate_callback()` is the shorthand for moving the callback
|
||||
you set with `callback()` to the parse-complete point.
|
||||
|
||||
### Triggering subcommands from the command line
|
||||
|
||||
A subcommand option can be given without entering the subcommand, using dot
|
||||
notation:
|
||||
|
||||
```text
|
||||
--sub.long=val
|
||||
--sub.long val
|
||||
--sub.f val
|
||||
--sub1.subsub.f val
|
||||
```
|
||||
|
||||
`--sub.long <args>` is the same as `sub --long <args> ++`, where `++` closes the
|
||||
subcommand again. The names may be quoted following the TOML rules, which is how
|
||||
you reach a subcommand whose name contains dots:
|
||||
`"subcommand.with.dots".arg1 = value`.
|
||||
|
||||
## Inheritance of defaults
|
||||
|
||||
The following values are inherited when you add a new subcommand. This happens
|
||||
at the point the subcommand is created:
|
||||
|
||||
- The name and description for the help flag
|
||||
- The footer
|
||||
- The usage
|
||||
- The failure message printer function
|
||||
- The formatter
|
||||
- The config formatter
|
||||
- Option defaults
|
||||
- Allow extras
|
||||
- Allow config extras
|
||||
- Prefix command
|
||||
- Immediate callback
|
||||
- Ignore case
|
||||
- Ignore underscore
|
||||
- Allow Windows style options
|
||||
- Fallthrough
|
||||
- Group name
|
||||
- Max required subcommands
|
||||
- prefix_matching
|
||||
- Configurable
|
||||
- validate positional arguments
|
||||
- validate optional arguments
|
||||
|
||||
## More subcommand modifiers
|
||||
|
||||
Some further modifiers apply to an app, a subcommand, or an option group:
|
||||
|
||||
| Modifier | Description |
|
||||
| ------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `->require_option()` | Require 1 or more options or option groups. Also `require_option(N)` and `require_option(min, max)`, like `require_subcommand`. |
|
||||
| `->needs(opt_or_sub)` | The given option or subcommand must be used before this subcommand passes validation. |
|
||||
| `->excludes(opt_or_sub)` | The given option or subcommand cannot be used together with this one. |
|
||||
| `->disabled()` | Turn the subcommand off. Takes an optional bool. |
|
||||
| `->disabled_by_default()` | Disable at the start of each parse, so another subcommand can turn it on. |
|
||||
| `->enabled_by_default()` | Enable at the start of each parse, so another subcommand can turn it off. |
|
||||
| `->positionals_at_end()` | Positional arguments must come after all options. |
|
||||
| `->configurable()` | Allow the subcommand to be triggered from a configuration file. By default config file entries only update defaults. |
|
||||
| `->alias(name)` | Add another name for the subcommand. |
|
||||
| `->group(name)` | Set the help group for the subcommand. An empty name hides it from the help. |
|
||||
| `->allow_non_standard_option_names()` | Accept long option names with a single dash, such as `-single`. Not recommended, but useful when you reproduce an existing interface. A short option may not share its first character with a single dash long name. |
|
||||
|
||||
`disabled_by_default` and `enabled_by_default` are the building blocks behind
|
||||
`CLI::TriggerOn` and `CLI::TriggerOff`, described in @ref book-option-groups.
|
||||
|
||||
## Help and version flags
|
||||
|
||||
Every app is created with a help flag. You can rename it, replace it, or remove
|
||||
it:
|
||||
|
||||
```cpp
|
||||
app.set_help_flag("-h,--help", "Print this help message and exit");
|
||||
app.set_help_flag(); // pass nothing to remove it
|
||||
```
|
||||
|
||||
`set_help_all_flag` adds a second flag that expands every subcommand in the help
|
||||
output:
|
||||
|
||||
```cpp
|
||||
app.set_help_all_flag("--help-all", "Expand all help");
|
||||
```
|
||||
|
||||
A version flag is not created for you. Add one with a fixed string, or with a
|
||||
callback that produces the string when the flag is used:
|
||||
|
||||
```cpp
|
||||
app.set_version_flag("--version", std::string(CLI11_VERSION));
|
||||
app.set_version_flag("--version", []() { return my_version_string(); });
|
||||
```
|
||||
|
||||
All three functions return the option pointer, so the usual option modifiers
|
||||
apply, and all three replace the existing flag if there is one. The pointers can
|
||||
be read back later with `get_help_ptr()`, `get_help_all_ptr()`, and
|
||||
`get_version_ptr()`.
|
||||
|
||||
## Special modes
|
||||
|
||||
There are several special modes for Apps and Subcommands.
|
||||
|
||||
### Allow extras
|
||||
|
||||
Normally CLI11 throws an error if you don't match all items given on the command
|
||||
line. However, you can enable `allow_extras()` to instead store the extra values
|
||||
in `.remaining()`. You can get all remaining options including those in
|
||||
contained subcommands recursively in the original order with `.remaining(true)`.
|
||||
`.remaining_size()` is also provided; this counts the size but ignores the `--`
|
||||
special separator if present.
|
||||
|
||||
### Fallthrough
|
||||
|
||||
Fallthrough allows an option that does not match in a subcommand to "fall
|
||||
through" to the parent command; if that parent allows that option, it matches
|
||||
there instead. This was added to allow CLI11 to represent models:
|
||||
|
||||
```text
|
||||
./my_program my_model_1 --model_flag --shared_flag
|
||||
```
|
||||
|
||||
Here, `--shared_flag` was set on the main app, and on the command line it "falls
|
||||
through" `my_model_1` to match on the main app. This is set through
|
||||
`->fallthrough()` on a subcommand.
|
||||
|
||||
calling help on subcommands with fallthrough will result in the parent options
|
||||
showing as if they were part of the subcommand.
|
||||
|
||||
#### Subcommand fallthrough
|
||||
|
||||
Subcommand fallthrough allows additional subcommands to be triggered after the
|
||||
first subcommand. By default subcommand fallthrough is enabled, but it can be
|
||||
turned off through `->subcommand_fallthrough(false)` on a subcommand. This will
|
||||
prevent additional subcommands at the same inheritance level from triggering,
|
||||
the strings would then be treated as positional values. As a technical note if
|
||||
fallthrough is enabled but subcommand fallthrough disabled (this is not the
|
||||
default in both cases), then subcommands on grandparents can still be triggered
|
||||
from the grandchild subcommand, unless subcommand fallthrough is also disabled
|
||||
on the parent. This is an unusual circumstance but may arise in some very
|
||||
particular situations.
|
||||
|
||||
### Prefix command
|
||||
|
||||
This is a special mode that allows "prefix" commands, where the parsing
|
||||
completely stops when it gets to an unknown option. Further unknown options are
|
||||
ignored, even if they could match. Git is the traditional example for prefix
|
||||
commands; if you run git with an unknown subcommand, like "`git thing`", it then
|
||||
calls another command called "`git-thing`" with the remaining options intact.
|
||||
|
||||
### prefix matching
|
||||
|
||||
A modifier is available for subcommand matching,
|
||||
`->allow_subcommand_prefix_matching()`. if this is enabled unambiguous prefix
|
||||
portions of a subcommand will match. For Example `upgrade_package` would match
|
||||
on `upgrade_`, `upg`, `u` as long as no other subcommand would also match. It
|
||||
also disallows subcommand names that are full prefixes of another subcommand.
|
||||
|
||||
### Silent subcommands
|
||||
|
||||
Subcommands can be modified by using the `silent` option. This will prevent the
|
||||
subcommand from showing up in the get_subcommands list. This can be used to make
|
||||
subcommands into modifiers. For example, a help subcommand might look like
|
||||
|
||||
```c++
|
||||
auto sub1 = app.add_subcommand("help")->silent();
|
||||
sub1->parse_complete_callback([]() { throw CLI::CallForHelp(); });
|
||||
```
|
||||
|
||||
This would allow calling help such as:
|
||||
|
||||
```bash
|
||||
./app help
|
||||
./app help sub1
|
||||
```
|
||||
|
||||
### Positional Validation
|
||||
|
||||
Some arguments supplied on the command line may be legitimately applied to more
|
||||
than 1 positional argument. In this context calling `validate_positionals()` on
|
||||
the application or subcommand will check any validators before applying the
|
||||
command line argument to the positional option. It is not an error to fail
|
||||
validation in this context, positional arguments not matching any validators
|
||||
will be treated as extra arguments (retrievable via `app.remaining()`) which may
|
||||
generate an error depending on settings.
|
||||
|
||||
### Optional Argument Validation
|
||||
|
||||
Similar to positional validation, there are occasional contexts in which case it
|
||||
might be ambiguous whether an argument should be applied to an option or a
|
||||
positional option.
|
||||
|
||||
```c++
|
||||
std::vector<std::string> vec;
|
||||
std::vector<int> ivec;
|
||||
app.add_option("pos", vec);
|
||||
app.add_option("--args", ivec)->check(CLI::Number);
|
||||
app.validate_optional_arguments();
|
||||
```
|
||||
|
||||
In this case a sequence of integers is expected for the argument and remaining
|
||||
strings go to the positional string vector. Without the
|
||||
`validate_optional_arguments()` active it would be impossible get any later
|
||||
arguments into the positional if the `--args` option is used. The validator in
|
||||
this context is used to make sure the optional arguments match with what the
|
||||
argument is expecting and if not the `-args` option is closed, and remaining
|
||||
arguments fall into the positional.
|
||||
@@ -0,0 +1,40 @@
|
||||
# Using CLI11 in a Toolkit {#book-toolkits}
|
||||
|
||||
CLI11 was designed to be integrate into a toolkit, providing a native experience
|
||||
for users. This was used in GooFit to provide `GooFit::Application`, an class
|
||||
designed to make ROOT users feel at home.
|
||||
|
||||
## Custom namespace
|
||||
|
||||
If you want to provide CLI11 in a custom namespace, you'll want to at least put
|
||||
`using CLI::App` in your namespace. You can also include Option, some errors,
|
||||
and validators. You can also put `using namespace CLI` inside your namespace to
|
||||
import everything.
|
||||
|
||||
You may also want to make your own copy of the `CLI11_PARSE` macro. Something
|
||||
like:
|
||||
|
||||
```cpp
|
||||
#define MYPACKAGE_PARSE(app, argc, argv) \
|
||||
try { \
|
||||
app.parse(argc, argv); \
|
||||
} catch(const CLI::ParseError &e) { \
|
||||
return app.exit(e); \
|
||||
}
|
||||
```
|
||||
|
||||
## Subclassing App
|
||||
|
||||
If you subclass `App`, you'll just need to do a few things. You'll need a
|
||||
constructor; calling the base `App` constructor is a good idea, but not
|
||||
necessary (it just sets a description and adds a help flag).
|
||||
|
||||
You can call anything you would like to configure in the constructor, like
|
||||
`option_defaults()->take_last()` or `fallthrough()`, and it will be set on all
|
||||
user instances. You can add flags and options, as well.
|
||||
|
||||
## Virtual functions provided
|
||||
|
||||
You are given a few virtual functions that you can change (only on the main
|
||||
App). `pre_callback` runs right before the callbacks run, letting you print out
|
||||
custom messages at the top of your app.
|
||||
@@ -0,0 +1,81 @@
|
||||
# Unicode support {#book-unicode}
|
||||
|
||||
CLI11 follows the [UTF-8 Everywhere](http://utf8everywhere.org/) manifesto:
|
||||
strings are UTF-8 everywhere inside the library, and wide strings are converted
|
||||
at the edges.
|
||||
|
||||
Three things follow from this:
|
||||
|
||||
- CLI11 can parse the wide form of the command line on Windows and convert it to
|
||||
UTF-8 internally.
|
||||
- An option value can be a `std::wstring`. CLI11 converts it to the correct wide
|
||||
encoding for your system, UTF-16 on Windows and UTF-32 on most others.
|
||||
- Rather than store wide strings, it is better to keep `std::string` and convert
|
||||
only when you must, such as when you call a Windows API.
|
||||
|
||||
## Getting correct arguments on Windows
|
||||
|
||||
On Windows the `argv` that reaches `main` may already have lost information,
|
||||
because the arguments were converted to the local code page. Parsing that `argv`
|
||||
cannot give you the right string back.
|
||||
|
||||
The recommended fix works on every platform:
|
||||
|
||||
```cpp
|
||||
int main(int argc, char **argv) {
|
||||
CLI::App app;
|
||||
argv = app.ensure_utf8(argv); // new argv memory is held by app
|
||||
// ...
|
||||
CLI11_PARSE(app, argc, argv);
|
||||
}
|
||||
```
|
||||
|
||||
On Linux and macOS `ensure_utf8` returns `argv` unchanged. On Windows it
|
||||
discards `argv` and rebuilds it from the win32 API. Call it before you read or
|
||||
change `argv`, since the values are reconstructed and any earlier change is
|
||||
lost.
|
||||
|
||||
Two alternatives exist. The first is the Windows-only `wmain` function, which
|
||||
gets `wchar_t *argv[]`; CLI11 accepts wide arguments directly:
|
||||
|
||||
```cpp
|
||||
int wmain(int argc, wchar_t *argv[]) {
|
||||
CLI::App app;
|
||||
// ...
|
||||
CLI11_PARSE(app, argc, argv);
|
||||
}
|
||||
```
|
||||
|
||||
The second is to get the arguments yourself with a Windows API such as
|
||||
`CommandLineToArgvW` and pass them to CLI11. That is what `ensure_utf8` does
|
||||
internally.
|
||||
|
||||
## Converting between narrow and wide strings
|
||||
|
||||
```cpp
|
||||
namespace CLI {
|
||||
std::string narrow(const std::wstring &str);
|
||||
std::string narrow(const wchar_t *str);
|
||||
std::string narrow(const wchar_t *str, std::size_t size);
|
||||
std::string narrow(std::wstring_view str); // C++17
|
||||
|
||||
std::wstring widen(const std::string &str);
|
||||
std::wstring widen(const char *str);
|
||||
std::wstring widen(const char *str, std::size_t size);
|
||||
std::wstring widen(std::string_view str); // C++17
|
||||
}
|
||||
```
|
||||
|
||||
## Unicode paths
|
||||
|
||||
A `std::filesystem::path` on Windows must be built from a wide string, or the
|
||||
name is mangled. `CLI::to_path` does the correct conversion on every platform:
|
||||
|
||||
```cpp
|
||||
std::string utf8_name = "Hello Halló Привет 你好 👩🚀❤️.txt";
|
||||
|
||||
std::filesystem::path p = CLI::to_path(utf8_name);
|
||||
std::ifstream stream(CLI::to_path(utf8_name));
|
||||
```
|
||||
|
||||
`to_path` needs `<filesystem>` support (C++17, `CLI11_HAS_FILESYSTEM`).
|
||||
@@ -0,0 +1,334 @@
|
||||
# Validators {#book-validators}
|
||||
|
||||
There are two forms of validators:
|
||||
|
||||
- `transform` validators: mutating
|
||||
- `check` validators: non-mutating (recommended unless the parsed string must be
|
||||
mutated)
|
||||
|
||||
A transform validator comes in one form, a function with the signature
|
||||
`std::string(std::string&)`. The function will take a string and return an error
|
||||
message, or an empty string if input is valid. Alternatively, the function may
|
||||
throw a `CLI::ValidationError` with the appropriate reason as a message; either
|
||||
returning a non-empty string or throwing signals a failure, so throwing is not
|
||||
required.
|
||||
|
||||
An example of a mutating validator:
|
||||
|
||||
```cpp
|
||||
auto transform_validator = CLI::Validator(
|
||||
[](std::string &input) {
|
||||
if (input == "error") {
|
||||
return "error is not a valid value";
|
||||
} else if (input == "unexpected") {
|
||||
throw CLI::ValidationError{"Unexpected error"};
|
||||
}
|
||||
input = "new string";
|
||||
return "";
|
||||
}, "VALIDATOR DESCRIPTION", "Validator name");
|
||||
|
||||
cli_global.add_option("option")->transform(transform_validator);
|
||||
```
|
||||
|
||||
However, `check` validators come in two forms; either a simple function with the
|
||||
const version of the above signature, `std::string(const std::string &)`, or a
|
||||
subclass of `struct CLI::Validator`. This structure has two members that a user
|
||||
should set; one (`func_`) is the function to add to the Option (exactly matching
|
||||
the above function signature, since it will become that function), and the other
|
||||
is `name_`, and is the type name to set on the Option (unless empty, in which
|
||||
case the typename will be left unchanged).
|
||||
|
||||
Validators can be combined with `&` and `|`, and inverted with `!`. They have an
|
||||
`operator()` so that you can call them as if they were a function. In CLI11,
|
||||
const static versions of the validators are provided so that the user does not
|
||||
have to call a constructor also.
|
||||
|
||||
```cpp
|
||||
->check(CLI::Range(0, 10) | CLI::Range(20, 30)); // 0-10 or 20-30
|
||||
->check(!CLI::PositiveNumber); // zero or negative
|
||||
```
|
||||
|
||||
An example of a custom validator:
|
||||
|
||||
```cpp
|
||||
struct LowerCaseValidator : public CLI::Validator {
|
||||
LowerCaseValidator() {
|
||||
name_ = "LOWER";
|
||||
func_ = [](const std::string &str) {
|
||||
if(CLI::detail::to_lower(str) != str)
|
||||
return std::string("String is not lower case");
|
||||
else
|
||||
return std::string();
|
||||
};
|
||||
}
|
||||
};
|
||||
const static LowerCaseValidator Lowercase;
|
||||
```
|
||||
|
||||
If you were not interested in the extra features of Validator, you could simply
|
||||
pass the lambda function above to the `->check()` method of `Option`.
|
||||
|
||||
The built-in validators for CLI11 are:
|
||||
|
||||
| Validator | Description |
|
||||
| ------------------- | ---------------------------------------------------------------------- |
|
||||
| `ExistingFile` | Check for existing file (returns error message if check fails) |
|
||||
| `ExistingDirectory` | Check for an existing directory (returns error message if check fails) |
|
||||
| `ExistingPath` | Check for an existing path |
|
||||
| `NonexistentPath` | Check for an non-existing path |
|
||||
| `Range(min=0, max)` | Produce a range (factory). Min and max are inclusive. |
|
||||
| `NonNegativeNumber` | Range(0,max<double>) |
|
||||
| `PositiveNumber` | Range(denorm_min<double>,max<double>), i.e. any positive number |
|
||||
|
||||
A few built-in transformers are also available
|
||||
|
||||
| Transformer | Description |
|
||||
| ------------------- | ---------------------------------------------------------- |
|
||||
| `EscapedString` | modify a string using defined escape characters |
|
||||
| `FileOnDefaultPath` | Modify a path if the file is a particular default location |
|
||||
|
||||
And, the protected members that you can set when you make your own are:
|
||||
|
||||
| Type | Member | Description |
|
||||
| ------------------------------------------- | -------------------- | ---------------------------------------------------------------------- |
|
||||
| `std::function<std::string(std::string &)>` | `func_` | Core validation function - modifies input and returns "" if successful |
|
||||
| `std::function<std::string()>` | `desc_function_` | Optional description function (returns an empty string if not set) |
|
||||
| `std::string` | `name_` | The name for search purposes |
|
||||
| `int` (`-1`) | `application_index_` | The element this validator applies to (-1 for all) |
|
||||
| `bool` (`true`) | `active_` | This can be disabled |
|
||||
| `bool` (`false`) | `non_modifying_` | Specify that this is a Validator instead of a Transformer |
|
||||
|
||||
## Extra Validators
|
||||
|
||||
Until CLI11 v3.0 these validators will be available by default. They can be
|
||||
disabled at compilation time by defining CLI11_DISABLE_EXTRA_VALIDATORS to 1.
|
||||
After version 3.0 they can be enabled by defining CLI11_ENABLE_EXTRA_VALIDATORS
|
||||
to 1. Some of the Validators are template heavy so if they are not needed and
|
||||
compilation time is a concern they can be disabled.
|
||||
|
||||
| Validator | Description |
|
||||
| -------------------- | ------------------------------------------------------------------ |
|
||||
| `ValidIPV4` | check for valid IPV4 address XX.XX.XX.XX |
|
||||
| `TypeValidator<T>` | template for checking that a value can convert to a specific type |
|
||||
| `Number` | Check that a value can convert to a number |
|
||||
| `IsMember` | Check that a value is one of a set of values |
|
||||
| `CheckedTransformer` | Values must be one of the transformed set or the result |
|
||||
| `AsNumberWithUnit` | checks for numbers with a unit as part of a specified set of units |
|
||||
| `AsSizeValue` | As Number with Unit with support for SI prefixes |
|
||||
|
||||
| Transformer | Description |
|
||||
| --------------------------------- | ---------------------------------------------------------------------- |
|
||||
| `Bound(min, max)` or `Bound(max)` | Force a range (factory). Min and max are inclusive; min defaults to 0. |
|
||||
| `Transformer` | Modify values in a set to the matching pair value |
|
||||
|
||||
## New Extra Validators
|
||||
|
||||
Some additional validators can be enabled by using CLI11_ENABLE_EXTRA_VALIDATORS
|
||||
to 1. These validators are disabled by default. They also require `<filesystem>`
|
||||
support (C++17, `CLI11_HAS_FILESYSTEM`).
|
||||
|
||||
| Validator | Description |
|
||||
| ------------------- | ----------------------------------------------------------- |
|
||||
| `ReadPermissions` | Ensure a file or directory has permissions to read the file |
|
||||
| `WritePermissions` | Ensure a file or directory has write permissions |
|
||||
| `ExecPermissions` | Ensure a file has exec permissions |
|
||||
| `NonEmptyFile` | Ensure that a file exists and is not empty |
|
||||
| `FileSizeValidator` | specify that the size must be between min and max sizes |
|
||||
|
||||
## Sets and transforms
|
||||
|
||||
`IsMember` restricts a value to a set. Pass any iterable container with a
|
||||
`::value_type`, or a copyable pointer to one, or an initializer list:
|
||||
|
||||
```cpp
|
||||
->check(CLI::IsMember({"choice1", "choice2"}));
|
||||
->check(CLI::IsMember(std::set<int>({2, 3, 4})));
|
||||
```
|
||||
|
||||
After the set you can pass "filter" functions of the form `T(T)`, which are
|
||||
applied before the comparison. `CLI::ignore_case`, `CLI::ignore_underscore`, and
|
||||
`CLI::ignore_space` are provided:
|
||||
|
||||
```cpp
|
||||
->check(CLI::IsMember({"choice1", "choice2"}, CLI::ignore_case, CLI::ignore_underscore));
|
||||
```
|
||||
|
||||
`Transformer` and `CheckedTransformer` map one value to another. They take
|
||||
containers of pairs, so a map is the usual choice:
|
||||
|
||||
```cpp
|
||||
std::map<std::string, int> levels{{"one", 1}, {"two", 2}};
|
||||
->transform(CLI::CheckedTransformer(levels));
|
||||
```
|
||||
|
||||
`Transformer` passes values that are not in the map through unchanged.
|
||||
`CheckedTransformer` requires the value to match either a key or one of the
|
||||
expected outputs, and raises a `ValidationError` if it does not. A `Transformer`
|
||||
used with `check` does nothing, since `check` may not modify the value.
|
||||
|
||||
If you pass a shared pointer to the container instead of the container itself,
|
||||
you can change the contents later, and the help text and the check both follow
|
||||
the current contents:
|
||||
|
||||
```cpp
|
||||
auto p = std::make_shared<std::vector<std::string>>(
|
||||
std::initializer_list<std::string>{"one", "two"});
|
||||
->check(CLI::IsMember(p));
|
||||
```
|
||||
|
||||
`TransformPairs<T>` is an alias for `std::vector<std::pair<std::string, T>>` for
|
||||
the same use with the transformers. If the container has a `find` function, like
|
||||
`std::map`, that function does the search; otherwise the search is linear. With
|
||||
filters present, the fast search runs first and a filtered linear search is the
|
||||
fallback.
|
||||
|
||||
## Validator operations
|
||||
|
||||
A Validator is a copyable object with settings you can change. Every one of
|
||||
these functions returns a reference to the Validator, so they chain:
|
||||
|
||||
| Operation | Effect |
|
||||
| ------------------------- | -------------------------------------------------------------------------------------------- |
|
||||
| `.description(text)` | Replace the description shown in the help. |
|
||||
| `.name(text)` | Name the Validator, so it can be found later with `get_validator(name)`. |
|
||||
| `.active(bool)` | Turn the Validator on or off. |
|
||||
| `.application_index(int)` | Apply only to one element of the result. Zero based; a negative index applies to all values. |
|
||||
| `.operation(func)` | Replace the validation function. |
|
||||
|
||||
The application index is what makes validation of compound types work. For a
|
||||
`std::pair<int, std::string>` where the first element must be positive and the
|
||||
second must be a file:
|
||||
|
||||
```cpp
|
||||
opt->check(CLI::Validator(CLI::PositiveNumber).application_index(0));
|
||||
opt->check(CLI::Validator(CLI::ExistingFile).application_index(1));
|
||||
```
|
||||
|
||||
A named Validator can be found and changed after the option is built, which is
|
||||
how you turn a check on or off at runtime:
|
||||
|
||||
```cpp
|
||||
opt->check(CLI::Range(10, 20).description("sensible values").active(false).name("range"));
|
||||
// later
|
||||
opt->get_validator("range")->active();
|
||||
```
|
||||
|
||||
`get_validator(name)` throws `CLI::OptionNotFound` if there is no match. With no
|
||||
name, or an empty name, it returns the first unnamed Validator, or the only one
|
||||
if there is just one. `get_validator(index)` takes the position in the applied
|
||||
order instead, and returns `nullptr` for an invalid index.
|
||||
|
||||
For reading the current state there are `get_description()`, `get_name()`,
|
||||
`get_active()`, `get_application_index()`, and `get_modifying()`, which is true
|
||||
when the Validator may change the value. Modification is controlled by
|
||||
`non_modifying()`, but it is better to let `check` and `transform` set it.
|
||||
|
||||
## Custom Validators
|
||||
|
||||
CLI11 also supports the use of custom validators, this includes using the
|
||||
Validator class constructor with a custom function calls or subclassing
|
||||
Validator to define a new class.
|
||||
|
||||
### Custom Validator operation
|
||||
|
||||
The simplest way to make a new Validator is to mimic how many of the existing
|
||||
Validators are created. Take for example the `IPV4Validator`
|
||||
|
||||
```cpp
|
||||
class IPV4Validator : public Validator {
|
||||
public:
|
||||
IPV4Validator();
|
||||
};
|
||||
|
||||
CLI11_INLINE IPV4Validator::IPV4Validator() : Validator("IPV4") {
|
||||
func_ = [](std::string &ip_addr) {
|
||||
auto cdot = std::count(ip_addr.begin(), ip_addr.end(), '.');
|
||||
if(cdot != 3u) {
|
||||
return std::string("Invalid IPV4 address: must have 3 separators");
|
||||
}
|
||||
auto result = CLI::detail::split(ip_addr, '.');
|
||||
if(result.size() != 4) {
|
||||
return std::string("Invalid IPV4 address: must have four parts (") + ip_addr + ')';
|
||||
}
|
||||
int num = 0;
|
||||
for(const auto &var : result) {
|
||||
using CLI::detail::lexical_cast;
|
||||
bool retval = lexical_cast(var, num);
|
||||
if(!retval) {
|
||||
return std::string("Failed parsing number (") + var + ')';
|
||||
}
|
||||
if(num < 0 || num > 255) {
|
||||
return std::string("Each IP number must be between 0 and 255 ") + var;
|
||||
}
|
||||
}
|
||||
return std::string{};
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
The `IPV4Validator` class inherits from `Validator` and creates a new
|
||||
constructor. In that constructor it defines the lambda function that does the
|
||||
checking. Then IPV4 can be used like any other Validator. One specific item of
|
||||
note is that the class does not define any new member variables, so the class if
|
||||
copyable to a Validator, only the constructor is different.
|
||||
|
||||
If additional members are needed, then the `check` and `transform` overloads
|
||||
that use shared pointers need to be used. The other overloads pass by value so
|
||||
polymorphism doesn't work. The custom_validator example shows a case like this.
|
||||
|
||||
```cpp
|
||||
template <typename T> class DeltaRange : public CLI::Validator {
|
||||
public:
|
||||
T center_point;
|
||||
T delta;
|
||||
DeltaRange(const T ¢er, const T &range)
|
||||
: CLI::Validator(
|
||||
[this](const std::string &value) -> std::string {
|
||||
T newValue;
|
||||
auto result = CLI::detail::lexical_cast(value, newValue);
|
||||
if(!(result && this->check(newValue))) {
|
||||
return std::string("value not within range");
|
||||
}
|
||||
return std::string{};
|
||||
},
|
||||
"RANGE"),
|
||||
center_point(center), delta(range) {}
|
||||
|
||||
CLI11_NODISCARD bool check(const T &test) const { return (test >= (center_point - delta)) && (test <= (center_point + delta)); }
|
||||
CLI11_NODISCARD T center() const { return center_point; }
|
||||
CLI11_NODISCARD T range() const { return delta; }
|
||||
void center(const T &value) { center_point = value; }
|
||||
void range(const T &value) { delta = value; }
|
||||
};
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
/* this application creates custom validator which is a range center+/- range The center and range can be defined by
|
||||
* other command line options and are updated dynamically
|
||||
*/
|
||||
CLI::App app("custom range validator");
|
||||
|
||||
std::string value;
|
||||
auto dr = std::make_shared<DeltaRange<int>>(7, 3);
|
||||
app.add_option("--number", value, "enter value in the related range")->check(dr)->required();
|
||||
|
||||
app.add_option_function<int>("--center", [&dr](int new_center) { dr->center(new_center); })->trigger_on_parse();
|
||||
app.add_option_function<int>("--range", [&dr](int new_range) { dr->range(new_range); })->trigger_on_parse();
|
||||
|
||||
CLI11_PARSE(app, argc, argv);
|
||||
|
||||
std::cout << "number " << value << " in range = " << dr->center() << " +/- " << dr->range() << '\n';
|
||||
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
The Validator defines some new operations, and in the use case the Validator is
|
||||
constructed using shared_ptrs. This allows polymorphism to work and the
|
||||
Validator instance to be shared across multiple options, and as in this example
|
||||
adapted during the parsing and checking.
|
||||
|
||||
There are a few limitation in this, single instances should not be used with
|
||||
both transform and check. Check modifies some flags in the Validator to prevent
|
||||
value modification, so that would prevent its use as a transform. Which could be
|
||||
user modified later but that would potentially allow the check to modify the
|
||||
value unintentionally.
|
||||
@@ -0,0 +1,38 @@
|
||||
# Copyright (c) 2017-2026, University of Cincinnati, developed by Henry Schreiner
|
||||
# under NSF AWARD 1414736 and by the respective contributors.
|
||||
# All rights reserved.
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
cmake_minimum_required(VERSION 3.14...4.0)
|
||||
|
||||
project(CLI11_Examples LANGUAGES CXX)
|
||||
|
||||
# Using CMake ability to set imported interface targets
|
||||
add_library(CLI11::CLI11 IMPORTED INTERFACE)
|
||||
target_include_directories(CLI11::CLI11 INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/../../include")
|
||||
target_compile_features(CLI11::CLI11 INTERFACE cxx_std_11)
|
||||
|
||||
# Add CTest
|
||||
enable_testing()
|
||||
|
||||
# Quick function to add the base executable
|
||||
function(add_cli_exe NAME)
|
||||
add_executable(${NAME} ${NAME}.cpp)
|
||||
target_link_libraries(${NAME} CLI11::CLI11)
|
||||
endfunction()
|
||||
|
||||
add_cli_exe(simplest)
|
||||
add_test(NAME simplest COMMAND simplest)
|
||||
|
||||
add_cli_exe(intro)
|
||||
add_test(NAME intro COMMAND intro)
|
||||
add_test(NAME intro_p COMMAND intro -p 5)
|
||||
|
||||
add_cli_exe(flags)
|
||||
add_test(NAME flags COMMAND flags)
|
||||
add_test(NAME flags_bip COMMAND flags -b -i -p)
|
||||
|
||||
add_cli_exe(geet)
|
||||
add_test(NAME geet_add COMMAND geet add)
|
||||
add_test(NAME geet_commit COMMAND geet commit -m "Test")
|
||||
@@ -0,0 +1,36 @@
|
||||
#include "CLI/CLI.hpp"
|
||||
#include <iostream>
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
CLI::App app{"Flag example program"};
|
||||
|
||||
/// [define]
|
||||
bool flag_bool;
|
||||
app.add_flag("--bool,-b", flag_bool, "This is a bool flag");
|
||||
|
||||
int flag_int;
|
||||
app.add_flag("-i,--int", flag_int, "This is an int flag");
|
||||
|
||||
CLI::Option *flag_plain = app.add_flag("--plain,-p", "This is a plain flag");
|
||||
/// [define]
|
||||
|
||||
/// [parser]
|
||||
try {
|
||||
app.parse(argc, argv);
|
||||
} catch(const CLI::ParseError &e) {
|
||||
return app.exit(e);
|
||||
}
|
||||
/// [parser]
|
||||
|
||||
/// [usage]
|
||||
cout << "The flags program" << endl;
|
||||
if(flag_bool)
|
||||
cout << "Bool flag passed" << endl;
|
||||
if(flag_int > 0)
|
||||
cout << "Flag int: " << flag_int << endl;
|
||||
if(*flag_plain)
|
||||
cout << "Flag plain: " << flag_plain->count() << endl;
|
||||
/// [usage]
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
#include "CLI/CLI.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
|
||||
/// [Intro]
|
||||
CLI::App app{"Geet, a command line git lookalike that does nothing"};
|
||||
app.require_subcommand(1);
|
||||
/// [Intro]
|
||||
|
||||
/// [Add]
|
||||
auto add = app.add_subcommand("add", "Add file(s)");
|
||||
|
||||
bool add_update;
|
||||
add->add_flag("-u,--update", add_update, "Add updated files only");
|
||||
|
||||
std::vector<std::string> add_files;
|
||||
add->add_option("files", add_files, "Files to add");
|
||||
|
||||
add->callback([&]() {
|
||||
std::cout << "Adding:";
|
||||
if(add_files.empty()) {
|
||||
if(add_update)
|
||||
std::cout << " all updated files";
|
||||
else
|
||||
std::cout << " all files";
|
||||
} else {
|
||||
for(auto file : add_files)
|
||||
std::cout << " " << file;
|
||||
}
|
||||
});
|
||||
/// [Add]
|
||||
|
||||
/// [Commit]
|
||||
auto commit = app.add_subcommand("commit", "Commit files");
|
||||
|
||||
std::string commit_message;
|
||||
commit->add_option("-m,--message", commit_message, "A message")->required();
|
||||
|
||||
commit->callback([&]() { std::cout << "Commit message: " << commit_message; });
|
||||
/// [Commit]
|
||||
|
||||
/// [Parse]
|
||||
CLI11_PARSE(app, argc, argv);
|
||||
|
||||
std::cout << "\nThanks for using geet!\n" << std::endl;
|
||||
return 0;
|
||||
/// [Parse]
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
#include "CLI/CLI.hpp"
|
||||
#include <iostream>
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
CLI::App app{"App description"};
|
||||
|
||||
// Define options
|
||||
int p = 0;
|
||||
app.add_option("-p", p, "Parameter");
|
||||
|
||||
CLI11_PARSE(app, argc, argv);
|
||||
|
||||
std::cout << "Parameter value: " << p << std::endl;
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#include "CLI/CLI.hpp"
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
CLI::App app;
|
||||
|
||||
// Add new options/flags here
|
||||
|
||||
CLI11_PARSE(app, argc, argv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 982 KiB |
@@ -0,0 +1 @@
|
||||
CLI11 provides all the features you expect in a powerful command line parser, with a beautiful, minimal syntax and no dependencies beyond C++11. It is header only, and comes in a single file form for easy inclusion in projects. It is easy to use for small projects, but powerful enough for complex command line projects, and can be customized for frameworks.
|
||||
@@ -0,0 +1,9 @@
|
||||
prefix=@CMAKE_INSTALL_PREFIX@
|
||||
exec_prefix=${prefix}
|
||||
includedir=${prefix}/include
|
||||
|
||||
Name: CLI11
|
||||
Description: C++ command line parser
|
||||
Version: @PROJECT_VERSION@
|
||||
|
||||
Cflags: -I${includedir}
|
||||
@@ -0,0 +1,16 @@
|
||||
# Copyright (c) 2017-2026, University of Cincinnati, developed by Henry Schreiner
|
||||
# under NSF AWARD 1414736 and by the respective contributors.
|
||||
# All rights reserved.
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/CLI11Targets.cmake")
|
||||
|
||||
# Give the module target its short name. ALIAS of an imported target needs
|
||||
# CMake 3.18+; modules need 3.28+ anyway, but keep this file loadable on the
|
||||
# project minimum (3.14) when the module target is absent.
|
||||
if(TARGET CLI11::CLI11_Module
|
||||
AND NOT TARGET CLI11::Module
|
||||
AND CMAKE_VERSION VERSION_GREATER_EQUAL 3.18)
|
||||
add_library(CLI11::Module ALIAS CLI11::CLI11_Module)
|
||||
endif()
|
||||
@@ -0,0 +1,13 @@
|
||||
# Adapted from write_basic_package_version_file(... COMPATIBILITY AnyNewerVersion) output
|
||||
# ARCH_INDEPENDENT is only present in cmake 3.14 and onwards
|
||||
|
||||
set(PACKAGE_VERSION "@VERSION_STRING@")
|
||||
|
||||
if(PACKAGE_VERSION VERSION_LESS PACKAGE_FIND_VERSION)
|
||||
set(PACKAGE_VERSION_COMPATIBLE FALSE)
|
||||
else()
|
||||
set(PACKAGE_VERSION_COMPATIBLE TRUE)
|
||||
if(PACKAGE_FIND_VERSION STREQUAL PACKAGE_VERSION)
|
||||
set(PACKAGE_VERSION_EXACT TRUE)
|
||||
endif()
|
||||
endif()
|
||||
@@ -0,0 +1,7 @@
|
||||
if(CLI11_PRECOMPILED)
|
||||
configure_file("cmake/CLI11precompiled.pc.in" "CLI11.pc" @ONLY)
|
||||
else()
|
||||
configure_file("cmake/CLI11.pc.in" "CLI11.pc" @ONLY)
|
||||
endif()
|
||||
|
||||
install(FILES "${PROJECT_BINARY_DIR}/CLI11.pc" DESTINATION "${CMAKE_INSTALL_DATADIR}/pkgconfig")
|
||||
@@ -0,0 +1,26 @@
|
||||
# Special target that adds warnings. Is not exported.
|
||||
add_library(CLI11_warnings INTERFACE)
|
||||
|
||||
set(unix-warnings -Wall -Wextra -pedantic -Wshadow -Wsign-conversion -Wswitch-enum)
|
||||
|
||||
# Clang warnings
|
||||
# -Wfloat-equal could be added with Catch::literals and _a usage
|
||||
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
list(APPEND unix-warnings -Wcast-align -Wmissing-declarations -Woverlength-strings)
|
||||
# -Wunreachable-code Doesn't work on Clang 3.4
|
||||
endif()
|
||||
|
||||
# Buggy in GCC 4.8
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9)
|
||||
list(APPEND unix-warnings -Weffc++)
|
||||
endif()
|
||||
|
||||
target_compile_options(
|
||||
CLI11_warnings
|
||||
INTERFACE $<$<BOOL:${CLI11_FORCE_LIBCXX}>:-stdlib=libc++>
|
||||
$<$<CXX_COMPILER_ID:MSVC>:/W4
|
||||
$<$<BOOL:${CLI11_WARNINGS_AS_ERRORS}>:/WX>>
|
||||
$<$<NOT:$<CXX_COMPILER_ID:MSVC>>:${unix-warnings}
|
||||
$<$<BOOL:${CLI11_WARNINGS_AS_ERRORS}>:-Werror>>)
|
||||
|
||||
target_link_options(CLI11_warnings INTERFACE $<$<BOOL:${CLI11_FORCE_LIBCXX}>:-stdlib=libc++>)
|
||||
@@ -0,0 +1,11 @@
|
||||
prefix=@CMAKE_INSTALL_PREFIX@
|
||||
exec_prefix=${prefix}
|
||||
includedir=${prefix}/include
|
||||
libdir=${exec_prefix}/lib
|
||||
|
||||
Name: CLI11
|
||||
Description: C++ command line parser
|
||||
Version: @PROJECT_VERSION@
|
||||
|
||||
Cflags: -I${includedir} -DCLI11_COMPILE
|
||||
Libs: -L${libdir} -lCLI11
|
||||
@@ -0,0 +1,818 @@
|
||||
# Copyright (c) 2012 - 2017, Lars Bilke
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
# are permitted provided that the following conditions are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright notice, this
|
||||
# list of conditions and the following disclaimer.
|
||||
#
|
||||
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
#
|
||||
# 3. Neither the name of the copyright holder nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software without
|
||||
# specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#
|
||||
# CHANGES:
|
||||
#
|
||||
# 2012-01-31, Lars Bilke
|
||||
# - Enable Code Coverage
|
||||
#
|
||||
# 2013-09-17, Joakim Söderberg
|
||||
# - Added support for Clang.
|
||||
# - Some additional usage instructions.
|
||||
#
|
||||
# 2016-02-03, Lars Bilke
|
||||
# - Refactored functions to use named parameters
|
||||
#
|
||||
# 2017-06-02, Lars Bilke
|
||||
# - Merged with modified version from github.com/ufz/ogs
|
||||
#
|
||||
# 2019-05-06, Anatolii Kurotych
|
||||
# - Remove unnecessary --coverage flag
|
||||
#
|
||||
# 2019-12-13, FeRD (Frank Dana)
|
||||
# - Deprecate COVERAGE_LCOVR_EXCLUDES and COVERAGE_GCOVR_EXCLUDES lists in favor
|
||||
# of tool-agnostic COVERAGE_EXCLUDES variable, or EXCLUDE setup arguments.
|
||||
# - CMake 3.4+: All excludes can be specified relative to BASE_DIRECTORY
|
||||
# - All setup functions: accept BASE_DIRECTORY, EXCLUDE list
|
||||
# - Set lcov basedir with -b argument
|
||||
# - Add automatic --demangle-cpp in lcovr, if 'c++filt' is available (can be
|
||||
# overridden with NO_DEMANGLE option in setup_target_for_coverage_lcovr().)
|
||||
# - Delete output dir, .info file on 'make clean'
|
||||
# - Remove Python detection, since version mismatches will break gcovr
|
||||
# - Minor cleanup (lowercase function names, update examples...)
|
||||
#
|
||||
# 2019-12-19, FeRD (Frank Dana)
|
||||
# - Rename Lcov outputs, make filtered file canonical, fix cleanup for targets
|
||||
#
|
||||
# 2020-01-19, Bob Apthorpe
|
||||
# - Added gfortran support
|
||||
#
|
||||
# 2020-02-17, FeRD (Frank Dana)
|
||||
# - Make all add_custom_target()s VERBATIM to auto-escape wildcard characters
|
||||
# in EXCLUDEs, and remove manual escaping from gcovr targets
|
||||
#
|
||||
# 2021-01-19, Robin Mueller
|
||||
# - Add CODE_COVERAGE_VERBOSE option which will allow to print out commands which are run
|
||||
# - Added the option for users to set the GCOVR_ADDITIONAL_ARGS variable to supply additional
|
||||
# flags to the gcovr command
|
||||
#
|
||||
# 2020-05-04, Michael Davis
|
||||
# - Add -fprofile-abs-path to make gcno files contain absolute paths
|
||||
# - Fix BASE_DIRECTORY not working when defined
|
||||
# - Change BYPRODUCT from folder to index.html to stop ninja from complaining about double defines
|
||||
#
|
||||
# 2021-05-10, Martin Stump
|
||||
# - Check if the generator is multi-config before warning about non-Debug builds
|
||||
#
|
||||
# 2022-02-22, Marko Wehle
|
||||
# - Change gcovr output from -o <filename> for --xml <filename> and --html <filename> output respectively.
|
||||
# This will allow for Multiple Output Formats at the same time by making use of GCOVR_ADDITIONAL_ARGS, e.g. GCOVR_ADDITIONAL_ARGS "--txt".
|
||||
#
|
||||
# 2022-09-28, Sebastian Mueller
|
||||
# - fix append_coverage_compiler_flags_to_target to correctly add flags
|
||||
# - replace "-fprofile-arcs -ftest-coverage" with "--coverage" (equivalent)
|
||||
#
|
||||
# USAGE:
|
||||
#
|
||||
# 1. Copy this file into your cmake modules path.
|
||||
#
|
||||
# 2. Add the following line to your CMakeLists.txt (best inside an if-condition
|
||||
# using a CMake option() to enable it just optionally):
|
||||
# include(CodeCoverage)
|
||||
#
|
||||
# 3. Append necessary compiler flags for all supported source files:
|
||||
# append_coverage_compiler_flags()
|
||||
# Or for specific target:
|
||||
# append_coverage_compiler_flags_to_target(YOUR_TARGET_NAME)
|
||||
#
|
||||
# 3.a (OPTIONAL) Set appropriate optimization flags, e.g. -O0, -O1 or -Og
|
||||
#
|
||||
# 4. If you need to exclude additional directories from the report, specify them
|
||||
# using full paths in the COVERAGE_EXCLUDES variable before calling
|
||||
# setup_target_for_coverage_*().
|
||||
# Example:
|
||||
# set(COVERAGE_EXCLUDES
|
||||
# '${PROJECT_SOURCE_DIR}/src/dir1/*'
|
||||
# '/path/to/my/src/dir2/*')
|
||||
# Or, use the EXCLUDE argument to setup_target_for_coverage_*().
|
||||
# Example:
|
||||
# setup_target_for_coverage_lcov(
|
||||
# NAME coverage
|
||||
# EXECUTABLE testrunner
|
||||
# EXCLUDE "${PROJECT_SOURCE_DIR}/src/dir1/*" "/path/to/my/src/dir2/*")
|
||||
#
|
||||
# 4.a NOTE: With CMake 3.4+, COVERAGE_EXCLUDES or EXCLUDE can also be set
|
||||
# relative to the BASE_DIRECTORY (default: PROJECT_SOURCE_DIR)
|
||||
# Example:
|
||||
# set(COVERAGE_EXCLUDES "dir1/*")
|
||||
# setup_target_for_coverage_gcovr_html(
|
||||
# NAME coverage
|
||||
# EXECUTABLE testrunner
|
||||
# BASE_DIRECTORY "${PROJECT_SOURCE_DIR}/src"
|
||||
# EXCLUDE "dir2/*")
|
||||
#
|
||||
# 5. Use the functions described below to create a custom make target which
|
||||
# runs your test executable and produces a code coverage report.
|
||||
#
|
||||
# 6. Build a Debug build:
|
||||
# cmake -DCMAKE_BUILD_TYPE=Debug ..
|
||||
# make
|
||||
# make my_coverage_target
|
||||
#
|
||||
|
||||
include(CMakeParseArguments)
|
||||
|
||||
option(CODE_COVERAGE_VERBOSE "Verbose information" FALSE)
|
||||
|
||||
# Check prereqs
|
||||
find_program(GCOV_PATH gcov)
|
||||
find_program(LCOV_PATH NAMES lcov lcov.bat lcov.exe lcov.perl)
|
||||
find_program(FASTCOV_PATH NAMES fastcov fastcov.py)
|
||||
find_program(GENHTML_PATH NAMES genhtml genhtml.perl genhtml.bat)
|
||||
find_program(GCOVR_PATH gcovr PATHS ${CMAKE_SOURCE_DIR}/scripts/test)
|
||||
find_program(CPPFILT_PATH NAMES c++filt)
|
||||
|
||||
if(NOT GCOV_PATH)
|
||||
message(FATAL_ERROR "gcov not found! Aborting...")
|
||||
endif() # NOT GCOV_PATH
|
||||
|
||||
# Check supported compiler (Clang, GNU and Flang)
|
||||
get_property(LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES)
|
||||
foreach(LANG ${LANGUAGES})
|
||||
if("${CMAKE_${LANG}_COMPILER_ID}" MATCHES "(Apple)?[Cc]lang")
|
||||
if("${CMAKE_${LANG}_COMPILER_VERSION}" VERSION_LESS 3)
|
||||
message(FATAL_ERROR "Clang version must be 3.0.0 or greater! Aborting...")
|
||||
endif()
|
||||
elseif(NOT "${CMAKE_${LANG}_COMPILER_ID}" MATCHES "GNU" AND NOT "${CMAKE_${LANG}_COMPILER_ID}"
|
||||
MATCHES "(LLVM)?[Ff]lang")
|
||||
message(FATAL_ERROR "Compiler is not GNU or Flang! Aborting...")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
set(COVERAGE_COMPILER_FLAGS
|
||||
"-g -O0 --coverage -fprofile-arcs -ftest-coverage -fno-inline -fno-inline-small-functions -fno-default-inline -fno-elide-constructors"
|
||||
CACHE INTERNAL "")
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Clang)")
|
||||
include(CheckCXXCompilerFlag)
|
||||
check_cxx_compiler_flag(-fprofile-abs-path HAVE_cxx_fprofile_abs_path)
|
||||
if(HAVE_cxx_fprofile_abs_path)
|
||||
set(COVERAGE_CXX_COMPILER_FLAGS "${COVERAGE_COMPILER_FLAGS} -fprofile-abs-path")
|
||||
endif()
|
||||
endif()
|
||||
if(CMAKE_C_COMPILER_ID MATCHES "(GNU|Clang)")
|
||||
include(CheckCCompilerFlag)
|
||||
check_c_compiler_flag(-fprofile-abs-path HAVE_c_fprofile_abs_path)
|
||||
if(HAVE_c_fprofile_abs_path)
|
||||
set(COVERAGE_C_COMPILER_FLAGS "${COVERAGE_COMPILER_FLAGS} -fprofile-abs-path")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(CMAKE_Fortran_FLAGS_COVERAGE
|
||||
${COVERAGE_COMPILER_FLAGS}
|
||||
CACHE STRING "Flags used by the Fortran compiler during coverage builds." FORCE)
|
||||
set(CMAKE_CXX_FLAGS_COVERAGE
|
||||
${COVERAGE_COMPILER_FLAGS}
|
||||
CACHE STRING "Flags used by the C++ compiler during coverage builds." FORCE)
|
||||
set(CMAKE_C_FLAGS_COVERAGE
|
||||
${COVERAGE_COMPILER_FLAGS}
|
||||
CACHE STRING "Flags used by the C compiler during coverage builds." FORCE)
|
||||
set(CMAKE_EXE_LINKER_FLAGS_COVERAGE
|
||||
""
|
||||
CACHE STRING "Flags used for linking binaries during coverage builds." FORCE)
|
||||
set(CMAKE_SHARED_LINKER_FLAGS_COVERAGE
|
||||
""
|
||||
CACHE STRING "Flags used by the shared libraries linker during coverage builds." FORCE)
|
||||
mark_as_advanced(CMAKE_Fortran_FLAGS_COVERAGE CMAKE_CXX_FLAGS_COVERAGE CMAKE_C_FLAGS_COVERAGE
|
||||
CMAKE_EXE_LINKER_FLAGS_COVERAGE CMAKE_SHARED_LINKER_FLAGS_COVERAGE)
|
||||
|
||||
get_property(GENERATOR_IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
|
||||
if(NOT (CMAKE_BUILD_TYPE STREQUAL "Debug" OR GENERATOR_IS_MULTI_CONFIG))
|
||||
message(WARNING "Code coverage results with an optimised (non-Debug) build may be misleading")
|
||||
endif() # NOT (CMAKE_BUILD_TYPE STREQUAL "Debug" OR GENERATOR_IS_MULTI_CONFIG)
|
||||
|
||||
if(CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_Fortran_COMPILER_ID STREQUAL "GNU")
|
||||
link_libraries(gcov)
|
||||
endif()
|
||||
|
||||
# Defines a target for running and collection code coverage information
|
||||
# Builds dependencies, runs the given executable and outputs reports.
|
||||
# NOTE! The executable should always have a ZERO as exit code otherwise
|
||||
# the coverage generation will not complete.
|
||||
#
|
||||
# setup_target_for_coverage_lcov(
|
||||
# NAME testrunner_coverage # New target name
|
||||
# EXECUTABLE testrunner -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR
|
||||
# DEPENDENCIES testrunner # Dependencies to build first
|
||||
# BASE_DIRECTORY "../" # Base directory for report
|
||||
# # (defaults to PROJECT_SOURCE_DIR)
|
||||
# EXCLUDE "src/dir1/*" "src/dir2/*" # Patterns to exclude (can be relative
|
||||
# # to BASE_DIRECTORY, with CMake 3.4+)
|
||||
# NO_DEMANGLE # Don't demangle C++ symbols
|
||||
# # even if c++filt is found
|
||||
# )
|
||||
function(setup_target_for_coverage_lcov)
|
||||
|
||||
set(options NO_DEMANGLE SONARQUBE)
|
||||
set(oneValueArgs BASE_DIRECTORY NAME)
|
||||
set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES LCOV_ARGS GENHTML_ARGS)
|
||||
cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
|
||||
if(NOT LCOV_PATH)
|
||||
message(FATAL_ERROR "lcov not found! Aborting...")
|
||||
endif() # NOT LCOV_PATH
|
||||
|
||||
if(NOT GENHTML_PATH)
|
||||
message(FATAL_ERROR "genhtml not found! Aborting...")
|
||||
endif() # NOT GENHTML_PATH
|
||||
|
||||
# Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR
|
||||
if(DEFINED Coverage_BASE_DIRECTORY)
|
||||
get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE)
|
||||
else()
|
||||
set(BASEDIR ${PROJECT_SOURCE_DIR})
|
||||
endif()
|
||||
|
||||
# Collect excludes (CMake 3.4+: Also compute absolute paths)
|
||||
set(LCOV_EXCLUDES "")
|
||||
foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_LCOV_EXCLUDES})
|
||||
if(CMAKE_VERSION VERSION_GREATER 3.4)
|
||||
get_filename_component(EXCLUDE ${EXCLUDE} ABSOLUTE BASE_DIR ${BASEDIR})
|
||||
endif()
|
||||
list(APPEND LCOV_EXCLUDES "${EXCLUDE}")
|
||||
endforeach()
|
||||
list(REMOVE_DUPLICATES LCOV_EXCLUDES)
|
||||
|
||||
# Conditional arguments
|
||||
if(CPPFILT_PATH AND NOT ${Coverage_NO_DEMANGLE})
|
||||
set(GENHTML_EXTRA_ARGS "--demangle-cpp")
|
||||
endif()
|
||||
|
||||
# Setting up commands which will be run to generate coverage data.
|
||||
# Cleanup lcov
|
||||
set(LCOV_CLEAN_CMD
|
||||
${LCOV_PATH}
|
||||
${Coverage_LCOV_ARGS}
|
||||
--gcov-tool
|
||||
${GCOV_PATH}
|
||||
--ignore-errors
|
||||
mismatch
|
||||
-directory
|
||||
.
|
||||
-b
|
||||
${BASEDIR}
|
||||
--zerocounters)
|
||||
# Create baseline to make sure untouched files show up in the report
|
||||
set(LCOV_BASELINE_CMD
|
||||
${LCOV_PATH}
|
||||
${Coverage_LCOV_ARGS}
|
||||
--gcov-tool
|
||||
${GCOV_PATH}
|
||||
--ignore-errors
|
||||
mismatch
|
||||
-c
|
||||
-i
|
||||
-d
|
||||
.
|
||||
-b
|
||||
${BASEDIR}
|
||||
-o
|
||||
${Coverage_NAME}.base)
|
||||
# Run tests
|
||||
set(LCOV_EXEC_TESTS_CMD ${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS})
|
||||
# Capturing lcov counters and generating report
|
||||
set(LCOV_CAPTURE_CMD
|
||||
${LCOV_PATH}
|
||||
${Coverage_LCOV_ARGS}
|
||||
--gcov-tool
|
||||
${GCOV_PATH}
|
||||
--directory
|
||||
.
|
||||
-b
|
||||
${BASEDIR}
|
||||
--capture
|
||||
--ignore-errors
|
||||
mismatch,gcov
|
||||
--output-file
|
||||
${Coverage_NAME}.capture)
|
||||
# add baseline counters
|
||||
set(LCOV_BASELINE_COUNT_CMD
|
||||
${LCOV_PATH}
|
||||
${Coverage_LCOV_ARGS}
|
||||
--gcov-tool
|
||||
${GCOV_PATH}
|
||||
-a
|
||||
${Coverage_NAME}.base
|
||||
-a
|
||||
${Coverage_NAME}.capture
|
||||
--ignore-errors
|
||||
mismatch,gcov
|
||||
--output-file
|
||||
${Coverage_NAME}.total)
|
||||
# filter collected data to final coverage report
|
||||
set(LCOV_FILTER_CMD
|
||||
${LCOV_PATH}
|
||||
${Coverage_LCOV_ARGS}
|
||||
--gcov-tool
|
||||
${GCOV_PATH}
|
||||
--ignore-errors
|
||||
mismatch,mismatch,gcov
|
||||
--remove
|
||||
${Coverage_NAME}.total
|
||||
${LCOV_EXCLUDES}
|
||||
--output-file
|
||||
${Coverage_NAME}.info)
|
||||
# Generate HTML output
|
||||
set(LCOV_GEN_HTML_CMD
|
||||
${GENHTML_PATH}
|
||||
${GENHTML_EXTRA_ARGS}
|
||||
--ignore-errors
|
||||
mismatch,mismatch
|
||||
${Coverage_GENHTML_ARGS}
|
||||
-o
|
||||
${Coverage_NAME}
|
||||
${Coverage_NAME}.info)
|
||||
if(${Coverage_SONARQUBE})
|
||||
# Generate SonarQube output
|
||||
set(GCOVR_XML_CMD
|
||||
${GCOVR_PATH}
|
||||
--sonarqube
|
||||
${Coverage_NAME}_sonarqube.xml
|
||||
-r
|
||||
${BASEDIR}
|
||||
${GCOVR_ADDITIONAL_ARGS}
|
||||
${GCOVR_EXCLUDE_ARGS}
|
||||
--object-directory=${PROJECT_BINARY_DIR})
|
||||
set(GCOVR_XML_CMD_COMMAND COMMAND ${GCOVR_XML_CMD})
|
||||
set(GCOVR_XML_CMD_BYPRODUCTS ${Coverage_NAME}_sonarqube.xml)
|
||||
set(GCOVR_XML_CMD_COMMENT
|
||||
COMMENT "SonarQube code coverage info report saved in ${Coverage_NAME}_sonarqube.xml.")
|
||||
endif()
|
||||
|
||||
if(CODE_COVERAGE_VERBOSE OR 1)
|
||||
message(STATUS "Executed command report")
|
||||
message(STATUS "Command to clean up lcov: ")
|
||||
string(REPLACE ";" " " LCOV_CLEAN_CMD_SPACED "${LCOV_CLEAN_CMD}")
|
||||
message(STATUS "${LCOV_CLEAN_CMD_SPACED}")
|
||||
|
||||
message(STATUS "Command to create baseline: ")
|
||||
string(REPLACE ";" " " LCOV_BASELINE_CMD_SPACED "${LCOV_BASELINE_CMD}")
|
||||
message(STATUS "${LCOV_BASELINE_CMD_SPACED}")
|
||||
|
||||
message(STATUS "Command to run the tests: ")
|
||||
string(REPLACE ";" " " LCOV_EXEC_TESTS_CMD_SPACED "${LCOV_EXEC_TESTS_CMD}")
|
||||
message(STATUS "${LCOV_EXEC_TESTS_CMD_SPACED}")
|
||||
|
||||
message(STATUS "Command to capture counters and generate report: ")
|
||||
string(REPLACE ";" " " LCOV_CAPTURE_CMD_SPACED "${LCOV_CAPTURE_CMD}")
|
||||
message(STATUS "${LCOV_CAPTURE_CMD_SPACED}")
|
||||
|
||||
message(STATUS "Command to add baseline counters: ")
|
||||
string(REPLACE ";" " " LCOV_BASELINE_COUNT_CMD_SPACED "${LCOV_BASELINE_COUNT_CMD}")
|
||||
message(STATUS "${LCOV_BASELINE_COUNT_CMD_SPACED}")
|
||||
|
||||
message(STATUS "Command to filter collected data: ")
|
||||
string(REPLACE ";" " " LCOV_FILTER_CMD_SPACED "${LCOV_FILTER_CMD}")
|
||||
message(STATUS "${LCOV_FILTER_CMD_SPACED}")
|
||||
|
||||
message(STATUS "Command to generate lcov HTML output: ")
|
||||
string(REPLACE ";" " " LCOV_GEN_HTML_CMD_SPACED "${LCOV_GEN_HTML_CMD}")
|
||||
message(STATUS "${LCOV_GEN_HTML_CMD_SPACED}")
|
||||
|
||||
if(${Coverage_SONARQUBE})
|
||||
message(STATUS "Command to generate SonarQube XML output: ")
|
||||
string(REPLACE ";" " " GCOVR_XML_CMD_SPACED "${GCOVR_XML_CMD}")
|
||||
message(STATUS "${GCOVR_XML_CMD_SPACED}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Setup target
|
||||
add_custom_target(
|
||||
${Coverage_NAME}
|
||||
COMMAND ${LCOV_CLEAN_CMD}
|
||||
COMMAND ${LCOV_BASELINE_CMD}
|
||||
COMMAND ${LCOV_EXEC_TESTS_CMD}
|
||||
COMMAND ${LCOV_CAPTURE_CMD}
|
||||
COMMAND ${LCOV_BASELINE_COUNT_CMD}
|
||||
COMMAND ${LCOV_FILTER_CMD}
|
||||
COMMAND ${LCOV_GEN_HTML_CMD} ${GCOVR_XML_CMD_COMMAND}
|
||||
# Set output files as GENERATED (will be removed on 'make clean')
|
||||
BYPRODUCTS ${Coverage_NAME}.base ${Coverage_NAME}.capture ${Coverage_NAME}.total
|
||||
${Coverage_NAME}.info ${GCOVR_XML_CMD_BYPRODUCTS} ${Coverage_NAME}/index.html
|
||||
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
|
||||
DEPENDS ${Coverage_DEPENDENCIES}
|
||||
VERBATIM # Protect arguments to commands
|
||||
COMMENT
|
||||
"Resetting code coverage counters to zero.\nProcessing code coverage counters and generating report."
|
||||
)
|
||||
|
||||
# Show where to find the lcov info report
|
||||
add_custom_command(
|
||||
TARGET ${Coverage_NAME}
|
||||
POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E echo
|
||||
"Lcov code coverage info report saved in ${Coverage_NAME}.info."
|
||||
COMMENT ${GCOVR_XML_CMD_COMMENT})
|
||||
|
||||
# Show info where to find the report
|
||||
add_custom_command(
|
||||
TARGET ${Coverage_NAME}
|
||||
POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E echo
|
||||
"Open ./${Coverage_NAME}/index.html in your browser to view the coverage report.")
|
||||
|
||||
endfunction() # setup_target_for_coverage_lcov
|
||||
|
||||
# Defines a target for running and collection code coverage information
|
||||
# Builds dependencies, runs the given executable and outputs reports.
|
||||
# NOTE! The executable should always have a ZERO as exit code otherwise
|
||||
# the coverage generation will not complete.
|
||||
#
|
||||
# setup_target_for_coverage_gcovr_xml(
|
||||
# NAME ctest_coverage # New target name
|
||||
# EXECUTABLE ctest -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR
|
||||
# DEPENDENCIES executable_target # Dependencies to build first
|
||||
# BASE_DIRECTORY "../" # Base directory for report
|
||||
# # (defaults to PROJECT_SOURCE_DIR)
|
||||
# EXCLUDE "src/dir1/*" "src/dir2/*" # Patterns to exclude (can be relative
|
||||
# # to BASE_DIRECTORY, with CMake 3.4+)
|
||||
# )
|
||||
# The user can set the variable GCOVR_ADDITIONAL_ARGS to supply additional flags to the
|
||||
# GCVOR command.
|
||||
function(setup_target_for_coverage_gcovr_xml)
|
||||
|
||||
set(options NONE)
|
||||
set(oneValueArgs BASE_DIRECTORY NAME)
|
||||
set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES)
|
||||
cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
|
||||
if(NOT GCOVR_PATH)
|
||||
message(FATAL_ERROR "gcovr not found! Aborting...")
|
||||
endif() # NOT GCOVR_PATH
|
||||
|
||||
# Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR
|
||||
if(DEFINED Coverage_BASE_DIRECTORY)
|
||||
get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE)
|
||||
else()
|
||||
set(BASEDIR ${PROJECT_SOURCE_DIR})
|
||||
endif()
|
||||
|
||||
# Collect excludes (CMake 3.4+: Also compute absolute paths)
|
||||
set(GCOVR_EXCLUDES "")
|
||||
foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_GCOVR_EXCLUDES})
|
||||
if(CMAKE_VERSION VERSION_GREATER 3.4)
|
||||
get_filename_component(EXCLUDE ${EXCLUDE} ABSOLUTE BASE_DIR ${BASEDIR})
|
||||
endif()
|
||||
list(APPEND GCOVR_EXCLUDES "${EXCLUDE}")
|
||||
endforeach()
|
||||
list(REMOVE_DUPLICATES GCOVR_EXCLUDES)
|
||||
|
||||
# Combine excludes to several -e arguments
|
||||
set(GCOVR_EXCLUDE_ARGS "")
|
||||
foreach(EXCLUDE ${GCOVR_EXCLUDES})
|
||||
list(APPEND GCOVR_EXCLUDE_ARGS "-e")
|
||||
list(APPEND GCOVR_EXCLUDE_ARGS "${EXCLUDE}")
|
||||
endforeach()
|
||||
|
||||
# Set up commands which will be run to generate coverage data
|
||||
# Run tests
|
||||
set(GCOVR_XML_EXEC_TESTS_CMD ${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS})
|
||||
# Running gcovr
|
||||
set(GCOVR_XML_CMD
|
||||
${GCOVR_PATH}
|
||||
--xml
|
||||
${Coverage_NAME}.xml
|
||||
-r
|
||||
${BASEDIR}
|
||||
${GCOVR_ADDITIONAL_ARGS}
|
||||
${GCOVR_EXCLUDE_ARGS}
|
||||
--object-directory=${PROJECT_BINARY_DIR})
|
||||
|
||||
if(CODE_COVERAGE_VERBOSE)
|
||||
message(STATUS "Executed command report")
|
||||
|
||||
message(STATUS "Command to run tests: ")
|
||||
string(REPLACE ";" " " GCOVR_XML_EXEC_TESTS_CMD_SPACED "${GCOVR_XML_EXEC_TESTS_CMD}")
|
||||
message(STATUS "${GCOVR_XML_EXEC_TESTS_CMD_SPACED}")
|
||||
|
||||
message(STATUS "Command to generate gcovr XML coverage data: ")
|
||||
string(REPLACE ";" " " GCOVR_XML_CMD_SPACED "${GCOVR_XML_CMD}")
|
||||
message(STATUS "${GCOVR_XML_CMD_SPACED}")
|
||||
endif()
|
||||
|
||||
add_custom_target(
|
||||
${Coverage_NAME}
|
||||
COMMAND ${GCOVR_XML_EXEC_TESTS_CMD}
|
||||
COMMAND ${GCOVR_XML_CMD}
|
||||
BYPRODUCTS ${Coverage_NAME}.xml
|
||||
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
|
||||
DEPENDS ${Coverage_DEPENDENCIES}
|
||||
VERBATIM # Protect arguments to commands
|
||||
COMMENT "Running gcovr to produce Cobertura code coverage report.")
|
||||
|
||||
# Show info where to find the report
|
||||
add_custom_command(
|
||||
TARGET ${Coverage_NAME}
|
||||
POST_BUILD
|
||||
COMMAND ;
|
||||
COMMENT "Cobertura code coverage report saved in ${Coverage_NAME}.xml.")
|
||||
endfunction() # setup_target_for_coverage_gcovr_xml
|
||||
|
||||
# Defines a target for running and collection code coverage information
|
||||
# Builds dependencies, runs the given executable and outputs reports.
|
||||
# NOTE! The executable should always have a ZERO as exit code otherwise
|
||||
# the coverage generation will not complete.
|
||||
#
|
||||
# setup_target_for_coverage_gcovr_html(
|
||||
# NAME ctest_coverage # New target name
|
||||
# EXECUTABLE ctest -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR
|
||||
# DEPENDENCIES executable_target # Dependencies to build first
|
||||
# BASE_DIRECTORY "../" # Base directory for report
|
||||
# # (defaults to PROJECT_SOURCE_DIR)
|
||||
# EXCLUDE "src/dir1/*" "src/dir2/*" # Patterns to exclude (can be relative
|
||||
# # to BASE_DIRECTORY, with CMake 3.4+)
|
||||
# )
|
||||
# The user can set the variable GCOVR_ADDITIONAL_ARGS to supply additional flags to the
|
||||
# GCVOR command.
|
||||
function(setup_target_for_coverage_gcovr_html)
|
||||
|
||||
set(options NONE)
|
||||
set(oneValueArgs BASE_DIRECTORY NAME)
|
||||
set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES)
|
||||
cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
|
||||
if(NOT GCOVR_PATH)
|
||||
message(FATAL_ERROR "gcovr not found! Aborting...")
|
||||
endif() # NOT GCOVR_PATH
|
||||
|
||||
# Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR
|
||||
if(DEFINED Coverage_BASE_DIRECTORY)
|
||||
get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE)
|
||||
else()
|
||||
set(BASEDIR ${PROJECT_SOURCE_DIR})
|
||||
endif()
|
||||
|
||||
# Collect excludes (CMake 3.4+: Also compute absolute paths)
|
||||
set(GCOVR_EXCLUDES "")
|
||||
foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_GCOVR_EXCLUDES})
|
||||
if(CMAKE_VERSION VERSION_GREATER 3.4)
|
||||
get_filename_component(EXCLUDE ${EXCLUDE} ABSOLUTE BASE_DIR ${BASEDIR})
|
||||
endif()
|
||||
list(APPEND GCOVR_EXCLUDES "${EXCLUDE}")
|
||||
endforeach()
|
||||
list(REMOVE_DUPLICATES GCOVR_EXCLUDES)
|
||||
|
||||
# Combine excludes to several -e arguments
|
||||
set(GCOVR_EXCLUDE_ARGS "")
|
||||
foreach(EXCLUDE ${GCOVR_EXCLUDES})
|
||||
list(APPEND GCOVR_EXCLUDE_ARGS "-e")
|
||||
list(APPEND GCOVR_EXCLUDE_ARGS "${EXCLUDE}")
|
||||
endforeach()
|
||||
|
||||
# Set up commands which will be run to generate coverage data
|
||||
# Run tests
|
||||
set(GCOVR_HTML_EXEC_TESTS_CMD ${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS})
|
||||
# Create folder
|
||||
set(GCOVR_HTML_FOLDER_CMD ${CMAKE_COMMAND} -E make_directory
|
||||
${PROJECT_BINARY_DIR}/${Coverage_NAME})
|
||||
# Running gcovr
|
||||
set(GCOVR_HTML_CMD
|
||||
${GCOVR_PATH}
|
||||
--html
|
||||
${Coverage_NAME}/index.html
|
||||
--html-details
|
||||
-r
|
||||
${BASEDIR}
|
||||
${GCOVR_ADDITIONAL_ARGS}
|
||||
${GCOVR_EXCLUDE_ARGS}
|
||||
--object-directory=${PROJECT_BINARY_DIR})
|
||||
|
||||
if(CODE_COVERAGE_VERBOSE)
|
||||
message(STATUS "Executed command report")
|
||||
|
||||
message(STATUS "Command to run tests: ")
|
||||
string(REPLACE ";" " " GCOVR_HTML_EXEC_TESTS_CMD_SPACED "${GCOVR_HTML_EXEC_TESTS_CMD}")
|
||||
message(STATUS "${GCOVR_HTML_EXEC_TESTS_CMD_SPACED}")
|
||||
|
||||
message(STATUS "Command to create a folder: ")
|
||||
string(REPLACE ";" " " GCOVR_HTML_FOLDER_CMD_SPACED "${GCOVR_HTML_FOLDER_CMD}")
|
||||
message(STATUS "${GCOVR_HTML_FOLDER_CMD_SPACED}")
|
||||
|
||||
message(STATUS "Command to generate gcovr HTML coverage data: ")
|
||||
string(REPLACE ";" " " GCOVR_HTML_CMD_SPACED "${GCOVR_HTML_CMD}")
|
||||
message(STATUS "${GCOVR_HTML_CMD_SPACED}")
|
||||
endif()
|
||||
|
||||
add_custom_target(
|
||||
${Coverage_NAME}
|
||||
COMMAND ${GCOVR_HTML_EXEC_TESTS_CMD}
|
||||
COMMAND ${GCOVR_HTML_FOLDER_CMD}
|
||||
COMMAND ${GCOVR_HTML_CMD}
|
||||
BYPRODUCTS ${PROJECT_BINARY_DIR}/${Coverage_NAME}/index.html # report directory
|
||||
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
|
||||
DEPENDS ${Coverage_DEPENDENCIES}
|
||||
VERBATIM # Protect arguments to commands
|
||||
COMMENT "Running gcovr to produce HTML code coverage report.")
|
||||
|
||||
# Show info where to find the report
|
||||
add_custom_command(
|
||||
TARGET ${Coverage_NAME}
|
||||
POST_BUILD
|
||||
COMMAND ;
|
||||
COMMENT "Open ./${Coverage_NAME}/index.html in your browser to view the coverage report.")
|
||||
|
||||
endfunction() # setup_target_for_coverage_gcovr_html
|
||||
|
||||
# Defines a target for running and collection code coverage information
|
||||
# Builds dependencies, runs the given executable and outputs reports.
|
||||
# NOTE! The executable should always have a ZERO as exit code otherwise
|
||||
# the coverage generation will not complete.
|
||||
#
|
||||
# setup_target_for_coverage_fastcov(
|
||||
# NAME testrunner_coverage # New target name
|
||||
# EXECUTABLE testrunner -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR
|
||||
# DEPENDENCIES testrunner # Dependencies to build first
|
||||
# BASE_DIRECTORY "../" # Base directory for report
|
||||
# # (defaults to PROJECT_SOURCE_DIR)
|
||||
# EXCLUDE "src/dir1/" "src/dir2/" # Patterns to exclude.
|
||||
# NO_DEMANGLE # Don't demangle C++ symbols
|
||||
# # even if c++filt is found
|
||||
# SKIP_HTML # Don't create html report
|
||||
# POST_CMD perl -i -pe s!${PROJECT_SOURCE_DIR}/!!g ctest_coverage.json # E.g. for stripping source dir from file paths
|
||||
# )
|
||||
function(setup_target_for_coverage_fastcov)
|
||||
|
||||
set(options NO_DEMANGLE SKIP_HTML)
|
||||
set(oneValueArgs BASE_DIRECTORY NAME)
|
||||
set(multiValueArgs
|
||||
EXCLUDE
|
||||
EXECUTABLE
|
||||
EXECUTABLE_ARGS
|
||||
DEPENDENCIES
|
||||
FASTCOV_ARGS
|
||||
GENHTML_ARGS
|
||||
POST_CMD)
|
||||
cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||
|
||||
if(NOT FASTCOV_PATH)
|
||||
message(FATAL_ERROR "fastcov not found! Aborting...")
|
||||
endif()
|
||||
|
||||
if(NOT Coverage_SKIP_HTML AND NOT GENHTML_PATH)
|
||||
message(FATAL_ERROR "genhtml not found! Aborting...")
|
||||
endif()
|
||||
|
||||
# Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR
|
||||
if(Coverage_BASE_DIRECTORY)
|
||||
get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE)
|
||||
else()
|
||||
set(BASEDIR ${PROJECT_SOURCE_DIR})
|
||||
endif()
|
||||
|
||||
# Collect excludes (Patterns, not paths, for fastcov)
|
||||
set(FASTCOV_EXCLUDES "")
|
||||
foreach(EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_FASTCOV_EXCLUDES})
|
||||
list(APPEND FASTCOV_EXCLUDES "${EXCLUDE}")
|
||||
endforeach()
|
||||
list(REMOVE_DUPLICATES FASTCOV_EXCLUDES)
|
||||
|
||||
# Conditional arguments
|
||||
if(CPPFILT_PATH AND NOT ${Coverage_NO_DEMANGLE})
|
||||
set(GENHTML_EXTRA_ARGS "--demangle-cpp")
|
||||
endif()
|
||||
|
||||
# Set up commands which will be run to generate coverage data
|
||||
set(FASTCOV_EXEC_TESTS_CMD ${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS})
|
||||
|
||||
set(FASTCOV_CAPTURE_CMD
|
||||
${FASTCOV_PATH}
|
||||
${Coverage_FASTCOV_ARGS}
|
||||
--gcov
|
||||
${GCOV_PATH}
|
||||
--search-directory
|
||||
${BASEDIR}
|
||||
--process-gcno
|
||||
--output
|
||||
${Coverage_NAME}.json
|
||||
--exclude
|
||||
${FASTCOV_EXCLUDES})
|
||||
|
||||
set(FASTCOV_CONVERT_CMD ${FASTCOV_PATH} -C ${Coverage_NAME}.json --lcov --output
|
||||
${Coverage_NAME}.info)
|
||||
|
||||
if(Coverage_SKIP_HTML)
|
||||
set(FASTCOV_HTML_CMD ";")
|
||||
else()
|
||||
set(FASTCOV_HTML_CMD ${GENHTML_PATH} ${GENHTML_EXTRA_ARGS} ${Coverage_GENHTML_ARGS} -o
|
||||
${Coverage_NAME} ${Coverage_NAME}.info)
|
||||
endif()
|
||||
|
||||
set(FASTCOV_POST_CMD ";")
|
||||
if(Coverage_POST_CMD)
|
||||
set(FASTCOV_POST_CMD ${Coverage_POST_CMD})
|
||||
endif()
|
||||
|
||||
if(CODE_COVERAGE_VERBOSE)
|
||||
message(STATUS "Code coverage commands for target ${Coverage_NAME} (fastcov):")
|
||||
|
||||
message(" Running tests:")
|
||||
string(REPLACE ";" " " FASTCOV_EXEC_TESTS_CMD_SPACED "${FASTCOV_EXEC_TESTS_CMD}")
|
||||
message(" ${FASTCOV_EXEC_TESTS_CMD_SPACED}")
|
||||
|
||||
message(" Capturing fastcov counters and generating report:")
|
||||
string(REPLACE ";" " " FASTCOV_CAPTURE_CMD_SPACED "${FASTCOV_CAPTURE_CMD}")
|
||||
message(" ${FASTCOV_CAPTURE_CMD_SPACED}")
|
||||
|
||||
message(" Converting fastcov .json to lcov .info:")
|
||||
string(REPLACE ";" " " FASTCOV_CONVERT_CMD_SPACED "${FASTCOV_CONVERT_CMD}")
|
||||
message(" ${FASTCOV_CONVERT_CMD_SPACED}")
|
||||
|
||||
if(NOT Coverage_SKIP_HTML)
|
||||
message(" Generating HTML report: ")
|
||||
string(REPLACE ";" " " FASTCOV_HTML_CMD_SPACED "${FASTCOV_HTML_CMD}")
|
||||
message(" ${FASTCOV_HTML_CMD_SPACED}")
|
||||
endif()
|
||||
if(Coverage_POST_CMD)
|
||||
message(" Running post command: ")
|
||||
string(REPLACE ";" " " FASTCOV_POST_CMD_SPACED "${FASTCOV_POST_CMD}")
|
||||
message(" ${FASTCOV_POST_CMD_SPACED}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Setup target
|
||||
add_custom_target(
|
||||
${Coverage_NAME}
|
||||
# Cleanup fastcov
|
||||
COMMAND ${FASTCOV_PATH} ${Coverage_FASTCOV_ARGS} --gcov ${GCOV_PATH} --search-directory
|
||||
${BASEDIR} --zerocounters
|
||||
COMMAND ${FASTCOV_EXEC_TESTS_CMD}
|
||||
COMMAND ${FASTCOV_CAPTURE_CMD}
|
||||
COMMAND ${FASTCOV_CONVERT_CMD}
|
||||
COMMAND ${FASTCOV_HTML_CMD}
|
||||
COMMAND ${FASTCOV_POST_CMD}
|
||||
# Set output files as GENERATED (will be removed on 'make clean')
|
||||
BYPRODUCTS ${Coverage_NAME}.info ${Coverage_NAME}.json
|
||||
${Coverage_NAME}/index.html # report directory
|
||||
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
|
||||
DEPENDS ${Coverage_DEPENDENCIES}
|
||||
VERBATIM # Protect arguments to commands
|
||||
COMMENT
|
||||
"Resetting code coverage counters to zero. Processing code coverage counters and generating report."
|
||||
)
|
||||
|
||||
set(INFO_MSG
|
||||
"fastcov code coverage info report saved in ${Coverage_NAME}.info and ${Coverage_NAME}.json."
|
||||
)
|
||||
if(NOT Coverage_SKIP_HTML)
|
||||
string(
|
||||
APPEND
|
||||
INFO_MSG
|
||||
" Open ${PROJECT_BINARY_DIR}/${Coverage_NAME}/index.html in your browser to view the coverage report."
|
||||
)
|
||||
endif()
|
||||
# Show where to find the fastcov info report
|
||||
add_custom_command(
|
||||
TARGET ${Coverage_NAME}
|
||||
POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E echo ${INFO_MSG})
|
||||
|
||||
endfunction() # setup_target_for_coverage_fastcov
|
||||
|
||||
function(append_coverage_compiler_flags)
|
||||
set(CMAKE_C_FLAGS
|
||||
"${CMAKE_C_FLAGS} ${COVERAGE_COMPILER_FLAGS}"
|
||||
PARENT_SCOPE)
|
||||
set(CMAKE_CXX_FLAGS
|
||||
"${CMAKE_CXX_FLAGS} ${COVERAGE_COMPILER_FLAGS}"
|
||||
PARENT_SCOPE)
|
||||
set(CMAKE_Fortran_FLAGS
|
||||
"${CMAKE_Fortran_FLAGS} ${COVERAGE_COMPILER_FLAGS}"
|
||||
PARENT_SCOPE)
|
||||
message(STATUS "Appending code coverage compiler flags: ${COVERAGE_COMPILER_FLAGS}")
|
||||
endfunction() # append_coverage_compiler_flags
|
||||
|
||||
# Setup coverage for specific library
|
||||
function(append_coverage_compiler_flags_to_target name)
|
||||
separate_arguments(_flag_list NATIVE_COMMAND "${COVERAGE_COMPILER_FLAGS}")
|
||||
target_compile_options(${name} PRIVATE ${_flag_list})
|
||||
if(CMAKE_C_COMPILER_ID STREQUAL "GNU"
|
||||
OR CMAKE_CXX_COMPILER_ID STREQUAL "GNU"
|
||||
OR CMAKE_Fortran_COMPILER_ID STREQUAL "GNU")
|
||||
target_link_libraries(${name} PRIVATE gcov)
|
||||
endif()
|
||||
endfunction()
|
||||
@@ -0,0 +1,2 @@
|
||||
/html/*
|
||||
/latex/*
|
||||
@@ -0,0 +1,114 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="34.342606mm"
|
||||
height="15.300875mm"
|
||||
viewBox="0 0 34.342606 15.300875"
|
||||
version="1.1"
|
||||
id="svg8"
|
||||
inkscape:version="0.92.2 (unknown)"
|
||||
sodipodi:docname="CLI11.svg"
|
||||
inkscape:export-filename="/data/CLI11_300.png"
|
||||
inkscape:export-xdpi="222.62143"
|
||||
inkscape:export-ydpi="222.62143">
|
||||
<defs
|
||||
id="defs2" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="11.206433"
|
||||
inkscape:cx="93.996945"
|
||||
inkscape:cy="15.843961"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1347"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="1"
|
||||
inkscape:window-maximized="1"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0" />
|
||||
<metadata
|
||||
id="metadata5">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-53.018986,-23.9019)">
|
||||
<g
|
||||
id="g4602"
|
||||
transform="rotate(-0.28559572,70.190289,31.552338)">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3732"
|
||||
transform="scale(0.26458333)"
|
||||
d="m 233.33789,90.337891 -32.95117,28.619139 31.94726,28.91406 64.9004,0.29688 32.95117,-28.61914 -31.94727,-28.914064 z"
|
||||
style="fill:#008080;stroke-width:0.54128456" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:none;stroke:#ffffff;stroke-width:0.3148967;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
d="m 62.120274,24.413292 -8.32335,7.065988 8.069765,7.138804 16.393615,0.0733 8.32335,-7.065988 -8.069768,-7.138806 z"
|
||||
id="path3774" />
|
||||
</g>
|
||||
<g
|
||||
id="g4609"
|
||||
transform="translate(-0.43472687)">
|
||||
<path
|
||||
inkscape:transform-center-y="0.00020894337"
|
||||
inkscape:transform-center-x="0.0229185"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3730"
|
||||
d="m 60.964519,27.804182 c 0.886395,-0.348655 1.859691,-0.390207 2.74248,-0.111952 0.651274,0.210103 1.042699,0.454066 1.576252,0.972044 l -1.506657,0.592635 c -0.744252,-0.446473 -1.423964,-0.497745 -2.270962,-0.164583 -0.738662,0.290549 -1.26082,0.814436 -1.498695,1.510755 -0.203801,0.580557 -0.182185,1.300104 0.05025,1.891033 0.534609,1.359137 2.079298,2.048044 3.418738,1.521183 0.699266,-0.275052 1.11846,-0.713017 1.465328,-1.565931 l 1.585527,-0.623658 c -0.04824,1.554281 -1.023053,2.892949 -2.510224,3.47792 -2.127345,0.836779 -4.497206,-0.187252 -5.322363,-2.28505 -0.809661,-2.058401 0.211919,-4.404734 2.270322,-5.214396 z"
|
||||
style="fill:#ffffff;stroke-width:0.14321487" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3728"
|
||||
transform="scale(0.26458333)"
|
||||
d="m 253.49609,104.47266 h 5.48047 v 24.32031 h 9.03906 v 5.24023 h -14.51953 z"
|
||||
style="fill:#ffffff;stroke-width:0.54128456" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3726"
|
||||
transform="scale(0.26458333)"
|
||||
d="m 271.07422,104.47266 h 5.48047 v 29.56054 h -5.48047 z"
|
||||
style="fill:#ffffff;stroke-width:0.54128456" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3724"
|
||||
transform="scale(0.26458333)"
|
||||
d="m 294.68555,104.47266 v 29.56054 h -5.32032 v -24.56054 h -3.71875 z"
|
||||
style="fill:#ffffff;stroke-width:0.54128456" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3705"
|
||||
transform="scale(0.26458333)"
|
||||
d="m 305.76758,104.47266 v 29.56054 h -5.32031 v -24.56054 h -3.71875 z"
|
||||
style="fill:#ffffff;stroke-width:0.54128456" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.5 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 3.3 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 9.7 KiB |
@@ -0,0 +1,11 @@
|
||||
# Run the real Doxyfile (same config as the published docs: impl/ headers,
|
||||
# book chapters, doxygen-awesome styling). All paths in the Doxyfile are
|
||||
# relative to the repo root, and OUTPUT_DIRECTORY is empty, so the output
|
||||
# lands in html/ at the repo root (gitignored).
|
||||
add_custom_target(
|
||||
docs
|
||||
COMMAND ${CMAKE_COMMAND} -E env CLI11_VERSION=${CLI11_VERSION} $<TARGET_FILE:Doxygen::doxygen>
|
||||
"${PROJECT_SOURCE_DIR}/docs/Doxyfile"
|
||||
WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}"
|
||||
COMMENT "Generating API documentation with Doxygen (output: ${PROJECT_SOURCE_DIR}/html)"
|
||||
VERBATIM)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,267 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<doxygenlayout version="2.0">
|
||||
<!-- Generated by doxygen 1.17.0 -->
|
||||
<!-- Navigation index tabs for HTML output -->
|
||||
<navindex>
|
||||
<tab type="mainpage" visible="yes" title="Introduction"/>
|
||||
<tab type="pages" visible="no" title="" intro=""/>
|
||||
<tab type="usergroup" title="Guide" url="[none]">
|
||||
<tab type="user" url="book-installation.html" title="Installation"/>
|
||||
<tab type="user" url="book-basics.html" title="The Basics"/>
|
||||
<tab type="user" url="book-flags.html" title="Adding Flags"/>
|
||||
<tab type="user" url="book-options.html" title="Options"/>
|
||||
<tab type="user" url="book-validators.html" title="Validators"/>
|
||||
<tab type="user" url="book-subcommands.html" title="Subcommands and the App"/>
|
||||
<tab type="user" url="book-option-groups.html" title="Option groups"/>
|
||||
<tab type="user" url="book-config.html" title="Accepting configure files"/>
|
||||
<tab type="user" url="book-formatting.html" title="Formatting help output"/>
|
||||
<tab type="user" url="book-unicode.html" title="Unicode support"/>
|
||||
<tab type="user" url="book-toolkits.html" title="Using CLI11 in a Toolkit"/>
|
||||
<tab type="user" url="book-advanced-topics.html" title="Advanced topics"/>
|
||||
<tab type="user" url="book-internals.html" title="CLI11 Internals"/>
|
||||
</tab>
|
||||
<tab type="usergroup" title="Examples" url="[none]">
|
||||
<tab type="user" url="book-an-advanced-example.html" title="Making a git clone"/>
|
||||
<tab type="user" url="book-modules-example.html" title="Using CLI11 as a C++20 module"/>
|
||||
<tab type="user" url="example-programs.html" title="Example programs"/>
|
||||
</tab>
|
||||
<tab type="usergroup" title="API Reference" url="[none]">
|
||||
<tab type="topics" visible="yes" title="Errors & Validators" intro=""/>
|
||||
<tab type="namespaces" visible="yes" title="">
|
||||
<tab type="namespacelist" visible="yes" title="" intro=""/>
|
||||
<tab type="namespacemembers" visible="yes" title="" intro=""/>
|
||||
</tab>
|
||||
<tab type="classes" visible="yes" title="">
|
||||
<tab type="classlist" visible="yes" title="" intro=""/>
|
||||
<tab type="classindex" visible="$ALPHABETICAL_INDEX" title=""/>
|
||||
<tab type="hierarchy" visible="yes" title="" intro=""/>
|
||||
<tab type="classmembers" visible="yes" title="" intro=""/>
|
||||
</tab>
|
||||
</tab>
|
||||
<tab type="user" url="changelog.html" title="Changelog"/>
|
||||
</navindex>
|
||||
|
||||
<!-- Layout definition for a class page -->
|
||||
<class>
|
||||
<briefdescription visible="yes"/>
|
||||
<includes visible="$SHOW_HEADERFILE"/>
|
||||
<inheritancegraph visible="yes"/>
|
||||
<collaborationgraph visible="yes"/>
|
||||
<memberdecl>
|
||||
<nestedclasses visible="yes" title=""/>
|
||||
<publictypes visible="yes" title=""/>
|
||||
<services visible="yes" title=""/>
|
||||
<interfaces visible="yes" title=""/>
|
||||
<publicslots visible="yes" title=""/>
|
||||
<signals visible="yes" title=""/>
|
||||
<publicmethods visible="yes" title=""/>
|
||||
<publicstaticmethods visible="yes" title=""/>
|
||||
<publicattributes visible="yes" title=""/>
|
||||
<publicstaticattributes visible="yes" title=""/>
|
||||
<protectedtypes visible="yes" title=""/>
|
||||
<protectedslots visible="yes" title=""/>
|
||||
<protectedmethods visible="yes" title=""/>
|
||||
<protectedstaticmethods visible="yes" title=""/>
|
||||
<protectedattributes visible="yes" title=""/>
|
||||
<protectedstaticattributes visible="yes" title=""/>
|
||||
<packagetypes visible="yes" title=""/>
|
||||
<packagemethods visible="yes" title=""/>
|
||||
<packagestaticmethods visible="yes" title=""/>
|
||||
<packageattributes visible="yes" title=""/>
|
||||
<packagestaticattributes visible="yes" title=""/>
|
||||
<properties visible="yes" title=""/>
|
||||
<events visible="yes" title=""/>
|
||||
<privatetypes visible="yes" title=""/>
|
||||
<privateslots visible="yes" title=""/>
|
||||
<privatemethods visible="yes" title=""/>
|
||||
<privatestaticmethods visible="yes" title=""/>
|
||||
<privateattributes visible="yes" title=""/>
|
||||
<privatestaticattributes visible="yes" title=""/>
|
||||
<friends visible="yes" title=""/>
|
||||
<related visible="yes" title="" subtitle=""/>
|
||||
<membergroups visible="yes"/>
|
||||
</memberdecl>
|
||||
<detaileddescription visible="yes" title=""/>
|
||||
<memberdef>
|
||||
<inlineclasses visible="yes" title=""/>
|
||||
<typedefs visible="yes" title=""/>
|
||||
<enums visible="yes" title=""/>
|
||||
<services visible="yes" title=""/>
|
||||
<interfaces visible="yes" title=""/>
|
||||
<constructors visible="yes" title=""/>
|
||||
<functions visible="yes" title=""/>
|
||||
<related visible="yes" title=""/>
|
||||
<variables visible="yes" title=""/>
|
||||
<properties visible="yes" title=""/>
|
||||
<events visible="yes" title=""/>
|
||||
</memberdef>
|
||||
<allmemberslink visible="yes"/>
|
||||
<usedfiles visible="$SHOW_USED_FILES"/>
|
||||
<authorsection visible="yes"/>
|
||||
</class>
|
||||
|
||||
<!-- Layout definition for a namespace page -->
|
||||
<namespace>
|
||||
<briefdescription visible="yes"/>
|
||||
<memberdecl>
|
||||
<nestednamespaces visible="yes" title=""/>
|
||||
<constantgroups visible="yes" title=""/>
|
||||
<interfaces visible="yes" title=""/>
|
||||
<classes visible="yes" title=""/>
|
||||
<concepts visible="yes" title=""/>
|
||||
<structs visible="yes" title=""/>
|
||||
<exceptions visible="yes" title=""/>
|
||||
<typedefs visible="yes" title=""/>
|
||||
<sequences visible="yes" title=""/>
|
||||
<dictionaries visible="yes" title=""/>
|
||||
<enums visible="yes" title=""/>
|
||||
<functions visible="yes" title=""/>
|
||||
<variables visible="yes" title=""/>
|
||||
<properties visible="yes" title=""/>
|
||||
<membergroups visible="yes"/>
|
||||
</memberdecl>
|
||||
<detaileddescription visible="yes" title=""/>
|
||||
<memberdef>
|
||||
<inlineclasses visible="yes" title=""/>
|
||||
<typedefs visible="yes" title=""/>
|
||||
<sequences visible="yes" title=""/>
|
||||
<dictionaries visible="yes" title=""/>
|
||||
<enums visible="yes" title=""/>
|
||||
<functions visible="yes" title=""/>
|
||||
<variables visible="yes" title=""/>
|
||||
<properties visible="yes" title=""/>
|
||||
</memberdef>
|
||||
<authorsection visible="yes"/>
|
||||
</namespace>
|
||||
|
||||
<!-- Layout definition for a concept page -->
|
||||
<concept>
|
||||
<briefdescription visible="yes"/>
|
||||
<includes visible="$SHOW_HEADERFILE"/>
|
||||
<definition visible="yes" title=""/>
|
||||
<detaileddescription visible="yes" title=""/>
|
||||
<authorsection visible="yes"/>
|
||||
</concept>
|
||||
|
||||
<!-- Layout definition for a file page -->
|
||||
<file>
|
||||
<briefdescription visible="yes"/>
|
||||
<includes visible="$SHOW_INCLUDE_FILES"/>
|
||||
<includegraph visible="yes"/>
|
||||
<includedbygraph visible="yes"/>
|
||||
<sourcelink visible="yes"/>
|
||||
<memberdecl>
|
||||
<interfaces visible="yes" title=""/>
|
||||
<classes visible="yes" title=""/>
|
||||
<structs visible="yes" title=""/>
|
||||
<exceptions visible="yes" title=""/>
|
||||
<namespaces visible="yes" title=""/>
|
||||
<concepts visible="yes" title=""/>
|
||||
<constantgroups visible="yes" title=""/>
|
||||
<defines visible="yes" title=""/>
|
||||
<typedefs visible="yes" title=""/>
|
||||
<sequences visible="yes" title=""/>
|
||||
<dictionaries visible="yes" title=""/>
|
||||
<enums visible="yes" title=""/>
|
||||
<functions visible="yes" title=""/>
|
||||
<variables visible="yes" title=""/>
|
||||
<properties visible="yes" title=""/>
|
||||
<membergroups visible="yes"/>
|
||||
</memberdecl>
|
||||
<detaileddescription visible="yes" title=""/>
|
||||
<memberdef>
|
||||
<inlineclasses visible="yes" title=""/>
|
||||
<defines visible="yes" title=""/>
|
||||
<typedefs visible="yes" title=""/>
|
||||
<sequences visible="yes" title=""/>
|
||||
<dictionaries visible="yes" title=""/>
|
||||
<enums visible="yes" title=""/>
|
||||
<functions visible="yes" title=""/>
|
||||
<variables visible="yes" title=""/>
|
||||
<properties visible="yes" title=""/>
|
||||
</memberdef>
|
||||
<authorsection/>
|
||||
</file>
|
||||
|
||||
<!-- Layout definition for a group page -->
|
||||
<group>
|
||||
<briefdescription visible="yes"/>
|
||||
<groupgraph visible="yes"/>
|
||||
<memberdecl>
|
||||
<nestedgroups visible="yes" title=""/>
|
||||
<modules visible="yes" title=""/>
|
||||
<dirs visible="yes" title=""/>
|
||||
<files visible="yes" title=""/>
|
||||
<namespaces visible="yes" title=""/>
|
||||
<concepts visible="yes" title=""/>
|
||||
<classes visible="yes" title=""/>
|
||||
<defines visible="yes" title=""/>
|
||||
<typedefs visible="yes" title=""/>
|
||||
<sequences visible="yes" title=""/>
|
||||
<dictionaries visible="yes" title=""/>
|
||||
<enums visible="yes" title=""/>
|
||||
<enumvalues visible="yes" title=""/>
|
||||
<functions visible="yes" title=""/>
|
||||
<variables visible="yes" title=""/>
|
||||
<signals visible="yes" title=""/>
|
||||
<publicslots visible="yes" title=""/>
|
||||
<protectedslots visible="yes" title=""/>
|
||||
<privateslots visible="yes" title=""/>
|
||||
<events visible="yes" title=""/>
|
||||
<properties visible="yes" title=""/>
|
||||
<friends visible="yes" title=""/>
|
||||
<membergroups visible="yes"/>
|
||||
</memberdecl>
|
||||
<detaileddescription visible="yes" title=""/>
|
||||
<memberdef>
|
||||
<pagedocs/>
|
||||
<inlineclasses visible="yes" title=""/>
|
||||
<defines visible="yes" title=""/>
|
||||
<typedefs visible="yes" title=""/>
|
||||
<sequences visible="yes" title=""/>
|
||||
<dictionaries visible="yes" title=""/>
|
||||
<enums visible="yes" title=""/>
|
||||
<enumvalues visible="yes" title=""/>
|
||||
<functions visible="yes" title=""/>
|
||||
<variables visible="yes" title=""/>
|
||||
<signals visible="yes" title=""/>
|
||||
<publicslots visible="yes" title=""/>
|
||||
<protectedslots visible="yes" title=""/>
|
||||
<privateslots visible="yes" title=""/>
|
||||
<events visible="yes" title=""/>
|
||||
<properties visible="yes" title=""/>
|
||||
<friends visible="yes" title=""/>
|
||||
</memberdef>
|
||||
<authorsection visible="yes"/>
|
||||
</group>
|
||||
|
||||
<!-- Layout definition for a C++20 module page -->
|
||||
<module>
|
||||
<briefdescription visible="yes"/>
|
||||
<exportedmodules visible="yes"/>
|
||||
<memberdecl>
|
||||
<concepts visible="yes" title=""/>
|
||||
<classes visible="yes" title=""/>
|
||||
<enums visible="yes" title=""/>
|
||||
<typedefs visible="yes" title=""/>
|
||||
<functions visible="yes" title=""/>
|
||||
<variables visible="yes" title=""/>
|
||||
<membergroups visible="yes" title=""/>
|
||||
</memberdecl>
|
||||
<detaileddescription visible="yes" title=""/>
|
||||
<memberdecl>
|
||||
<files visible="yes"/>
|
||||
</memberdecl>
|
||||
</module>
|
||||
|
||||
<!-- Layout definition for a directory page -->
|
||||
<directory>
|
||||
<briefdescription visible="yes"/>
|
||||
<directorygraph visible="yes"/>
|
||||
<memberdecl>
|
||||
<dirs visible="yes"/>
|
||||
<files visible="yes"/>
|
||||
</memberdecl>
|
||||
<detaileddescription visible="yes" title=""/>
|
||||
</directory>
|
||||
</doxygenlayout>
|
||||
@@ -0,0 +1,138 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
/**
|
||||
|
||||
Doxygen Awesome
|
||||
https://github.com/jothepro/doxygen-awesome-css
|
||||
|
||||
Copyright (c) 2021 - 2025 jothepro
|
||||
|
||||
*/
|
||||
|
||||
class DoxygenAwesomeDarkModeToggle extends HTMLElement {
|
||||
// SVG icons from https://fonts.google.com/icons
|
||||
// Licensed under the Apache 2.0 license:
|
||||
// https://www.apache.org/licenses/LICENSE-2.0.html
|
||||
static lightModeIcon = `<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#FCBF00"><rect fill="none" height="24" width="24"/><circle cx="12" cy="12" opacity=".3" r="3"/><path d="M12,9c1.65,0,3,1.35,3,3s-1.35,3-3,3s-3-1.35-3-3S10.35,9,12,9 M12,7c-2.76,0-5,2.24-5,5s2.24,5,5,5s5-2.24,5-5 S14.76,7,12,7L12,7z M2,13l2,0c0.55,0,1-0.45,1-1s-0.45-1-1-1l-2,0c-0.55,0-1,0.45-1,1S1.45,13,2,13z M20,13l2,0c0.55,0,1-0.45,1-1 s-0.45-1-1-1l-2,0c-0.55,0-1,0.45-1,1S19.45,13,20,13z M11,2v2c0,0.55,0.45,1,1,1s1-0.45,1-1V2c0-0.55-0.45-1-1-1S11,1.45,11,2z M11,20v2c0,0.55,0.45,1,1,1s1-0.45,1-1v-2c0-0.55-0.45-1-1-1C11.45,19,11,19.45,11,20z M5.99,4.58c-0.39-0.39-1.03-0.39-1.41,0 c-0.39,0.39-0.39,1.03,0,1.41l1.06,1.06c0.39,0.39,1.03,0.39,1.41,0s0.39-1.03,0-1.41L5.99,4.58z M18.36,16.95 c-0.39-0.39-1.03-0.39-1.41,0c-0.39,0.39-0.39,1.03,0,1.41l1.06,1.06c0.39,0.39,1.03,0.39,1.41,0c0.39-0.39,0.39-1.03,0-1.41 L18.36,16.95z M19.42,5.99c0.39-0.39,0.39-1.03,0-1.41c-0.39-0.39-1.03-0.39-1.41,0l-1.06,1.06c-0.39,0.39-0.39,1.03,0,1.41 s1.03,0.39,1.41,0L19.42,5.99z M7.05,18.36c0.39-0.39,0.39-1.03,0-1.41c-0.39-0.39-1.03-0.39-1.41,0l-1.06,1.06 c-0.39,0.39-0.39,1.03,0,1.41s1.03,0.39,1.41,0L7.05,18.36z"/></svg>`
|
||||
static darkModeIcon = `<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="24px" viewBox="0 0 24 24" width="24px" fill="#FE9700"><rect fill="none" height="24" width="24"/><path d="M9.37,5.51C9.19,6.15,9.1,6.82,9.1,7.5c0,4.08,3.32,7.4,7.4,7.4c0.68,0,1.35-0.09,1.99-0.27 C17.45,17.19,14.93,19,12,19c-3.86,0-7-3.14-7-7C5,9.07,6.81,6.55,9.37,5.51z" opacity=".3"/><path d="M9.37,5.51C9.19,6.15,9.1,6.82,9.1,7.5c0,4.08,3.32,7.4,7.4,7.4c0.68,0,1.35-0.09,1.99-0.27C17.45,17.19,14.93,19,12,19 c-3.86,0-7-3.14-7-7C5,9.07,6.81,6.55,9.37,5.51z M12,3c-4.97,0-9,4.03-9,9s4.03,9,9,9s9-4.03,9-9c0-0.46-0.04-0.92-0.1-1.36 c-0.98,1.37-2.58,2.26-4.4,2.26c-2.98,0-5.4-2.42-5.4-5.4c0-1.81,0.89-3.42,2.26-4.4C12.92,3.04,12.46,3,12,3L12,3z"/></svg>`
|
||||
static title = "Toggle Light/Dark Mode"
|
||||
|
||||
static prefersLightModeInDarkModeKey = "prefers-light-mode-in-dark-mode"
|
||||
static prefersDarkModeInLightModeKey = "prefers-dark-mode-in-light-mode"
|
||||
|
||||
static _staticConstructor = function() {
|
||||
DoxygenAwesomeDarkModeToggle.enableDarkMode(DoxygenAwesomeDarkModeToggle.userPreference)
|
||||
// Update the color scheme when the browsers preference changes
|
||||
// without user interaction on the website.
|
||||
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', event => {
|
||||
DoxygenAwesomeDarkModeToggle.onSystemPreferenceChanged()
|
||||
})
|
||||
// Update the color scheme when the tab is made visible again.
|
||||
// It is possible that the appearance was changed in another tab
|
||||
// while this tab was in the background.
|
||||
document.addEventListener("visibilitychange", visibilityState => {
|
||||
if (document.visibilityState === 'visible') {
|
||||
DoxygenAwesomeDarkModeToggle.onSystemPreferenceChanged()
|
||||
}
|
||||
});
|
||||
}()
|
||||
|
||||
static init() {
|
||||
$(function() {
|
||||
$(document).ready(function() {
|
||||
const toggleButton = document.createElement('doxygen-awesome-dark-mode-toggle')
|
||||
toggleButton.title = DoxygenAwesomeDarkModeToggle.title
|
||||
toggleButton.updateIcon()
|
||||
|
||||
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', event => {
|
||||
toggleButton.updateIcon()
|
||||
})
|
||||
document.addEventListener("visibilitychange", visibilityState => {
|
||||
if (document.visibilityState === 'visible') {
|
||||
toggleButton.updateIcon()
|
||||
}
|
||||
});
|
||||
|
||||
$(document).ready(function(){
|
||||
document.getElementById("MSearchBox").parentNode.appendChild(toggleButton)
|
||||
})
|
||||
$(window).resize(function(){
|
||||
document.getElementById("MSearchBox").parentNode.appendChild(toggleButton)
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.onclick=this.toggleDarkMode
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns `true` for dark-mode, `false` for light-mode system preference
|
||||
*/
|
||||
static get systemPreference() {
|
||||
return window.matchMedia('(prefers-color-scheme: dark)').matches
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns `true` for dark-mode, `false` for light-mode user preference
|
||||
*/
|
||||
static get userPreference() {
|
||||
return (!DoxygenAwesomeDarkModeToggle.systemPreference && localStorage.getItem(DoxygenAwesomeDarkModeToggle.prefersDarkModeInLightModeKey)) ||
|
||||
(DoxygenAwesomeDarkModeToggle.systemPreference && !localStorage.getItem(DoxygenAwesomeDarkModeToggle.prefersLightModeInDarkModeKey))
|
||||
}
|
||||
|
||||
static set userPreference(userPreference) {
|
||||
DoxygenAwesomeDarkModeToggle.darkModeEnabled = userPreference
|
||||
if(!userPreference) {
|
||||
if(DoxygenAwesomeDarkModeToggle.systemPreference) {
|
||||
localStorage.setItem(DoxygenAwesomeDarkModeToggle.prefersLightModeInDarkModeKey, true)
|
||||
} else {
|
||||
localStorage.removeItem(DoxygenAwesomeDarkModeToggle.prefersDarkModeInLightModeKey)
|
||||
}
|
||||
} else {
|
||||
if(!DoxygenAwesomeDarkModeToggle.systemPreference) {
|
||||
localStorage.setItem(DoxygenAwesomeDarkModeToggle.prefersDarkModeInLightModeKey, true)
|
||||
} else {
|
||||
localStorage.removeItem(DoxygenAwesomeDarkModeToggle.prefersLightModeInDarkModeKey)
|
||||
}
|
||||
}
|
||||
DoxygenAwesomeDarkModeToggle.onUserPreferenceChanged()
|
||||
}
|
||||
|
||||
static enableDarkMode(enable) {
|
||||
if(enable) {
|
||||
DoxygenAwesomeDarkModeToggle.darkModeEnabled = true
|
||||
document.documentElement.classList.add("dark-mode")
|
||||
document.documentElement.classList.remove("light-mode")
|
||||
} else {
|
||||
DoxygenAwesomeDarkModeToggle.darkModeEnabled = false
|
||||
document.documentElement.classList.remove("dark-mode")
|
||||
document.documentElement.classList.add("light-mode")
|
||||
}
|
||||
}
|
||||
|
||||
static onSystemPreferenceChanged() {
|
||||
DoxygenAwesomeDarkModeToggle.darkModeEnabled = DoxygenAwesomeDarkModeToggle.userPreference
|
||||
DoxygenAwesomeDarkModeToggle.enableDarkMode(DoxygenAwesomeDarkModeToggle.darkModeEnabled)
|
||||
}
|
||||
|
||||
static onUserPreferenceChanged() {
|
||||
DoxygenAwesomeDarkModeToggle.enableDarkMode(DoxygenAwesomeDarkModeToggle.darkModeEnabled)
|
||||
}
|
||||
|
||||
toggleDarkMode() {
|
||||
DoxygenAwesomeDarkModeToggle.userPreference = !DoxygenAwesomeDarkModeToggle.userPreference
|
||||
this.updateIcon()
|
||||
}
|
||||
|
||||
updateIcon() {
|
||||
if(DoxygenAwesomeDarkModeToggle.darkModeEnabled) {
|
||||
this.innerHTML = DoxygenAwesomeDarkModeToggle.darkModeIcon
|
||||
} else {
|
||||
this.innerHTML = DoxygenAwesomeDarkModeToggle.lightModeIcon
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("doxygen-awesome-dark-mode-toggle", DoxygenAwesomeDarkModeToggle);
|
||||
@@ -0,0 +1,107 @@
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
/**
|
||||
|
||||
Doxygen Awesome
|
||||
https://github.com/jothepro/doxygen-awesome-css
|
||||
|
||||
Copyright (c) 2021 - 2025 jothepro
|
||||
|
||||
*/
|
||||
|
||||
html {
|
||||
/* side nav width. MUST be = `TREEVIEW_WIDTH`.
|
||||
* Make sure it is wide enough to contain the page title (logo + title + version)
|
||||
*/
|
||||
--side-nav-fixed-width: 335px;
|
||||
--menu-display: none;
|
||||
|
||||
--top-height: 120px;
|
||||
--toc-sticky-top: -25px;
|
||||
--toc-max-height: calc(100vh - 2 * var(--spacing-medium) - 25px);
|
||||
}
|
||||
|
||||
#projectname {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
html {
|
||||
--searchbar-background: var(--page-background-color);
|
||||
}
|
||||
|
||||
#side-nav {
|
||||
min-width: var(--side-nav-fixed-width);
|
||||
max-width: var(--side-nav-fixed-width);
|
||||
top: var(--top-height);
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
#nav-tree,
|
||||
#side-nav {
|
||||
height: calc(100vh - var(--top-height)) !important;
|
||||
}
|
||||
|
||||
#top {
|
||||
display: block;
|
||||
border-bottom: none;
|
||||
height: var(--top-height);
|
||||
margin-bottom: calc(0px - var(--top-height));
|
||||
max-width: var(--side-nav-fixed-width);
|
||||
overflow: hidden;
|
||||
background: var(--side-nav-background);
|
||||
}
|
||||
|
||||
#main-nav {
|
||||
float: left;
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
.ui-resizable-handle {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.ui-resizable-e {
|
||||
width: 0;
|
||||
}
|
||||
|
||||
#nav-path {
|
||||
position: fixed;
|
||||
right: 0;
|
||||
left: calc(var(--side-nav-fixed-width) + 1px);
|
||||
bottom: 0;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
#doc-content {
|
||||
height: calc(100vh - 31px) !important;
|
||||
padding-bottom: calc(3 * var(--spacing-large));
|
||||
padding-top: calc(var(--top-height) - 80px);
|
||||
box-sizing: border-box;
|
||||
margin-left: var(--side-nav-fixed-width) !important;
|
||||
}
|
||||
|
||||
#MSearchBox {
|
||||
width: calc(var(--side-nav-fixed-width) - calc(2 * var(--spacing-medium)));
|
||||
}
|
||||
|
||||
#MSearchField {
|
||||
width: calc(
|
||||
var(--side-nav-fixed-width) - calc(2 * var(--spacing-medium)) - 65px
|
||||
);
|
||||
}
|
||||
|
||||
#MSearchResultsWindow {
|
||||
left: var(--spacing-medium) !important;
|
||||
right: auto;
|
||||
}
|
||||
|
||||
#nav-sync {
|
||||
bottom: 4px;
|
||||
right: auto;
|
||||
left: 300px;
|
||||
width: 35px;
|
||||
top: auto !important;
|
||||
user-select: none;
|
||||
position: fixed;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,37 @@
|
||||
/* CLI11 tweaks on top of doxygen-awesome (sidebar-only layout). */
|
||||
|
||||
/* doxygen-awesome hides the sidebar expander arrows until hover, which makes
|
||||
collapsed sections (Guide, Examples, API Reference) easy to miss. Keep them
|
||||
visible. */
|
||||
html {
|
||||
--side-nav-arrow-opacity: 0.4;
|
||||
--side-nav-arrow-hover-opacity: 0.9;
|
||||
|
||||
/* The theme reserves --top-height for the sidebar header (title + search).
|
||||
Its 120px default is a few pixels too short for our logo + two-line brief,
|
||||
which clips the bottom of the search bar. */
|
||||
--top-height: 140px;
|
||||
}
|
||||
|
||||
/* The sidebar header already shows the project name, so the always-expanded
|
||||
"CLI11" root node in the navtree is redundant. Hide its row and shift the
|
||||
remaining rows left one indent level (the tree indents 16px per level with
|
||||
inline padding, so a fixed negative margin promotes every row equally). */
|
||||
#nav-tree-contents > ul > li > div.item {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#nav-tree-contents ul.children_ul .arrow {
|
||||
margin-left: -16px;
|
||||
}
|
||||
|
||||
/* Place the dark-mode toggle in the top-right corner of the sidebar header. */
|
||||
#titlearea {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
doxygen-awesome-dark-mode-toggle {
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
right: 8px;
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
# Example programs {#example-programs}
|
||||
|
||||
The [`examples/`][examples] directory holds small, complete programs. Each one
|
||||
builds and runs on its own, so you can copy a file into your project and change
|
||||
it. Build them with the `CLI11_BUILD_EXAMPLES` CMake option, which is on by
|
||||
default when CLI11 is the top level project.
|
||||
|
||||
Read @ref book-an-advanced-example for a step by step walkthrough of a larger
|
||||
program.
|
||||
|
||||
## Start here
|
||||
|
||||
`minimal.cpp` is the smallest program that parses and exits correctly. Use it as
|
||||
a starting point for a new application:
|
||||
|
||||
\include minimal.cpp
|
||||
|
||||
`simple.cpp` adds options, a flag, and a version flag, and shows how to read the
|
||||
values and the counts back after the parse:
|
||||
|
||||
\include simple.cpp
|
||||
|
||||
## Options and flags
|
||||
|
||||
| Example | Shows |
|
||||
| ---------------------------- | ------------------------------------------------------------ |
|
||||
| [array_option.cpp][] | An option that fills a `std::array` |
|
||||
| [digit_args.cpp][] | Numbered flags (`-1` to `-9`) with values attached |
|
||||
| [enum.cpp][] | An option that takes an `enum class` value |
|
||||
| [enum_ostream.cpp][] | A custom `operator<<` for an `enum class` in help and errors |
|
||||
| [ranges.cpp][] | Options that accept a range of values |
|
||||
| [retired.cpp][] | Retired and deprecated options |
|
||||
| [inter_argument_order.cpp][] | Recovery of the original order of unlimited arguments |
|
||||
| [custom_parse.cpp][] | A custom lexical cast for a user type |
|
||||
|
||||
## Validators
|
||||
|
||||
| Example | Shows |
|
||||
| ----------------------------- | -------------------------------------------------- |
|
||||
| [validators.cpp][] | The built in `ExistingFile` and `Range` validators |
|
||||
| [custom_validator.cpp][] | A `CLI::Validator` subclass |
|
||||
| [date_validator.cpp][] | A `CLI::CustomValidator` built from a lambda |
|
||||
| [positional_validation.cpp][] | Validators that select between positionals |
|
||||
|
||||
## Subcommands and groups
|
||||
|
||||
| Example | Shows |
|
||||
| -------------------------- | ------------------------------------------------------ |
|
||||
| [subcommands.cpp][] | Basic subcommands and `--help-all` |
|
||||
| [subcom_help.cpp][] | A required argument on a subcommand |
|
||||
| [subcom_partitioned.cpp][] | Subcommands built as separate `CLI::App_p` objects |
|
||||
| [subcom_in_files][] | A subcommand defined in its own source and header file |
|
||||
| [shapes.cpp][] | Repeated subcommands with immediate callbacks |
|
||||
| [nested.cpp][] | Nested subcommands |
|
||||
| [groups.cpp][] | Help groups |
|
||||
| [option_groups.cpp][] | Option groups and the requirements between them |
|
||||
| [positional_arity.cpp][] | Option groups that select on the number of positionals |
|
||||
|
||||
## Help output
|
||||
|
||||
| Example | Shows |
|
||||
| ------------------- | ------------------------------------------------ |
|
||||
| [formatter.cpp][] | A `CLI::Formatter` subclass |
|
||||
| [help_usage.cpp][] | A custom usage line and wide character support |
|
||||
| [modhelp.cpp][] | Help printed after the parse, with option values |
|
||||
| [close_match.cpp][] | A "did you mean" message for an unknown option |
|
||||
|
||||
## Configuration files
|
||||
|
||||
| Example | Shows |
|
||||
| ------------------ | --------------------------------------------------------------- |
|
||||
| [config_app.cpp][] | Reading a configuration file and printing the result |
|
||||
| [json.cpp][] | A JSON configuration file, with [nlohmann/json][] as the parser |
|
||||
|
||||
## Passing arguments on
|
||||
|
||||
| Example | Shows |
|
||||
| ---------------------------- | ----------------------------------------------- |
|
||||
| [prefix_command.cpp][] | `prefix_command` on the main application |
|
||||
| [arg_capture.cpp][] | `prefix_command` on a subcommand, with an alias |
|
||||
| [callback_passthrough.cpp][] | A callback that parses the remaining arguments |
|
||||
| [testEXE.cpp][] | Arguments collected for another program |
|
||||
|
||||
[examples]: https://github.com/CLIUtils/CLI11/tree/main/examples
|
||||
[nlohmann/json]: https://github.com/nlohmann/json
|
||||
[arg_capture.cpp]:
|
||||
https://github.com/CLIUtils/CLI11/blob/main/examples/arg_capture.cpp
|
||||
[array_option.cpp]:
|
||||
https://github.com/CLIUtils/CLI11/blob/main/examples/array_option.cpp
|
||||
[callback_passthrough.cpp]:
|
||||
https://github.com/CLIUtils/CLI11/blob/main/examples/callback_passthrough.cpp
|
||||
[close_match.cpp]:
|
||||
https://github.com/CLIUtils/CLI11/blob/main/examples/close_match.cpp
|
||||
[config_app.cpp]:
|
||||
https://github.com/CLIUtils/CLI11/blob/main/examples/config_app.cpp
|
||||
[custom_parse.cpp]:
|
||||
https://github.com/CLIUtils/CLI11/blob/main/examples/custom_parse.cpp
|
||||
[custom_validator.cpp]:
|
||||
https://github.com/CLIUtils/CLI11/blob/main/examples/custom_validator.cpp
|
||||
[date_validator.cpp]:
|
||||
https://github.com/CLIUtils/CLI11/blob/main/examples/date_validator.cpp
|
||||
[digit_args.cpp]:
|
||||
https://github.com/CLIUtils/CLI11/blob/main/examples/digit_args.cpp
|
||||
[enum.cpp]: https://github.com/CLIUtils/CLI11/blob/main/examples/enum.cpp
|
||||
[enum_ostream.cpp]:
|
||||
https://github.com/CLIUtils/CLI11/blob/main/examples/enum_ostream.cpp
|
||||
[formatter.cpp]:
|
||||
https://github.com/CLIUtils/CLI11/blob/main/examples/formatter.cpp
|
||||
[groups.cpp]: https://github.com/CLIUtils/CLI11/blob/main/examples/groups.cpp
|
||||
[help_usage.cpp]:
|
||||
https://github.com/CLIUtils/CLI11/blob/main/examples/help_usage.cpp
|
||||
[inter_argument_order.cpp]:
|
||||
https://github.com/CLIUtils/CLI11/blob/main/examples/inter_argument_order.cpp
|
||||
[json.cpp]: https://github.com/CLIUtils/CLI11/blob/main/examples/json.cpp
|
||||
[modhelp.cpp]: https://github.com/CLIUtils/CLI11/blob/main/examples/modhelp.cpp
|
||||
[nested.cpp]: https://github.com/CLIUtils/CLI11/blob/main/examples/nested.cpp
|
||||
[option_groups.cpp]:
|
||||
https://github.com/CLIUtils/CLI11/blob/main/examples/option_groups.cpp
|
||||
[positional_arity.cpp]:
|
||||
https://github.com/CLIUtils/CLI11/blob/main/examples/positional_arity.cpp
|
||||
[positional_validation.cpp]:
|
||||
https://github.com/CLIUtils/CLI11/blob/main/examples/positional_validation.cpp
|
||||
[prefix_command.cpp]:
|
||||
https://github.com/CLIUtils/CLI11/blob/main/examples/prefix_command.cpp
|
||||
[ranges.cpp]: https://github.com/CLIUtils/CLI11/blob/main/examples/ranges.cpp
|
||||
[retired.cpp]: https://github.com/CLIUtils/CLI11/blob/main/examples/retired.cpp
|
||||
[shapes.cpp]: https://github.com/CLIUtils/CLI11/blob/main/examples/shapes.cpp
|
||||
[subcom_help.cpp]:
|
||||
https://github.com/CLIUtils/CLI11/blob/main/examples/subcom_help.cpp
|
||||
[subcom_in_files]:
|
||||
https://github.com/CLIUtils/CLI11/tree/main/examples/subcom_in_files
|
||||
[subcom_partitioned.cpp]:
|
||||
https://github.com/CLIUtils/CLI11/blob/main/examples/subcom_partitioned.cpp
|
||||
[subcommands.cpp]:
|
||||
https://github.com/CLIUtils/CLI11/blob/main/examples/subcommands.cpp
|
||||
[testEXE.cpp]: https://github.com/CLIUtils/CLI11/blob/main/examples/testEXE.cpp
|
||||
[validators.cpp]:
|
||||
https://github.com/CLIUtils/CLI11/blob/main/examples/validators.cpp
|
||||
@@ -0,0 +1,123 @@
|
||||
<!-- HTML header for doxygen 1.17.0-->
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" lang="$langISO">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=11"/>
|
||||
<meta name="generator" content="Doxygen $doxygenversion"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
||||
<!--BEGIN PROJECT_NAME--><title>$projectname: $title</title><!--END PROJECT_NAME-->
|
||||
<!--BEGIN !PROJECT_NAME--><title>$title</title><!--END !PROJECT_NAME-->
|
||||
<!--BEGIN PROJECT_ICON-->
|
||||
<link rel="icon" href="$relpath^$projecticon" type="image/x-icon" />
|
||||
<!--END PROJECT_ICON-->
|
||||
<link href="$relpath^tabs.css" rel="stylesheet" type="text/css"/>
|
||||
<!--BEGIN FULL_SIDEBAR-->
|
||||
<script type="text/javascript">var page_layout=1;</script>
|
||||
<!--END FULL_SIDEBAR-->
|
||||
<!--BEGIN HTML_DYNAMIC_SECTIONS-->
|
||||
<script type="text/javascript" src="$relpath^dynsections.js"></script>
|
||||
<!--END HTML_DYNAMIC_SECTIONS-->
|
||||
<!--BEGIN HTML_CODE_FOLDING-->
|
||||
<script type="text/javascript" src="$relpath^codefolding.js"></script>
|
||||
<!--END HTML_CODE_FOLDING-->
|
||||
<!--BEGIN COPY_CLIPBOARD-->
|
||||
<script type="text/javascript" src="$relpath^clipboard.js"></script>
|
||||
<!--END COPY_CLIPBOARD-->
|
||||
$treeview
|
||||
$search
|
||||
$mathjax
|
||||
$darkmode
|
||||
$mermaidjs
|
||||
<link href="$relpath^$stylesheet" rel="stylesheet" type="text/css" />
|
||||
$extrastylesheet
|
||||
<script type="text/javascript" src="$relpath^doxygen-awesome-darkmode-toggle.js"></script>
|
||||
<script type="text/javascript">
|
||||
// jQuery-free replacement for DoxygenAwesomeDarkModeToggle.init();
|
||||
// doxygen >= 1.12 no longer ships jquery.js.
|
||||
window.addEventListener("DOMContentLoaded", () => {
|
||||
const toggleButton = document.createElement("doxygen-awesome-dark-mode-toggle")
|
||||
toggleButton.title = DoxygenAwesomeDarkModeToggle.title
|
||||
toggleButton.updateIcon()
|
||||
window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", () => {
|
||||
toggleButton.updateIcon()
|
||||
})
|
||||
document.addEventListener("visibilitychange", () => {
|
||||
if (document.visibilityState === "visible") {
|
||||
toggleButton.updateIcon()
|
||||
}
|
||||
})
|
||||
document.getElementById("titlearea").appendChild(toggleButton)
|
||||
})
|
||||
// The navtree renders with every group collapsed except the current
|
||||
// page's path. Start the top-level groups (Guide, Examples, API
|
||||
// Reference) open. The tree is built asynchronously, so wait for it to
|
||||
// appear.
|
||||
window.addEventListener("load", () => {
|
||||
const expandTopLevel = () => {
|
||||
const items = document.querySelectorAll(
|
||||
"#nav-tree-contents > ul > li > ul > li > .item")
|
||||
if (!items.length) return false
|
||||
items.forEach((item) => {
|
||||
const arrow = item.querySelector(".arrowhead.closed")
|
||||
if (arrow) arrow.closest("a").click()
|
||||
})
|
||||
return true
|
||||
}
|
||||
if (expandTopLevel()) return
|
||||
const contents = document.getElementById("nav-tree-contents")
|
||||
if (!contents) return
|
||||
const observer = new MutationObserver(() => {
|
||||
if (expandTopLevel()) observer.disconnect()
|
||||
})
|
||||
observer.observe(contents, { childList: true, subtree: true })
|
||||
})
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<!--BEGIN FULL_SIDEBAR-->
|
||||
<div id="side-nav" class="ui-resizable side-nav-resizable"><!-- do not remove this div, it is closed by doxygen! -->
|
||||
<!--END FULL_SIDEBAR-->
|
||||
|
||||
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
|
||||
|
||||
<!--BEGIN TITLEAREA-->
|
||||
<div id="titlearea">
|
||||
<table cellspacing="0" cellpadding="0">
|
||||
<tbody>
|
||||
<tr id="projectrow">
|
||||
<!--BEGIN PROJECT_LOGO-->
|
||||
<td id="projectlogo"><img alt="Logo" src="$relpath^$projectlogo"$logosize/></td>
|
||||
<!--END PROJECT_LOGO-->
|
||||
<!--BEGIN PROJECT_NAME-->
|
||||
<td id="projectalign">
|
||||
<div id="projectname">$projectname<!--BEGIN PROJECT_NUMBER--><span id="projectnumber"> $projectnumber</span><!--END PROJECT_NUMBER-->
|
||||
</div>
|
||||
<!--BEGIN PROJECT_BRIEF--><div id="projectbrief">$projectbrief</div><!--END PROJECT_BRIEF-->
|
||||
</td>
|
||||
<!--END PROJECT_NAME-->
|
||||
<!--BEGIN !PROJECT_NAME-->
|
||||
<!--BEGIN PROJECT_BRIEF-->
|
||||
<td>
|
||||
<div id="projectbrief">$projectbrief</div>
|
||||
</td>
|
||||
<!--END PROJECT_BRIEF-->
|
||||
<!--END !PROJECT_NAME-->
|
||||
<!--BEGIN DISABLE_INDEX-->
|
||||
<!--BEGIN SEARCHENGINE-->
|
||||
<!--BEGIN !FULL_SIDEBAR-->
|
||||
<td>$searchbox</td>
|
||||
<!--END !FULL_SIDEBAR-->
|
||||
<!--END SEARCHENGINE-->
|
||||
<!--END DISABLE_INDEX-->
|
||||
</tr>
|
||||
<!--BEGIN SEARCHENGINE-->
|
||||
<!--BEGIN FULL_SIDEBAR-->
|
||||
<tr><td colspan="2">$searchbox</td></tr>
|
||||
<!--END FULL_SIDEBAR-->
|
||||
<!--END SEARCHENGINE-->
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!--END TITLEAREA-->
|
||||
<!-- end header part -->
|
||||
@@ -0,0 +1,123 @@
|
||||
# Introduction {#mainpage}
|
||||
|
||||
CLI11 lets you write your own command line programs in C++. The library is
|
||||
designed to be clean, intuitive, but powerful. There are no requirements beyond
|
||||
C++11 support (and even `<regex>` support not required). It works on Mac, Linux,
|
||||
and Windows, and has 100% test coverage on all three systems. You can simply
|
||||
drop in a single header file (`CLI11.hpp` available in [releases][]) to use
|
||||
CLI11 in your own application. Other ways to integrate it into a build system
|
||||
are listed in the [README][].
|
||||
|
||||
The library was inspired by the Python libraries [Plumbum][] and [Click][], and
|
||||
incorporates many of their user friendly features.
|
||||
|
||||
The syntax is simple and scales from a basic application to a massive physics
|
||||
analysis with multiple models and many parameters and switches. For example,
|
||||
this is a simple program that has an optional parameter that defaults to 0:
|
||||
|
||||
```text
|
||||
$ ./a.out
|
||||
Parameter value: 0
|
||||
|
||||
$ ./a.out -p 4
|
||||
Parameter value: 4
|
||||
|
||||
$ ./a.out --help
|
||||
App description
|
||||
Usage: ./a.out [OPTIONS]
|
||||
|
||||
Options:
|
||||
-h,--help Print this help message and exit
|
||||
-p INT Parameter
|
||||
```
|
||||
|
||||
Like any good command line application, help is provided. This program can be
|
||||
implemented in only a few lines:
|
||||
|
||||
\include intro.cpp
|
||||
|
||||
[Source code](https://github.com/CLIUtils/CLI11/blob/main/book/code/intro.cpp)
|
||||
|
||||
Unlike some other libraries, this is enough to exit correctly and cleanly if
|
||||
help is requested or if incorrect arguments are passed. You can try this example
|
||||
out for yourself. To compile with GCC:
|
||||
|
||||
```text
|
||||
c++ -std=c++11 intro.cpp
|
||||
```
|
||||
|
||||
Much more complicated options are handled elegantly:
|
||||
|
||||
```cpp
|
||||
std::string file;
|
||||
app.add_option("-f,--file", file, "Require an existing file")
|
||||
->required()
|
||||
->check(CLI::ExistingFile);
|
||||
```
|
||||
|
||||
You can use any valid type; the above example could have used a
|
||||
`boost::file_system` file instead of a `std::string`. The value is a real value
|
||||
and does not require any special lookups to access. You do not have to risk
|
||||
typos by repeating the values after parsing like some libraries require. The
|
||||
library also handles positional arguments, flags, fixed or unlimited repeating
|
||||
options, interdependent options, flags, custom validators, help groups, and
|
||||
more.
|
||||
|
||||
You can use subcommands, as well. Subcommands support callback lambda functions
|
||||
when parsed, or they can be checked later. You can infinitely nest subcommands,
|
||||
and each is a full `App` instance, supporting everything listed above.
|
||||
|
||||
Reading/producing `.ini` files for configuration is also supported, as is using
|
||||
environment variables as input. The base `App` can be subclassed and customized
|
||||
for use in a toolkit (like [GooFit][]). All the standard shell idioms, like
|
||||
`--`, work as well.
|
||||
|
||||
The guide chapters in the sidebar walk through the library step by step,
|
||||
starting with [installation](@ref book-installation) and a first program. The
|
||||
examples section shows complete programs: the @ref book-an-advanced-example
|
||||
walkthrough, and @ref example-programs, an index of the programs that ship with
|
||||
CLI11. Feel free to contribute to [the guide here][cli11guide] if something can
|
||||
be improved!
|
||||
|
||||
## API reference
|
||||
|
||||
The main classes are:
|
||||
|
||||
| Name | Where used |
|
||||
| -------------- | --------------------------------------------------------- |
|
||||
| CLI::Option | Options, stored in the app |
|
||||
| CLI::App | The main application or subcommands |
|
||||
| CLI::Validator | A check that can affect the type name |
|
||||
| CLI::Formatter | A subclassable formatter for help printing |
|
||||
| CLI::ExitCode | A scoped enum with exit codes |
|
||||
| CLI::Timer | A timer class, only in CLI/Timer.hpp (not in `CLI11.hpp`) |
|
||||
| CLI::AutoTimer | A timer that prints on deletion |
|
||||
|
||||
Groups of related topics:
|
||||
|
||||
| Name | Description |
|
||||
| -------------------- | ---------------------------------------------- |
|
||||
| @ref error_group | Errors that can be thrown |
|
||||
| @ref validator_group | Common validators used in CLI::Option::check() |
|
||||
|
||||
Full indexes:
|
||||
|
||||
- [Errors & Validators](topics.html)
|
||||
- [Namespace List](namespaces.html)
|
||||
- [Class List](annotated.html)
|
||||
|
||||
## About
|
||||
|
||||
CLI11 was developed at the [University of Cincinnati][] in support of the
|
||||
[GooFit][] library under [NSF Award 1414736][nsf 1414736]. It was featured in a
|
||||
[DIANA/HEP][] meeting at CERN. Please give it a try! Feedback is always welcome.
|
||||
|
||||
[goofit]: https://github.com/GooFit/GooFit
|
||||
[diana/hep]: https://diana-hep.org
|
||||
[releases]: https://github.com/CLIUtils/CLI11/releases
|
||||
[readme]: https://github.com/CLIUtils/CLI11/blob/main/README.md
|
||||
[nsf 1414736]: https://nsf.gov/awardsearch/showAward?AWD_ID=1414736
|
||||
[university of cincinnati]: https://www.uc.edu
|
||||
[cli11guide]: https://github.com/CLIUtils/CLI11/tree/main/book
|
||||
[plumbum]: https://plumbum.readthedocs.io/en/latest/
|
||||
[click]: https://click.palletsprojects.com
|
||||
@@ -0,0 +1,317 @@
|
||||
function(add_cli_exe T)
|
||||
add_executable(${T} ${ARGN})
|
||||
target_link_libraries(${T} PUBLIC CLI11)
|
||||
set_property(TARGET ${T} PROPERTY FOLDER "Examples")
|
||||
if(CLI11_FORCE_LIBCXX)
|
||||
set_property(
|
||||
TARGET ${T}
|
||||
APPEND_STRING
|
||||
PROPERTY LINK_FLAGS -stdlib=libc++)
|
||||
target_link_libraries(${T} PUBLIC c++abi)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
if(CLI11_BUILD_EXAMPLES_JSON AND CMAKE_VERSION VERSION_GREATER "3.14.7")
|
||||
message(STATUS "Using nlohmann/json")
|
||||
FetchContent_Declare(
|
||||
json
|
||||
URL https://github.com/nlohmann/json/releases/download/v3.7.3/include.zip
|
||||
URL_HASH "SHA256=87b5884741427220d3a33df1363ae0e8b898099fbc59f1c451113f6732891014")
|
||||
|
||||
FetchContent_MakeAvailable(json)
|
||||
|
||||
add_cli_exe(json json.cpp)
|
||||
target_include_directories(json PUBLIC SYSTEM "${json_SOURCE_DIR}/single_include")
|
||||
|
||||
add_test(NAME json_config_out COMMAND json --item 2)
|
||||
set_property(TEST json_config_out PROPERTY PASS_REGULAR_EXPRESSION
|
||||
[=[\{.*"item": "2".*"simple": false.*\}]=])
|
||||
|
||||
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/input.json" [=[{"item":3,"simple":false}]=])
|
||||
add_test(NAME json_config_in COMMAND json --config "${CMAKE_CURRENT_BINARY_DIR}/input.json")
|
||||
set_property(TEST json_config_in PROPERTY PASS_REGULAR_EXPRESSION
|
||||
[=[\{.*"item": "3".*"simple": false.*\}]=])
|
||||
endif()
|
||||
|
||||
add_cli_exe(simple simple.cpp)
|
||||
add_test(NAME simple_basic COMMAND simple)
|
||||
add_test(NAME simple_all COMMAND simple -f filename.txt -c 12 --flag --flag -d 1.2)
|
||||
set_property(
|
||||
TEST simple_all
|
||||
PROPERTY
|
||||
PASS_REGULAR_EXPRESSION
|
||||
[=[Working on file: filename.txt, direct count: 1, opt count: 1.*Working on count: 12, direct count: 1, opt count: 1.*Received flag: 2 \(2\) times.*Some value: 1\.2]=]
|
||||
)
|
||||
|
||||
add_test(NAME simple_version COMMAND simple --version)
|
||||
set_property(TEST simple_version PROPERTY PASS_REGULAR_EXPRESSION "${CLI11_VERSION}")
|
||||
|
||||
add_cli_exe(subcommands subcommands.cpp)
|
||||
add_test(NAME subcommands_none COMMAND subcommands)
|
||||
set_property(TEST subcommands_none PROPERTY PASS_REGULAR_EXPRESSION "A subcommand is required")
|
||||
add_test(
|
||||
NAME subcommands_all
|
||||
COMMAND subcommands --random start --file
|
||||
name stop --count)
|
||||
set_property(
|
||||
TEST subcommands_all
|
||||
PROPERTY
|
||||
PASS_REGULAR_EXPRESSION
|
||||
[=[Working on --file from start: name.*Working on --count from stop: 1, direct count: 1.*Count of --random flag: 1.*Subcommand: start.*Subcommand: stop]=]
|
||||
)
|
||||
|
||||
add_cli_exe(subcom_partitioned subcom_partitioned.cpp)
|
||||
add_test(NAME subcom_partitioned_none COMMAND subcom_partitioned)
|
||||
set_property(
|
||||
TEST subcom_partitioned_none
|
||||
PROPERTY PASS_REGULAR_EXPRESSION
|
||||
[=[--file is required.*Run with --help for more information\..*This is a timer:]=])
|
||||
|
||||
add_test(NAME subcom_partitioned_all COMMAND subcom_partitioned --file this --count --count -d 1.2)
|
||||
set_property(
|
||||
TEST subcom_partitioned_all
|
||||
PROPERTY
|
||||
PASS_REGULAR_EXPRESSION
|
||||
[=[Working on file: this, direct count: 1, opt count: 1.*Working on count: 2, direct count: 2, opt count: 2.*Some value: 1\.2.*This is a timer:]=]
|
||||
)
|
||||
# test shows that the help prints out for unnamed subcommands
|
||||
add_test(NAME subcom_partitioned_help COMMAND subcom_partitioned --help)
|
||||
set_property(
|
||||
TEST subcom_partitioned_help
|
||||
PROPERTY PASS_REGULAR_EXPRESSION
|
||||
[=[-f,[ \t]*--file TEXT REQUIRED(.|[\r\n])*-d,[ \t]*--double FLOAT]=])
|
||||
|
||||
####################################################
|
||||
add_cli_exe(config_app config_app.cpp)
|
||||
add_test(NAME config_app1 COMMAND config_app -p)
|
||||
set_property(TEST config_app1 PROPERTY PASS_REGULAR_EXPRESSION "file=")
|
||||
|
||||
add_test(NAME config_app2 COMMAND config_app -p -f /)
|
||||
set_property(TEST config_app2 PROPERTY PASS_REGULAR_EXPRESSION "file=\"/\"")
|
||||
|
||||
add_test(NAME config_app3 COMMAND config_app -f "" -p)
|
||||
set_property(TEST config_app3 PROPERTY PASS_REGULAR_EXPRESSION "file=\"\"")
|
||||
|
||||
add_test(NAME config_app4 COMMAND config_app -f "/" -p)
|
||||
set_property(TEST config_app4 PROPERTY PASS_REGULAR_EXPRESSION "file=\"/\"")
|
||||
|
||||
####################################################
|
||||
|
||||
add_cli_exe(option_groups option_groups.cpp)
|
||||
add_test(NAME option_groups_missing COMMAND option_groups)
|
||||
set_property(TEST option_groups_missing PROPERTY PASS_REGULAR_EXPRESSION
|
||||
[=[Exactly 1 option from(.|[\r\n])*is required]=])
|
||||
add_test(NAME option_groups_extra COMMAND option_groups --csv --binary)
|
||||
set_property(TEST option_groups_extra PROPERTY PASS_REGULAR_EXPRESSION "but 2 were given")
|
||||
add_test(NAME option_groups_extra2 COMMAND option_groups --csv --address "192.168.1.1" -o
|
||||
"test.out")
|
||||
set_property(TEST option_groups_extra2 PROPERTY PASS_REGULAR_EXPRESSION "at most 1")
|
||||
|
||||
add_cli_exe(positional_arity positional_arity.cpp)
|
||||
add_test(NAME positional_arity1 COMMAND positional_arity one)
|
||||
set_property(TEST positional_arity1 PROPERTY PASS_REGULAR_EXPRESSION "File 1 = one")
|
||||
add_test(NAME positional_arity2 COMMAND positional_arity one two)
|
||||
set_property(TEST positional_arity2 PROPERTY PASS_REGULAR_EXPRESSION
|
||||
[=[File 1 = one.*File 2 = two]=])
|
||||
add_test(NAME positional_arity3 COMMAND positional_arity 1 2 one)
|
||||
set_property(TEST positional_arity3 PROPERTY PASS_REGULAR_EXPRESSION "File 1 = one")
|
||||
add_test(NAME positional_arity_fail COMMAND positional_arity 1 one two)
|
||||
set_property(TEST positional_arity_fail PROPERTY PASS_REGULAR_EXPRESSION "Could not convert")
|
||||
|
||||
add_cli_exe(positional_validation positional_validation.cpp)
|
||||
add_test(NAME positional_validation1 COMMAND positional_validation one)
|
||||
set_property(TEST positional_validation1 PROPERTY PASS_REGULAR_EXPRESSION "File 1 = one")
|
||||
add_test(NAME positional_validation2 COMMAND positional_validation one 1 2 two)
|
||||
set_property(TEST positional_validation2 PROPERTY PASS_REGULAR_EXPRESSION
|
||||
[=[File 1 = one.*File 2 = two]=])
|
||||
add_test(NAME positional_validation3 COMMAND positional_validation 1 2 one)
|
||||
set_property(TEST positional_validation3 PROPERTY PASS_REGULAR_EXPRESSION "File 1 = one")
|
||||
add_test(NAME positional_validation4 COMMAND positional_validation 1 one two 2)
|
||||
set_property(TEST positional_validation4 PROPERTY PASS_REGULAR_EXPRESSION
|
||||
[=[File 1 = one.*File 2 = two]=])
|
||||
if((CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0)
|
||||
OR NOT CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
#this test doesn't work without get_time which isn't available until GCC 5.0
|
||||
add_cli_exe(date_validator date_validator.cpp)
|
||||
add_test(NAME date_validator1 COMMAND date_validator --time 2025-08-14T14:30:00)
|
||||
set_property(TEST date_validator1 PROPERTY PASS_REGULAR_EXPRESSION "date given")
|
||||
add_test(NAME date_validator2 COMMAND date_validator --time 8/17/2025T14:30:00)
|
||||
set_property(TEST date_validator2 PROPERTY PASS_REGULAR_EXPRESSION "Failed to parse time string")
|
||||
endif()
|
||||
|
||||
add_cli_exe(custom_validator custom_validator.cpp)
|
||||
add_test(NAME custom_validator1 COMMAND custom_validator --number 12)
|
||||
set_property(TEST custom_validator1 PROPERTY PASS_REGULAR_EXPRESSION "value not within range")
|
||||
add_test(NAME custom_validator2 COMMAND custom_validator --number 12 --center 10 --range 3)
|
||||
set_property(TEST custom_validator2 PROPERTY PASS_REGULAR_EXPRESSION "number 12 in range")
|
||||
|
||||
add_cli_exe(shapes shapes.cpp)
|
||||
add_test(NAME shapes_all COMMAND shapes circle 4.4 circle 10.7 rectangle 4 4 circle 2.3 triangle
|
||||
4.5 ++ rectangle 2.1 ++ circle 234.675)
|
||||
set_property(
|
||||
TEST shapes_all
|
||||
PROPERTY
|
||||
PASS_REGULAR_EXPRESSION
|
||||
[=[circle2 with radius 10\.7(.|[\r\n])*triangle1 with sides \[4\.5\](.|[\r\n])*rectangle2 with edges \[2\.1,2\.1\](.|[\r\n])*circle4 with radius 234\.675]=]
|
||||
)
|
||||
|
||||
add_cli_exe(ranges ranges.cpp)
|
||||
add_test(NAME ranges_range COMMAND ranges --range 1 2 3)
|
||||
set_property(TEST ranges_range PROPERTY PASS_REGULAR_EXPRESSION "[2:1:3]")
|
||||
add_test(NAME ranges_minmax COMMAND ranges --min 2 --max 3)
|
||||
set_property(TEST ranges_minmax PROPERTY PASS_REGULAR_EXPRESSION "[2:1:3]")
|
||||
add_test(NAME ranges_error COMMAND ranges --min 2 --max 3 --step 1 --range 1 2 3)
|
||||
set_property(TEST ranges_error PROPERTY PASS_REGULAR_EXPRESSION "Exactly 1 option from")
|
||||
|
||||
add_cli_exe(validators validators.cpp)
|
||||
add_test(NAME validators_help COMMAND validators --help)
|
||||
set_property(
|
||||
TEST validators_help
|
||||
PROPERTY
|
||||
PASS_REGULAR_EXPRESSION
|
||||
[=[ -f,[ \t]*--file TEXT:FILE.*File name.* -v,[ \t]*--value INT:INT in \[3 - 6\].*Value in range]=]
|
||||
)
|
||||
add_test(NAME validators_file COMMAND validators --file nonex.xxx)
|
||||
set_property(
|
||||
TEST validators_file
|
||||
PROPERTY PASS_REGULAR_EXPRESSION
|
||||
[=[--file: File does not exist: nonex\.xxx.*Run with --help for more information\.]=])
|
||||
add_test(NAME validators_plain COMMAND validators --value 9)
|
||||
set_property(
|
||||
TEST validators_plain
|
||||
PROPERTY PASS_REGULAR_EXPRESSION
|
||||
[=[--value: Value 9 not in range \[3 - 6\].*Run with --help for more information\.]=])
|
||||
|
||||
add_cli_exe(groups groups.cpp)
|
||||
add_test(NAME groups_none COMMAND groups)
|
||||
set_property(
|
||||
TEST groups_none
|
||||
PROPERTY PASS_REGULAR_EXPRESSION
|
||||
[=[--file is required.*Run with --help for more information\..*This is a timer:]=])
|
||||
add_test(NAME groups_all COMMAND groups --file this --count --count -d 1.2)
|
||||
set_property(
|
||||
TEST groups_all
|
||||
PROPERTY
|
||||
PASS_REGULAR_EXPRESSION
|
||||
[=[Working on file: this, direct count: 1, opt count: 1.*Working on count: 2, direct count: 2, opt count: 2.*Some value: 1\.2.*This is a timer:]=]
|
||||
)
|
||||
|
||||
add_cli_exe(inter_argument_order inter_argument_order.cpp)
|
||||
add_test(NAME inter_argument_order COMMAND inter_argument_order --foo 1 2 3 --x --bar 4 5 6 --z
|
||||
--foo 7 8)
|
||||
set_property(
|
||||
TEST inter_argument_order
|
||||
PROPERTY
|
||||
PASS_REGULAR_EXPRESSION
|
||||
[=[foo : 1
|
||||
foo : 2
|
||||
foo : 3
|
||||
bar : 4
|
||||
bar : 5
|
||||
bar : 6
|
||||
foo : 7
|
||||
foo : 8]=])
|
||||
|
||||
add_cli_exe(prefix_command prefix_command.cpp)
|
||||
add_test(NAME prefix_command COMMAND prefix_command -v 3 2 1 -- other one two 3)
|
||||
set_property(
|
||||
TEST prefix_command PROPERTY PASS_REGULAR_EXPRESSION
|
||||
[=[Prefix: 3 : 2 : 1.*Remaining commands: other one two 3]=])
|
||||
|
||||
#just test a minimal build
|
||||
add_cli_exe(minimal minimal.cpp)
|
||||
add_test(NAME minimal COMMAND minimal)
|
||||
|
||||
add_cli_exe(arg_capture arg_capture.cpp)
|
||||
add_test(NAME arg_capture COMMAND arg_capture -v 27 --sub -v 13 --val prefix)
|
||||
set_property(TEST arg_capture PROPERTY PASS_REGULAR_EXPRESSION "value=27")
|
||||
add_test(NAME arg_capture2 COMMAND arg_capture -v 27 --sub -v 13 --val prefix)
|
||||
set_property(TEST arg_capture2 PROPERTY PASS_REGULAR_EXPRESSION "after Args:-v 13 --val prefix")
|
||||
|
||||
add_cli_exe(callback_passthrough callback_passthrough.cpp)
|
||||
add_test(NAME callback_passthrough1 COMMAND callback_passthrough --argname t2 --t2 test)
|
||||
set_property(TEST callback_passthrough1 PROPERTY PASS_REGULAR_EXPRESSION "the value is now test")
|
||||
add_test(NAME callback_passthrough2 COMMAND callback_passthrough --arg EEEK --argname arg)
|
||||
set_property(TEST callback_passthrough2 PROPERTY PASS_REGULAR_EXPRESSION "the value is now EEEK")
|
||||
|
||||
add_cli_exe(enum enum.cpp)
|
||||
add_test(NAME enum_pass COMMAND enum -l 1)
|
||||
add_test(NAME enum_fail COMMAND enum -l 4)
|
||||
set_property(
|
||||
TEST enum_fail
|
||||
PROPERTY PASS_REGULAR_EXPRESSION
|
||||
[=[--level: Check 4 value in \{.*FAILED.*Run with --help for more information\.]=])
|
||||
|
||||
add_cli_exe(enum_ostream enum_ostream.cpp)
|
||||
add_test(NAME enum_ostream_pass COMMAND enum_ostream --level medium)
|
||||
set_property(TEST enum_ostream_pass PROPERTY PASS_REGULAR_EXPRESSION "Enum received: Medium")
|
||||
|
||||
add_cli_exe(digit_args digit_args.cpp)
|
||||
add_test(NAME digit_args COMMAND digit_args -h)
|
||||
set_property(TEST digit_args PROPERTY PASS_REGULAR_EXPRESSION "-3{3}")
|
||||
|
||||
add_cli_exe(modhelp modhelp.cpp)
|
||||
add_test(NAME modhelp COMMAND modhelp -a test -h)
|
||||
set_property(TEST modhelp PROPERTY PASS_REGULAR_EXPRESSION "Option -a string in help: test")
|
||||
|
||||
add_cli_exe(array_option array_option.cpp)
|
||||
add_test(NAME array_option COMMAND array_option --a 1 2)
|
||||
set_property(TEST array_option PROPERTY PASS_REGULAR_EXPRESSION "pass")
|
||||
|
||||
add_subdirectory(subcom_in_files)
|
||||
add_test(NAME subcom_in_files COMMAND subcommand_main subcommand_a -f this.txt --with-foo)
|
||||
set_property(TEST subcom_in_files PROPERTY PASS_REGULAR_EXPRESSION
|
||||
[=[Working on file: this\.txt.*Using foo!]=])
|
||||
|
||||
add_cli_exe(formatter formatter.cpp)
|
||||
|
||||
add_cli_exe(nested nested.cpp)
|
||||
|
||||
add_cli_exe(subcom_help subcom_help.cpp)
|
||||
add_test(NAME subcom_help_normal COMMAND subcom_help sub --help)
|
||||
add_test(NAME subcom_help_reversed COMMAND subcom_help --help sub)
|
||||
|
||||
add_cli_exe(retired retired.cpp)
|
||||
add_test(NAME retired_retired_test COMMAND retired --retired_option)
|
||||
add_test(NAME retired_retired_test2 COMMAND retired --retired_option 567)
|
||||
add_test(NAME retired_retired_test3 COMMAND retired --retired_option2 567 689 789)
|
||||
add_test(NAME retired_deprecated COMMAND retired --deprecate 19 20)
|
||||
|
||||
set_property(TEST retired_retired_test PROPERTY PASS_REGULAR_EXPRESSION "WARNING.*retired")
|
||||
|
||||
set_property(TEST retired_retired_test2 PROPERTY PASS_REGULAR_EXPRESSION "WARNING.*retired")
|
||||
|
||||
set_property(TEST retired_retired_test3 PROPERTY PASS_REGULAR_EXPRESSION "WARNING.*retired")
|
||||
|
||||
set_property(TEST retired_deprecated PROPERTY PASS_REGULAR_EXPRESSION "deprecated.*not_deprecated")
|
||||
|
||||
if(CMAKE_CXX_STANDARD GREATER 13)
|
||||
add_cli_exe(close_match close_match.cpp)
|
||||
|
||||
add_test(NAME close_match_test COMMAND close_match i)
|
||||
add_test(NAME close_match_test2 COMMAND close_match upg)
|
||||
add_test(NAME close_match_test3 COMMAND close_match rem)
|
||||
add_test(NAME close_match_test4 COMMAND close_match upgrde)
|
||||
|
||||
set_property(TEST close_match_test PROPERTY PASS_REGULAR_EXPRESSION "install")
|
||||
|
||||
set_property(TEST close_match_test2 PROPERTY PASS_REGULAR_EXPRESSION "upgrade")
|
||||
|
||||
set_property(TEST close_match_test3 PROPERTY PASS_REGULAR_EXPRESSION "remove")
|
||||
|
||||
set_property(TEST close_match_test4 PROPERTY PASS_REGULAR_EXPRESSION "closest match is upgrade")
|
||||
endif()
|
||||
#--------------------------------------------
|
||||
add_cli_exe(custom_parse custom_parse.cpp)
|
||||
add_test(NAME cp_test COMMAND custom_parse --dv 1.7)
|
||||
set_property(TEST cp_test PROPERTY PASS_REGULAR_EXPRESSION "called correct")
|
||||
|
||||
#-----------------------------------------------------
|
||||
add_cli_exe(help_usage help_usage.cpp)
|
||||
add_test(NAME help_use COMMAND help_usage --help)
|
||||
set_property(TEST help_use PROPERTY PASS_REGULAR_EXPRESSION "[1..9]")
|
||||
|
||||
#------------------------------------------------
|
||||
# This executable is for manual testing and is expected to change regularly
|
||||
|
||||
add_cli_exe(tester testEXE.cpp)
|
||||
@@ -0,0 +1,34 @@
|
||||
// Copyright (c) 2017-2026, University of Cincinnati, developed by Henry Schreiner
|
||||
// under NSF AWARD 1414736 and by the respective contributors.
|
||||
// All rights reserved.
|
||||
//
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
// Code modified from https://github.com/CLIUtils/CLI11/issues/559
|
||||
|
||||
#include <CLI/CLI.hpp>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
/** This example demonstrates the use of `prefix_command` on a subcommand
|
||||
to capture all subsequent arguments along with an alias to make it appear as a regular options.
|
||||
|
||||
All the values after the "sub" or "--sub" are available in the remaining() method.
|
||||
*/
|
||||
int main(int argc, const char *argv[]) {
|
||||
|
||||
int value{0};
|
||||
CLI::App app{"Test App"};
|
||||
app.add_option("-v", value, "value");
|
||||
|
||||
auto *subcom = app.add_subcommand("sub", "")->prefix_command();
|
||||
subcom->alias("--sub");
|
||||
CLI11_PARSE(app, argc, argv);
|
||||
|
||||
std::cout << "value=" << value << '\n';
|
||||
std::cout << "after Args:";
|
||||
for(const auto &aarg : subcom->remaining()) {
|
||||
std::cout << aarg << " ";
|
||||
}
|
||||
std::cout << '\n';
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
// Copyright (c) 2017-2026, University of Cincinnati, developed by Henry Schreiner
|
||||
// under NSF AWARD 1414736 and by the respective contributors.
|
||||
// All rights reserved.
|
||||
//
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
// Code modified from Loic Gouarin(https://github.com/gouarin) https://github.com/CLIUtils/CLI11/issues/1135
|
||||
|
||||
#include <CLI/CLI.hpp>
|
||||
#include <array>
|
||||
#include <iostream>
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
std::array<int, 2> a{0, 1};
|
||||
CLI::App app{"My app"};
|
||||
app.add_option("--a", a, "an array")->capture_default_str();
|
||||
app.parse(argc, argv);
|
||||
|
||||
std::cout << "pass\n";
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// Copyright (c) 2017-2026, University of Cincinnati, developed by Henry Schreiner
|
||||
// under NSF AWARD 1414736 and by the respective contributors.
|
||||
// All rights reserved.
|
||||
//
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#include <CLI/CLI.hpp>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
|
||||
CLI::App app("callback_passthrough");
|
||||
app.allow_extras();
|
||||
std::string argName;
|
||||
std::string val;
|
||||
app.add_option("--argname", argName, "the name of the custom command line argument");
|
||||
app.callback([&app, &val, &argName]() {
|
||||
if(!argName.empty()) {
|
||||
CLI::App subApp;
|
||||
subApp.add_option("--" + argName, val, "custom argument option");
|
||||
subApp.parse(app.remaining_for_passthrough());
|
||||
}
|
||||
});
|
||||
|
||||
CLI11_PARSE(app, argc, argv);
|
||||
std::cout << "the value is now " << val << '\n';
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
// Copyright (c) 2017-2026, University of Cincinnati, developed by Henry Schreiner
|
||||
// under NSF AWARD 1414736 and by the respective contributors.
|
||||
// All rights reserved.
|
||||
//
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
// Code inspired by discussion from https://github.com/CLIUtils/CLI11/issues/1149
|
||||
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <numeric>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <CLI/CLI.hpp>
|
||||
|
||||
// only works with C++14 or higher
|
||||
|
||||
// Levenshtein distance function code generated by chatgpt/copilot
|
||||
std::size_t levenshteinDistance(const std::string &s1, const std::string &s2) {
|
||||
std::size_t len1 = s1.size(), len2 = s2.size();
|
||||
if(len1 == 0 || len2 == 0) {
|
||||
return (std::max)(len1, len2);
|
||||
}
|
||||
std::vector<std::size_t> prev(len2 + 1), curr(len2 + 1);
|
||||
std::iota(prev.begin(), prev.end(), 0); // Fill prev with {0, 1, ..., len2}
|
||||
|
||||
for(std::size_t ii = 1; ii <= len1; ++ii) {
|
||||
curr[0] = ii;
|
||||
for(std::size_t jj = 1; jj <= len2; ++jj) {
|
||||
// If characters match, no substitution cost; otherwise, cost is 1.
|
||||
std::size_t cost = (s1[ii - 1] == s2[jj - 1]) ? 0 : 1;
|
||||
|
||||
// Compute the minimum cost between:
|
||||
// - Deleting a character from `s1` (prev[jj] + 1)
|
||||
// - Inserting a character into `s1` (curr[jj - 1] + 1)
|
||||
// - Substituting a character (prev[jj - 1] + cost)
|
||||
|
||||
curr[jj] = (std::min)({prev[jj] + 1, curr[jj - 1] + 1, prev[jj - 1] + cost});
|
||||
}
|
||||
prev = std::exchange(curr, prev); // Swap vectors efficiently
|
||||
}
|
||||
return prev[len2];
|
||||
}
|
||||
|
||||
// Finds the closest string from a list (modified from chat gpt code)
|
||||
std::pair<std::string, std::size_t> findClosestMatch(const std::string &input,
|
||||
const std::vector<std::string> &candidates) {
|
||||
std::string closest;
|
||||
std::size_t minDistance{std::string::npos};
|
||||
for(const auto &candidate : candidates) {
|
||||
std::size_t distance = levenshteinDistance(input, candidate);
|
||||
if(distance < minDistance) {
|
||||
minDistance = distance;
|
||||
closest = candidate;
|
||||
}
|
||||
}
|
||||
|
||||
return {closest, minDistance};
|
||||
}
|
||||
|
||||
void addSubcommandCloseMatchDetection(CLI::App *app, std::size_t minDistance = 3) {
|
||||
// if extras are not allowed then there will be no remaining
|
||||
app->allow_extras(true);
|
||||
// generate a list of subcommand names
|
||||
auto subs = app->get_subcommands(nullptr);
|
||||
CLI::results_t list;
|
||||
for(const auto *sub : subs) {
|
||||
if(!sub->get_name().empty()) {
|
||||
list.emplace_back(sub->get_name());
|
||||
}
|
||||
const auto &aliases = sub->get_aliases();
|
||||
if(!aliases.empty()) {
|
||||
list.insert(list.end(), aliases.begin(), aliases.end());
|
||||
}
|
||||
}
|
||||
// add a callback that runs before a final callback and loops over the remaining arguments for subcommands
|
||||
app->parse_complete_callback([app, minDistance, list = std::move(list)]() {
|
||||
for(auto &extra : app->remaining()) {
|
||||
if(!extra.empty() && extra.front() != '-') {
|
||||
auto closest = findClosestMatch(extra, list);
|
||||
if(closest.second <= minDistance) {
|
||||
std::cout << "unmatched command \"" << extra << "\", closest match is " << closest.first << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** This example demonstrates the use of close match detection to detect invalid commands that are close matches to
|
||||
* existing ones
|
||||
*/
|
||||
int main(int argc, const char *argv[]) {
|
||||
|
||||
int value{0};
|
||||
CLI::App app{"App for testing prefix matching and close string matching"};
|
||||
// turn on prefix matching
|
||||
app.allow_subcommand_prefix_matching();
|
||||
app.add_option("-v", value, "value");
|
||||
|
||||
app.add_subcommand("install", "");
|
||||
app.add_subcommand("upgrade", "");
|
||||
app.add_subcommand("remove", "");
|
||||
app.add_subcommand("test", "");
|
||||
// enable close matching for subcommands
|
||||
addSubcommandCloseMatchDetection(&app, 5);
|
||||
CLI11_PARSE(app, argc, argv);
|
||||
|
||||
auto subs = app.get_subcommands();
|
||||
for(const auto &sub : subs) {
|
||||
std::cout << sub->get_name() << "\n";
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
// Copyright (c) 2017-2026, University of Cincinnati, developed by Henry Schreiner
|
||||
// under NSF AWARD 1414736 and by the respective contributors.
|
||||
// All rights reserved.
|
||||
//
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#include <CLI/CLI.hpp>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
|
||||
CLI::App app("configuration print example");
|
||||
|
||||
app.add_flag("-p,--print", "Print configuration and exit")->configurable(false); // NEW: print flag
|
||||
|
||||
std::string file;
|
||||
CLI::Option *opt = app.add_option("-f,--file,file", file, "File name")
|
||||
->capture_default_str()
|
||||
->run_callback_for_default(); // NEW: capture_default_str()
|
||||
|
||||
int count{0};
|
||||
CLI::Option *copt =
|
||||
app.add_option("-c,--count", count, "Counter")->capture_default_str(); // NEW: capture_default_str()
|
||||
|
||||
int v{0};
|
||||
CLI::Option *flag = app.add_flag("--flag", v, "Some flag that can be passed multiple times")
|
||||
->capture_default_str(); // NEW: capture_default_str()
|
||||
|
||||
double value{0.0}; // = 3.14;
|
||||
app.add_option("-d,--double", value, "Some Value")->capture_default_str(); // NEW: capture_default_str()
|
||||
|
||||
app.get_config_formatter_base()->quoteCharacter('"', '"');
|
||||
|
||||
CLI11_PARSE(app, argc, argv);
|
||||
|
||||
if(app.get_option("--print")->as<bool>()) { // NEW: print configuration and exit
|
||||
std::cout << app.config_to_str(true, false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::cout << "Working on file: " << file << ", direct count: " << app.count("--file")
|
||||
<< ", opt count: " << opt->count() << '\n';
|
||||
std::cout << "Working on count: " << count << ", direct count: " << app.count("--count")
|
||||
<< ", opt count: " << copt->count() << '\n';
|
||||
std::cout << "Received flag: " << v << " (" << flag->count() << ") times\n";
|
||||
std::cout << "Some value: " << value << '\n';
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
// Copyright (c) 2017-2026, University of Cincinnati, developed by Henry Schreiner
|
||||
// under NSF AWARD 1414736 and by the respective contributors.
|
||||
// All rights reserved.
|
||||
//
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
// from Issue #566 on github https://github.com/CLIUtils/CLI11/issues/566
|
||||
|
||||
#include <CLI/CLI.hpp>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
// example file to demonstrate a custom lexical cast function
|
||||
|
||||
template <class T = int> struct Values {
|
||||
T a;
|
||||
T b;
|
||||
T c;
|
||||
};
|
||||
|
||||
// in C++20 this is constructible from a double due to the new aggregate initialization in C++20.
|
||||
using DoubleValues = Values<double>;
|
||||
|
||||
// the lexical cast operator should be in the same namespace as the type for ADL to work properly
|
||||
bool lexical_cast(const std::string &input, Values<double> & /*v*/) {
|
||||
std::cout << "called correct lexical_cast function ! val: " << input << '\n';
|
||||
return true;
|
||||
}
|
||||
|
||||
DoubleValues doubles;
|
||||
void argparse(CLI::Option_group *group) { group->add_option("--dv", doubles)->default_str("0"); }
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
CLI::App app;
|
||||
|
||||
argparse(app.add_option_group("param"));
|
||||
CLI11_PARSE(app, argc, argv);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
// Copyright (c) 2017-2026, University of Cincinnati, developed by Henry Schreiner
|
||||
// under NSF AWARD 1414736 and by the respective contributors.
|
||||
// All rights reserved.
|
||||
//
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#include <CLI/CLI.hpp>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
template <typename T> class DeltaRange : public CLI::Validator {
|
||||
public:
|
||||
T center_point;
|
||||
T delta;
|
||||
DeltaRange(const T ¢er, const T &range)
|
||||
: CLI::Validator(
|
||||
[this](const std::string &value) -> std::string {
|
||||
T newValue;
|
||||
auto result = CLI::detail::lexical_cast(value, newValue);
|
||||
if(!(result && this->check(newValue))) {
|
||||
return std::string("value not within range");
|
||||
}
|
||||
return std::string{};
|
||||
},
|
||||
"RANGE"),
|
||||
center_point(center), delta(range) {}
|
||||
|
||||
CLI11_NODISCARD bool check(const T &test) const {
|
||||
return (test >= (center_point - delta)) && (test <= (center_point + delta));
|
||||
}
|
||||
CLI11_NODISCARD T center() const { return center_point; }
|
||||
CLI11_NODISCARD T range() const { return delta; }
|
||||
void center(const T &value) { center_point = value; }
|
||||
void range(const T &value) { delta = value; }
|
||||
};
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
/* this application creates custom validator which is a range center+/- range The center and range can be defined by
|
||||
* other command line options and are updated dynamically
|
||||
*/
|
||||
CLI::App app("custom range validator");
|
||||
|
||||
std::string value;
|
||||
auto dr = std::make_shared<DeltaRange<int>>(7, 3);
|
||||
app.add_option("--number", value, "enter value in the related range")->check(dr)->required();
|
||||
|
||||
app.add_option_function<int>("--center", [&dr](int new_center) { dr->center(new_center); })->trigger_on_parse();
|
||||
app.add_option_function<int>("--range", [&dr](int new_range) { dr->range(new_range); })->trigger_on_parse();
|
||||
|
||||
CLI11_PARSE(app, argc, argv);
|
||||
|
||||
std::cout << "number " << value << " in range = " << dr->center() << " +/- " << dr->range() << '\n';
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
// Copyright (c) 2017-2026, University of Cincinnati, developed by Henry Schreiner
|
||||
// under NSF AWARD 1414736 and by the respective contributors.
|
||||
// All rights reserved.
|
||||
//
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#include <CLI/CLI.hpp>
|
||||
#include <ctime>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
// Custom validator is an alias of Validator, the constructor takes a function that takes as input and returns a string
|
||||
const CLI::CustomValidator ISO8601(
|
||||
[](std::string &input) {
|
||||
std::tm tm = {};
|
||||
std::istringstream ss(input);
|
||||
|
||||
ss >> std::get_time(&tm, "%Y-%m-%dT%H:%M:%S");
|
||||
if(ss.fail()) {
|
||||
return std::string("Failed to parse time string");
|
||||
}
|
||||
return std::string{};
|
||||
},
|
||||
"datetime[%Y-%m-%dT%H:%M:%S]");
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
|
||||
CLI::App app("custom validator testing");
|
||||
|
||||
std::string value;
|
||||
app.add_option("--time", value, "enter a date in iso8601 format")->check(ISO8601)->required();
|
||||
|
||||
CLI11_PARSE(app, argc, argv);
|
||||
|
||||
std::cout << "date given = " << value << '\n';
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
// Copyright (c) 2017-2026, University of Cincinnati, developed by Henry Schreiner
|
||||
// under NSF AWARD 1414736 and by the respective contributors.
|
||||
// All rights reserved.
|
||||
//
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#include <CLI/CLI.hpp>
|
||||
#include <iostream>
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
CLI::App app;
|
||||
|
||||
int val{0};
|
||||
// add a set of flags with default values associate with them
|
||||
app.add_flag("-1{1},-2{2},-3{3},-4{4},-5{5},-6{6}, -7{7}, -8{8}, -9{9}", val, "compression level");
|
||||
|
||||
CLI11_PARSE(app, argc, argv);
|
||||
|
||||
std::cout << "value = " << val << '\n';
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
// Copyright (c) 2017-2026, University of Cincinnati, developed by Henry Schreiner
|
||||
// under NSF AWARD 1414736 and by the respective contributors.
|
||||
// All rights reserved.
|
||||
//
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#define CLI11_ENABLE_EXTRA_VALIDATORS 1
|
||||
#include <CLI/CLI.hpp>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
// NOLINTNEXTLINE
|
||||
enum class Level : int { High, Medium, Low };
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
CLI::App app;
|
||||
|
||||
Level level{Level::Low};
|
||||
// specify string->value mappings
|
||||
std::map<std::string, Level> map{{"high", Level::High}, {"medium", Level::Medium}, {"low", Level::Low}};
|
||||
// CheckedTransformer translates and checks whether the results are either in one of the strings or in one of the
|
||||
// translations already
|
||||
app.add_option("-l,--level", level, "Level settings")
|
||||
->required()
|
||||
->transform(CLI::CheckedTransformer(map, CLI::ignore_case));
|
||||
|
||||
CLI11_PARSE(app, argc, argv);
|
||||
|
||||
// CLI11's built in enum streaming can be used outside CLI11 like this:
|
||||
using CLI::enums::operator<<;
|
||||
std::cout << "Enum received: " << level << '\n';
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
// Copyright (c) 2017-2026, University of Cincinnati, developed by Henry Schreiner
|
||||
// under NSF AWARD 1414736 and by the respective contributors.
|
||||
// All rights reserved.
|
||||
//
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#define CLI11_ENABLE_EXTRA_VALIDATORS 1
|
||||
#include <CLI/CLI.hpp>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
enum class Level : int { High, Medium, Low };
|
||||
|
||||
// Defining operator<<() for your enum class (in this case for 'Level') overrides CLI11's enum streaming
|
||||
inline std::ostream &operator<<(std::ostream &os, const Level &level) {
|
||||
switch(level) {
|
||||
case Level::High:
|
||||
os << "High";
|
||||
break;
|
||||
case Level::Medium:
|
||||
os << "Medium";
|
||||
break;
|
||||
case Level::Low:
|
||||
os << "Low";
|
||||
break;
|
||||
}
|
||||
os << " (ft rom custom ostream)";
|
||||
return os;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
CLI::App app;
|
||||
|
||||
Level level{Level::Low};
|
||||
// specify string->value mappings
|
||||
std::map<std::string, Level> map{{"high", Level::High}, {"medium", Level::Medium}, {"low", Level::Low}};
|
||||
// CheckedTransformer translates and checks whether the results are either in one of the strings or in one of the
|
||||
// translations already
|
||||
app.add_option("-l,--level", level, "Level settings")
|
||||
->required()
|
||||
->transform(CLI::CheckedTransformer(map, CLI::ignore_case));
|
||||
|
||||
CLI11_PARSE(app, argc, argv);
|
||||
|
||||
// CLI11's built in enum streaming can be used outside CLI11 like this:
|
||||
using CLI::enums::operator<<;
|
||||
std::cout << "Enum received: " << level << '\n';
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
// Copyright (c) 2017-2026, University of Cincinnati, developed by Henry Schreiner
|
||||
// under NSF AWARD 1414736 and by the respective contributors.
|
||||
// All rights reserved.
|
||||
//
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#include <CLI/CLI.hpp>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
class MyFormatter : public CLI::Formatter {
|
||||
public:
|
||||
MyFormatter() : Formatter() {}
|
||||
std::string make_option_opts(const CLI::Option *) const override { return " OPTION"; }
|
||||
};
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
CLI::App app;
|
||||
app.set_help_all_flag("--help-all", "Show all help");
|
||||
|
||||
auto fmt = std::make_shared<MyFormatter>();
|
||||
fmt->column_width(15);
|
||||
app.formatter(fmt);
|
||||
|
||||
app.add_flag("--flag", "This is a flag");
|
||||
|
||||
auto *sub1 = app.add_subcommand("one", "Description One");
|
||||
sub1->add_flag("--oneflag", "Some flag");
|
||||
auto *sub2 = app.add_subcommand("two", "Description Two");
|
||||
sub2->add_flag("--twoflag", "Some other flag");
|
||||
|
||||
CLI11_PARSE(app, argc, argv);
|
||||
|
||||
std::cout << "This app was meant to show off the formatter, run with -h" << '\n';
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
// Copyright (c) 2017-2026, University of Cincinnati, developed by Henry Schreiner
|
||||
// under NSF AWARD 1414736 and by the respective contributors.
|
||||
// All rights reserved.
|
||||
//
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#include <CLI/CLI.hpp>
|
||||
#include <CLI/Timer.hpp>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
CLI::AutoTimer give_me_a_name("This is a timer");
|
||||
|
||||
CLI::App app("K3Pi goofit fitter");
|
||||
|
||||
std::string file;
|
||||
CLI::Option *opt = app.add_option("-f,--file,file", file, "File name")->required()->group("Important");
|
||||
|
||||
int count{0};
|
||||
CLI::Option *copt = app.add_flag("-c,--count", count, "Counter")->required()->group("Important");
|
||||
|
||||
double value{0.0}; // = 3.14;
|
||||
app.add_option("-d,--double", value, "Some Value")->group("Other");
|
||||
|
||||
try {
|
||||
app.parse(argc, argv);
|
||||
} catch(const CLI::ParseError &e) {
|
||||
return app.exit(e);
|
||||
}
|
||||
|
||||
std::cout << "Working on file: " << file << ", direct count: " << app.count("--file")
|
||||
<< ", opt count: " << opt->count() << '\n';
|
||||
std::cout << "Working on count: " << count << ", direct count: " << app.count("--count")
|
||||
<< ", opt count: " << copt->count() << '\n';
|
||||
std::cout << "Some value: " << value << '\n';
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
// Copyright (c) 2017-2026, University of Cincinnati, developed by Henry Schreiner
|
||||
// under NSF AWARD 1414736 and by the respective contributors.
|
||||
// All rights reserved.
|
||||
//
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#include <CLI/CLI.hpp>
|
||||
#include <string>
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
std::string input_file_name, output_file_name;
|
||||
int level{5}, subopt{0};
|
||||
|
||||
// app caption
|
||||
CLI::App app{"CLI11 help"};
|
||||
// this tests out some of the wide character support, mainly for compilation checking
|
||||
argv = app.ensure_utf8(argv);
|
||||
app.require_subcommand(1);
|
||||
// subcommands options and flags
|
||||
CLI::App *const encode = app.add_subcommand("e", "encode")->ignore_case(); // ignore case
|
||||
encode->add_option("input", input_file_name, "input file")
|
||||
->option_text(" ")
|
||||
->required()
|
||||
->check(CLI::ExistingFile); // file must exist
|
||||
encode->add_option("output", output_file_name, "output file")->option_text(" ")->required(); // required option
|
||||
encode->add_option("-l, --level", level, "encoding level")
|
||||
->option_text("[1..9]")
|
||||
->check(CLI::Range(1, 9))
|
||||
->default_val(5); // limit parameter range
|
||||
encode->add_flag("-R, --remove", "remove input file"); // no parameter option
|
||||
encode->add_flag("-s, --suboption", subopt, "suboption")->option_text(" ");
|
||||
|
||||
CLI::App *const decode = app.add_subcommand("d", "decode")->ignore_case();
|
||||
decode->add_option("input", input_file_name, "input file")->option_text(" ")->required()->check(CLI::ExistingFile);
|
||||
decode->add_option("output", output_file_name, "output file")->option_text(" ")->required();
|
||||
|
||||
// Usage message modification
|
||||
std::string usage_msg = "Usage: " + std::string(argv[0]) + " <command> [options] <input-file> <output-file>";
|
||||
app.usage(usage_msg);
|
||||
// flag to display full help at once
|
||||
app.set_help_flag("");
|
||||
app.set_help_all_flag("-h, --help");
|
||||
|
||||
CLI11_PARSE(app, argc, argv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
$ ./help_usage -h
|
||||
CLI11 help
|
||||
|
||||
OPTIONS:
|
||||
-h, --help
|
||||
|
||||
SUBCOMMANDS:
|
||||
e
|
||||
encode
|
||||
|
||||
POSITIONALS:
|
||||
input input file
|
||||
output output file
|
||||
|
||||
OPTIONS:
|
||||
-l, --level [1..9] encoding level
|
||||
-R, --remove remove input file
|
||||
-s, --suboption suboption
|
||||
|
||||
|
||||
d
|
||||
decode
|
||||
|
||||
POSITIONALS:
|
||||
input input file
|
||||
output output file
|
||||
*/
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user