initial commit

This commit is contained in:
hwinkel
2023-08-05 13:41:10 +02:00
commit c3cc840791
587 changed files with 140234 additions and 0 deletions

42
.drone.yml Normal file
View File

@@ -0,0 +1,42 @@
kind: pipeline
type: kubernetes
name: default-build
steps:
- name: build
image: kmaster.ti.unibw-hamburg.de:30808/debianbullseye
commands:
- mkdir -p build && cd build
- CC=clang-11 CXX=clang++-11 cmake -DCMAKE_BUILD_TYPE=DEBUG ..
- make -j
- make test
- name: CodeChecker
image: kmaster.ti.unibw-hamburg.de:30808/drone-ftewa-codechecker
pull: always
settings:
CODECHECKER_URL: "http://codechecker:8001"
CODECHECKER_PRODUCT: "Kubecontrol"
CODECHECKER_USER:
from_secret: CODECHECKER_USER_SECRET
CODECHECKER_PASS:
from_secret: CODECHECKER_PASS_SECRET
when:
event:
include:
- push
- pull_request
---
kind: secret
name: CODECHECKER_USER_SECRET
get:
path: codechecker-client
name: username
---
kind: secret
name: CODECHECKER_PASS_SECRET
get:
path: codechecker-client
name: password

3
.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
build
.clangd
compile_commands.json

58
CMakeLists.txt Normal file
View File

@@ -0,0 +1,58 @@
cmake_minimum_required (VERSION 3.1 FATAL_ERROR)
project (Ship VERSION 0.1.0 LANGUAGES CXX C)
set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/Modules)
include(defaultOptions)
IF(NOT TARGET Catch2)
add_subdirectory(libs/Catch2 EXCLUDE_FROM_ALL)
include(libs/Catch2/contrib/Catch.cmake)
ENDIF()
IF (NOT TARGET CLI11)
set(CLI11_TESTING OFF CACHE BOOL "disable testing")
add_subdirectory(libs/CLI11 EXCLUDE_FROM_ALL)
ENDIF()
# build with CC=clang-15 CXX=clang++-15 cmake -D CMAKE_BUILD_TYPE=DEBUG ..
add_library(kubecontrol STATIC
libs/kubecontrol/kubecontrol.hpp
src/kubecontrol/kubecontrol.cpp
)
target_link_libraries(kubecontrol
EntityLibrary
loguru
)
target_include_directories(kubecontrol PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>
src)
#
# Everything TEST related
#
option(TEST_KUBECONTROL_LIBRARY "Turn running of application template specific tests off" ON)
IF (${TEST_ENTITIY_LIBRARY})
# add_executable(test_SensorClass tests/test_SensorClass.cpp)
# target_link_libraries(test_SensorClass Catch2::Catch2 EntityLibrary loguru)
# catch_discover_tests(test_SensorClass)
ENDIF()

59
cmake/Jenkinsfile vendored Normal file
View File

@@ -0,0 +1,59 @@
@Library('ftewa-jenkins-library@main') _
@NonCPS
def getPipelineJobNames() {
Hudson.instance.getAllItems(org.jenkinsci.plugins.workflow.job.WorkflowJob)*.fullName
}
@NonCPS
def triggerJobs() {
def jobs = getPipelineJobNames();
for (job in jobs)
{
echo "Trigger ${job}"
if (job.contains("Integration"))
{
build job: job,
parameters: [
string(name: 'REPO_NAME', value: "cmake"),
string(name: 'TAG', value: "main")
],
wait: false
}
}
}
pipeline {
agent {
kubernetes {
//workspaceVolume: dynamicPVC(accessModes: 'ReadWriteOnce', requestsSize: '20G', storageClassName: 'csi-rbd-sc')
yaml libraryResource("files/pod-build.yml")
defaultContainer 'clang-build'
}
} // agent
options {
// Only keep the 1 most recent builds
buildDiscarder(logRotator(numToKeepStr: "1"))
}
stages {
stage("Build")
{
steps {
echo "build"
triggerJobs()
}
} // stage("Build")
}
}

View File

View File

@@ -0,0 +1 @@
get_directory_property(hasParent PARENT_DIRECTORY)

View File

@@ -0,0 +1,28 @@
option(CPPCHECK "Turns cppcheck processing on if executable is found." OFF)
find_program(CPPCHECK_PATH NAMES cppcheck)
# export compile commands to json file to be used by cppcheck
set(CMAKE_EXPORT_COMPILE_COMMANDS 1)
set(CPPCHECK_COMPILE_COMMANDS "${CMAKE_BINARY_DIR}/compile_commands.json")
# output directory for codecheck analysis
set(CPPCHECK_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/cppcheck_results)
if(CPPCHECK AND CPPCHECK_PATH AND NOT CPPCHECK_ADDED)
set(CPPCHECK_ADDED ON)
if (NOT TARGET cppcheck)
add_custom_target(cppcheck
COMMAND rm -rf ${CPPCHECK_OUTPUT_DIRECTORY}\;
mkdir -p ${CPPCHECK_OUTPUT_DIRECTORY}\;
${CPPCHECK_PATH}
--project=${CPPCHECK_COMPILE_COMMANDS}
--plist-output=${CPPCHECK_OUTPUT_DIRECTORY}
--enable=all
--inline-suppr
)
endif()
endif()

View File

@@ -0,0 +1,76 @@
# Try to find the GMP library
# https://gmplib.org/
#
# This module supports requiring a minimum version, e.g. you can do
# find_package(GMP 6.0.0)
# to require version 6.0.0 to newer of GMP.
#
# Once done this will define
#
# GMP_FOUND - system has GMP lib with correct version
# GMP_INCLUDES - the GMP include directory
# GMP_LIBRARIES - the GMP library
# GMP_VERSION - GMP version
#
# Copyright (c) 2016 Jack Poulson, <jack.poulson@gmail.com>
# Redistribution and use is allowed according to the terms of the BSD license.
find_path(GMP_INCLUDES NAMES gmp.h PATHS $ENV{GMPDIR} ${INCLUDE_INSTALL_DIR})
# Set GMP_FIND_VERSION to 5.1.0 if no minimum version is specified
if(NOT GMP_FIND_VERSION)
if(NOT GMP_FIND_VERSION_MAJOR)
set(GMP_FIND_VERSION_MAJOR 5)
endif()
if(NOT GMP_FIND_VERSION_MINOR)
set(GMP_FIND_VERSION_MINOR 1)
endif()
if(NOT GMP_FIND_VERSION_PATCH)
set(GMP_FIND_VERSION_PATCH 0)
endif()
set(GMP_FIND_VERSION
"${GMP_FIND_VERSION_MAJOR}.${GMP_FIND_VERSION_MINOR}.${GMP_FIND_VERSION_PATCH}")
endif()
message("GMP_INCLUDES=${GMP_INCLUDES}")
if(GMP_INCLUDES)
# Since the GMP version macros may be in a file included by gmp.h of the form
# gmp-.*[_]?.*.h (e.g., gmp-x86_64.h), we search each of them.
file(GLOB GMP_HEADERS "${GMP_INCLUDES}/gmp.h" "${GMP_INCLUDES}/gmp-*.h")
foreach(gmp_header_filename ${GMP_HEADERS})
file(READ "${gmp_header_filename}" _gmp_version_header)
string(REGEX MATCH
"define[ \t]+__GNU_MP_VERSION[ \t]+([0-9]+)" _gmp_major_version_match
"${_gmp_version_header}")
if(_gmp_major_version_match)
set(GMP_MAJOR_VERSION "${CMAKE_MATCH_1}")
string(REGEX MATCH "define[ \t]+__GNU_MP_VERSION_MINOR[ \t]+([0-9]+)"
_gmp_minor_version_match "${_gmp_version_header}")
set(GMP_MINOR_VERSION "${CMAKE_MATCH_1}")
string(REGEX MATCH "define[ \t]+__GNU_MP_VERSION_PATCHLEVEL[ \t]+([0-9]+)"
_gmp_patchlevel_version_match "${_gmp_version_header}")
set(GMP_PATCHLEVEL_VERSION "${CMAKE_MATCH_1}")
set(GMP_VERSION
${GMP_MAJOR_VERSION}.${GMP_MINOR_VERSION}.${GMP_PATCHLEVEL_VERSION})
endif()
endforeach()
# Check whether found version exists and exceeds the minimum requirement
if(NOT GMP_VERSION)
set(GMP_VERSION_OK FALSE)
message(STATUS "GMP version was not detected")
elseif(${GMP_VERSION} VERSION_LESS ${GMP_FIND_VERSION})
set(GMP_VERSION_OK FALSE)
message(STATUS "GMP version ${GMP_VERSION} found in ${GMP_INCLUDES}, "
"but at least version ${GMP_FIND_VERSION} is required")
else()
set(GMP_VERSION_OK TRUE)
endif()
endif()
find_library(GMP_LIBRARIES gmp PATHS $ENV{GMPDIR} ${LIB_INSTALL_DIR})
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(GMP DEFAULT_MSG
GMP_INCLUDES GMP_LIBRARIES GMP_VERSION_OK)
mark_as_advanced(GMP_INCLUDES GMP_LIBRARIES)

View File

@@ -0,0 +1,25 @@
# Find Valgrind.
#
# This module defines:
# VALGRIND_INCLUDE_DIR, where to find valgrind/memcheck.h, etc.
# VALGRIND_PROGRAM, the valgrind executable.
# VALGRIND_FOUND, If false, do not try to use valgrind.
#
# If you have valgrind installed in a non-standard place, you can define
# VALGRIND_PREFIX to tell cmake where it is.
#
# NOTE: Copied from the opencog project, where it is distributed under the
# terms of the New BSD License.
message(STATUS "Valgrind Prefix: ${VALGRIND_PREFIX}")
find_path(VALGRIND_INCLUDE_DIR valgrind/memcheck.h
/usr/include /usr/local/include ${VALGRIND_PREFIX}/include)
find_program(VALGRIND_PROGRAM NAMES valgrind PATH
/usr/bin /usr/local/bin ${VALGRIND_PREFIX}/bin)
find_package_handle_standard_args(VALGRIND DEFAULT_MSG
VALGRIND_INCLUDE_DIR
VALGRIND_PROGRAM)
mark_as_advanced(VALGRIND_INCLUDE_DIR VALGRIND_PROGRAM)

View File

@@ -0,0 +1,269 @@
# Written in 2016 by Henrik Steffen Gaßmann <henrik@gassmann.onl>
#
# To the extent possible under law, the author(s) have dedicated all
# copyright and related and neighboring rights to this software to the
# public domain worldwide. This software is distributed without any warranty.
#
# You should have received a copy of the CC0 Public Domain Dedication
# along with this software. If not, see
#
# http://creativecommons.org/publicdomain/zero/1.0/
#
########################################################################
# Tries to find the local libsodium installation.
#
# On Windows the sodium_DIR environment variable is used as a default
# hint which can be overridden by setting the corresponding cmake variable.
#
# Once done the following variables will be defined:
#
# sodium_FOUND
# sodium_INCLUDE_DIR
# sodium_LIBRARY_DEBUG
# sodium_LIBRARY_RELEASE
#
#
# Furthermore an imported "sodium" target is created.
#
if (CMAKE_C_COMPILER_ID STREQUAL "GNU"
OR CMAKE_C_COMPILER_ID STREQUAL "Clang")
set(_GCC_COMPATIBLE 1)
endif()
# static library option
option(sodium_USE_STATIC_LIBS "enable to statically link against sodium")
if(NOT (sodium_USE_STATIC_LIBS EQUAL sodium_USE_STATIC_LIBS_LAST))
unset(sodium_LIBRARY CACHE)
unset(sodium_LIBRARY_DEBUG CACHE)
unset(sodium_LIBRARY_RELEASE CACHE)
unset(sodium_DLL_DEBUG CACHE)
unset(sodium_DLL_RELEASE CACHE)
set(sodium_USE_STATIC_LIBS_LAST ${sodium_USE_STATIC_LIBS} CACHE INTERNAL "internal change tracking variable")
endif()
########################################################################
# UNIX
if (UNIX)
# import pkg-config
find_package(PkgConfig QUIET)
if (PKG_CONFIG_FOUND)
pkg_check_modules(sodium_PKG QUIET libsodium)
endif()
if(sodium_USE_STATIC_LIBS)
set(XPREFIX sodium_PKG_STATIC)
else()
set(XPREFIX sodium_PKG)
endif()
find_path(sodium_INCLUDE_DIR sodium.h
HINTS ${${XPREFIX}_INCLUDE_DIRS}
)
find_library(sodium_LIBRARY_DEBUG NAMES ${${XPREFIX}_LIBRARIES} sodium
HINTS ${${XPREFIX}_LIBRARY_DIRS}
)
find_library(sodium_LIBRARY_RELEASE NAMES ${${XPREFIX}_LIBRARIES} sodium
HINTS ${${XPREFIX}_LIBRARY_DIRS}
)
########################################################################
# Windows
elseif (WIN32)
set(sodium_DIR "$ENV{sodium_DIR}" CACHE FILEPATH "sodium install directory")
mark_as_advanced(sodium_DIR)
find_path(sodium_INCLUDE_DIR sodium.h
HINTS ${sodium_DIR}
PATH_SUFFIXES include
)
if (MSVC)
# detect target architecture
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/arch.c" [=[
#if defined _M_IX86
#error ARCH_VALUE x86_32
#elif defined _M_X64
#error ARCH_VALUE x86_64
#endif
#error ARCH_VALUE unknown
]=])
try_compile(_UNUSED_VAR "${CMAKE_CURRENT_BINARY_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/arch.c"
OUTPUT_VARIABLE _COMPILATION_LOG
)
string(REGEX REPLACE ".*ARCH_VALUE ([a-zA-Z0-9_]+).*" "\\1" _TARGET_ARCH "${_COMPILATION_LOG}")
# construct library path
if (_TARGET_ARCH STREQUAL "x86_32")
string(APPEND _PLATFORM_PATH "Win32")
elseif(_TARGET_ARCH STREQUAL "x86_64")
string(APPEND _PLATFORM_PATH "x64")
else()
message(FATAL_ERROR "the ${_TARGET_ARCH} architecture is not supported by Findsodium.cmake.")
endif()
string(APPEND _PLATFORM_PATH "/$$CONFIG$$")
if (MSVC_VERSION LESS 1900)
math(EXPR _VS_VERSION "${MSVC_VERSION} / 10 - 60")
else()
math(EXPR _VS_VERSION "${MSVC_VERSION} / 10 - 50")
endif()
string(APPEND _PLATFORM_PATH "/v${_VS_VERSION}")
if (sodium_USE_STATIC_LIBS)
string(APPEND _PLATFORM_PATH "/static")
else()
string(APPEND _PLATFORM_PATH "/dynamic")
endif()
string(REPLACE "$$CONFIG$$" "Debug" _DEBUG_PATH_SUFFIX "${_PLATFORM_PATH}")
string(REPLACE "$$CONFIG$$" "Release" _RELEASE_PATH_SUFFIX "${_PLATFORM_PATH}")
find_library(sodium_LIBRARY_DEBUG libsodium.lib
HINTS ${sodium_DIR}
PATH_SUFFIXES ${_DEBUG_PATH_SUFFIX}
)
find_library(sodium_LIBRARY_RELEASE libsodium.lib
HINTS ${sodium_DIR}
PATH_SUFFIXES ${_RELEASE_PATH_SUFFIX}
)
if (NOT sodium_USE_STATIC_LIBS)
set(CMAKE_FIND_LIBRARY_SUFFIXES_BCK ${CMAKE_FIND_LIBRARY_SUFFIXES})
set(CMAKE_FIND_LIBRARY_SUFFIXES ".dll")
find_library(sodium_DLL_DEBUG libsodium
HINTS ${sodium_DIR}
PATH_SUFFIXES ${_DEBUG_PATH_SUFFIX}
)
find_library(sodium_DLL_RELEASE libsodium
HINTS ${sodium_DIR}
PATH_SUFFIXES ${_RELEASE_PATH_SUFFIX}
)
set(CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES_BCK})
endif()
elseif(_GCC_COMPATIBLE)
if (sodium_USE_STATIC_LIBS)
find_library(sodium_LIBRARY_DEBUG libsodium.a
HINTS ${sodium_DIR}
PATH_SUFFIXES lib
)
find_library(sodium_LIBRARY_RELEASE libsodium.a
HINTS ${sodium_DIR}
PATH_SUFFIXES lib
)
else()
find_library(sodium_LIBRARY_DEBUG libsodium.dll.a
HINTS ${sodium_DIR}
PATH_SUFFIXES lib
)
find_library(sodium_LIBRARY_RELEASE libsodium.dll.a
HINTS ${sodium_DIR}
PATH_SUFFIXES lib
)
file(GLOB _DLL
LIST_DIRECTORIES false
RELATIVE "${sodium_DIR}/bin"
"${sodium_DIR}/bin/libsodium*.dll"
)
find_library(sodium_DLL_DEBUG ${_DLL} libsodium
HINTS ${sodium_DIR}
PATH_SUFFIXES bin
)
find_library(sodium_DLL_RELEASE ${_DLL} libsodium
HINTS ${sodium_DIR}
PATH_SUFFIXES bin
)
endif()
else()
message(FATAL_ERROR "this platform is not supported by FindSodium.cmake")
endif()
########################################################################
# unsupported
else()
message(FATAL_ERROR "this platform is not supported by FindSodium.cmake")
endif()
########################################################################
# common stuff
# extract sodium version
if (sodium_INCLUDE_DIR)
set(_VERSION_HEADER "${_INCLUDE_DIR}/sodium/version.h")
if (EXISTS _VERSION_HEADER)
file(READ "${_VERSION_HEADER}" _VERSION_HEADER_CONTENT)
string(REGEX REPLACE ".*#[ \t]*define[ \t]*SODIUM_VERSION_STRING[ \t]*\"([^\n]*)\".*" "\\1"
sodium_VERSION "${_VERSION_HEADER_CONTENT}")
set(sodium_VERSION "${sodium_VERSION}" PARENT_SCOPE)
endif()
endif()
# communicate results
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(sodium
REQUIRED_VARS
sodium_LIBRARY_RELEASE
sodium_LIBRARY_DEBUG
sodium_INCLUDE_DIR
VERSION_VAR
sodium_VERSION
)
# mark file paths as advanced
mark_as_advanced(sodium_INCLUDE_DIR)
mark_as_advanced(sodium_LIBRARY_DEBUG)
mark_as_advanced(sodium_LIBRARY_RELEASE)
if (WIN32)
mark_as_advanced(sodium_DLL_DEBUG)
mark_as_advanced(sodium_DLL_RELEASE)
endif()
# create imported target
if(sodium_USE_STATIC_LIBS)
set(_LIB_TYPE STATIC)
else()
set(_LIB_TYPE SHARED)
endif()
add_library(sodium ${_LIB_TYPE} IMPORTED)
set_target_properties(sodium PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${sodium_INCLUDE_DIR}"
IMPORTED_LINK_INTERFACE_LANGUAGES "C"
)
if (sodium_USE_STATIC_LIBS)
set_target_properties(sodium PROPERTIES
INTERFACE_COMPILE_DEFINITIONS "SODIUM_STATIC"
IMPORTED_LOCATION "${sodium_LIBRARY_RELEASE}"
IMPORTED_LOCATION_DEBUG "${sodium_LIBRARY_DEBUG}"
)
else()
if (UNIX)
set_target_properties(sodium PROPERTIES
IMPORTED_LOCATION "${sodium_LIBRARY_RELEASE}"
IMPORTED_LOCATION_DEBUG "${sodium_LIBRARY_DEBUG}"
)
elseif (WIN32)
set_target_properties(sodium PROPERTIES
IMPORTED_IMPLIB "${sodium_LIBRARY_RELEASE}"
IMPORTED_IMPLIB_DEBUG "${sodium_LIBRARY_DEBUG}"
)
if (NOT (sodium_DLL_DEBUG MATCHES ".*-NOTFOUND"))
set_target_properties(sodium PROPERTIES
IMPORTED_LOCATION_DEBUG "${sodium_DLL_DEBUG}"
)
endif()
if (NOT (sodium_DLL_RELEASE MATCHES ".*-NOTFOUND"))
set_target_properties(sodium PROPERTIES
IMPORTED_LOCATION_RELWITHDEBINFO "${sodium_DLL_RELEASE}"
IMPORTED_LOCATION_MINSIZEREL "${sodium_DLL_RELEASE}"
IMPORTED_LOCATION_RELEASE "${sodium_DLL_RELEASE}"
)
endif()
endif()
endif()

View File

@@ -0,0 +1,47 @@
INCLUDE(CheckParent)
# search for CCCC binary
FIND_PROGRAM(CCCC cccc )
#
# check if the GENERATE_CCCC function has already been defined
#
get_property(_GENERATE_CCCC GLOBAL PROPERTY _GENERATE_CCCC)
IF (NOT _GENERATE_CCCC)
# set that we have defined GENERATE_CCCC
set_property(GLOBAL PROPERTY _GENERATE_CCCC "YES")
FUNCTION(GENERATE_CCCC)
IF(CCCC)
CMAKE_PARSE_ARGUMENTS(ARG "" "" "TARGETS" ${ARGN})
get_property(_ccccfiles GLOBAL PROPERTY _ccccfiles)
foreach(_target ${ARG_TARGETS})
get_target_property(_sources ${_target} SOURCES)
get_target_property(_source_dir ${_target} SOURCE_DIR)
foreach(_source ${_sources})
set(_fullsource "${_source_dir}/${_source}")
list(APPEND _ccccfiles "${_fullsource}")
endforeach()
endforeach()
set_property(GLOBAL PROPERTY _ccccfiles ${_ccccfiles})
ENDIF()
ENDFUNCTION()
FUNCTION(RESET_CCCC)
set_property(GLOBAL PROPERTY _ccccfiles "")
ENDFUNCTION()
FUNCTION(GENERATE_CCCC_TARGET)
IF (NOT hasParent AND CCCC)
get_property(_targetccccfiles GLOBAL PROPERTY _ccccfiles)
ADD_CUSTOM_TARGET(cccc
COMMAND ${CCCC} --outdir=cccc ${_targetccccfiles}
COMMENT "Generating cccc result")
ENDIF()
ENDFUNCTION()
ENDIF()

View File

@@ -0,0 +1,74 @@
INCLUDE(CheckParent)
find_program(CPPCHECK NAMES cppcheck)
#
# check if the GENERATE_CPPCHECK function has already been defined
#
get_property(_GENERATE_CPPCHECK GLOBAL PROPERTY _GENERATE_CPPCHECK)
IF (NOT _GENERATE_CPPCHECK)
# set that we have defined GENERATE_CCCC
set_property(GLOBAL PROPERTY _GENERATE_CPPCHECK "YES")
FUNCTION(GENERATE_CPPCHECK)
IF(NOT TARGET cppcheck)
IF(CPPCHECK)
CMAKE_PARSE_ARGUMENTS(ARG "" "" "TARGETS" ${ARGN})
get_property(_cppcheckfiles GLOBAL PROPERTY _cppcheckfiles)
get_property(_cppcheckincludedirs GLOBAL PROPERTY _cppcheckincludedirs)
foreach(_target ${ARG_TARGETS})
get_target_property(_sources ${_target} SOURCES)
get_target_property(_source_dir ${_target} SOURCE_DIR)
get_target_property(_include_dir ${_target} INCLUDE_DIRECTORIES)
string(REPLACE "$<" ";" _include_dirs ${_include_dir})
foreach(_dir ${_include_dirs})
list(APPEND _cppcheckincludedirs -I${_include_dir})
endforeach()
foreach(_source ${_sources})
set(_fullsource "${_source_dir}/${_source}")
list(APPEND _cppcheckfiles ${_fullsource})
endforeach()
endforeach()
set_property(GLOBAL PROPERTY _cppcheckfiles ${_cppcheckfiles})
set_property(GLOBAL PROPERTY _cppcheckincludedirs ${_cppcheckincludedirs})
ENDIF()
ENDIF()
ENDFUNCTION()
FUNCTION(RESET_CPPCHECK)
set_property(GLOBAL PROPERTY _cppcheckfiles "")
set_property(GLOBAL PROPERTY _cppcheckincludedirs "")
ENDFUNCTION()
FUNCTION(GENERATE_CPPCHECK_TARGET)
IF ( NOT hasParent AND CPPCHECK)
message("generate cppcheck target")
get_property(_targetcppcheckfiles GLOBAL PROPERTY _cppcheckfiles)
get_property(_targetcppcheckincludedirs GLOBAL PROPERTY _cppcheckincludedirs)
add_custom_target(cppcheck
COMMAND
${CPPCHECK}
--xml
--xml-version=2
--enable=all
--inconclusive
--force
--inline-suppr
${_targetcppcheckincludedirs}
${_targetcppcheckfiles}
2> cppcheck.xml
WORKING_DIRECTORY
${CMAKE_CURRENT_BINARY_DIR}
COMMENT
"cppcheck: Running cppcheck on target ${_targetname}..."
VERBATIM)
ENDIF()
ENDFUNCTION()
ENDIF()

View File

@@ -0,0 +1,23 @@
INCLUDE(CheckParent)
IF(NOT hasParent)
find_package(Doxygen)
IF (DOXYGEN_FOUND)
# set input and output files
set(DOXYGEN_IN ${CMAKE_CURRENT_SOURCE_DIR}/docs/Doxyfile.in)
set(DOXYGEN_OUT ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile)
# request to configure the file
configure_file(${DOXYGEN_IN} ${DOXYGEN_OUT} @ONLY)
# note the option ALL which allows to build the docs together with the application
add_custom_target( doxygen
COMMAND ${DOXYGEN_EXECUTABLE} ${DOXYGEN_OUT}
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMENT "Generating API documentation with Doxygen"
VERBATIM )
ENDIF()
ENDIF()

View File

@@ -0,0 +1,17 @@
if (GIT_FOUND)
execute_process(
COMMAND git rev-parse --abbrev-ref HEAD
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE GIT_BRANCH
OUTPUT_STRIP_TRAILING_WHITESPACE
)
# Get the latest abbreviated commit hash of the working branch
execute_process(
COMMAND git log -1 --format=%h
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE GIT_COMMIT_HASH
OUTPUT_STRIP_TRAILING_WHITESPACE
)
endif()

View File

@@ -0,0 +1,22 @@
get_property(_ADD_MY_TEST GLOBAL PROPERTY _ADD_MY_TEST)
IF (NOT _ADD_MY_TEST)
# set that we have defined GENERATE_CCCC
set_property(GLOBAL PROPERTY _ADD_MY_TEST "YES")
FUNCTION(ADD_MY_TEST)
CMAKE_PARSE_ARGUMENTS(ARG "" "TEST" "SOURCES;LIBS" ${ARGN})
get_property(_mytests GLOBAL PROPERTY _mytests)
list(APPEND _mytests "${ARG_TEST}")
add_executable(${ARG_TEST} ${ARG_SOURCES})
target_link_libraries(${ARG_TEST} ${ARG_LIBS})
add_test(${ARG_TEST} ${ARG_TEST})
set_property(GLOBAL PROPERTY _mytests ${_mytests})
ENDFUNCTION()
ENDIF()

View File

@@ -0,0 +1,66 @@
#
# Copyright (C) 2018 by George Cave - gcave@stablecoder.ca
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy of
# the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under
# the License.
# Set the compiler standard to C++11
macro(cxx_11)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
if(MSVC_VERSION GREATER_EQUAL "1900" AND CMAKE_VERSION LESS 3.10)
include(CheckCXXCompilerFlag)
check_cxx_compiler_flag("/std:c++11" _cpp_latest_flag_supported)
if(_cpp_latest_flag_supported)
add_compile_options("/std:c++11")
endif()
endif()
endmacro()
# Set the compiler standard to C++14
macro(cxx_14)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
if(MSVC_VERSION GREATER_EQUAL "1900" AND CMAKE_VERSION LESS 3.10)
include(CheckCXXCompilerFlag)
check_cxx_compiler_flag("/std:c++14" _cpp_latest_flag_supported)
if(_cpp_latest_flag_supported)
add_compile_options("/std:c++14")
endif()
endif()
endmacro()
# Set the compiler standard to C++17
macro(cxx_17)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
if(MSVC_VERSION GREATER_EQUAL "1900" AND CMAKE_VERSION LESS 3.10)
include(CheckCXXCompilerFlag)
check_cxx_compiler_flag("/std:c++17" _cpp_latest_flag_supported)
if(_cpp_latest_flag_supported)
add_compile_options("/std:c++17")
endif()
endif()
endmacro()
# Set the compiler standard to C++20
macro(cxx_20)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
endmacro()

View File

@@ -0,0 +1,588 @@
#
# Copyright (C) 2018 by George Cave - gcave@stablecoder.ca
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy of
# the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under
# the License.
# USAGE: To enable any code coverage instrumentation/targets, the single CMake
# option of `CODE_COVERAGE` needs to be set to 'ON', either by GUI, ccmake, or
# on the command line.
#
# From this point, there are two primary methods for adding instrumentation to
# targets: 1 - A blanket instrumentation by calling `add_code_coverage()`, where
# all targets in that directory and all subdirectories are automatically
# instrumented. 2 - Per-target instrumentation by calling
# `target_code_coverage(<TARGET_NAME>)`, where the target is given and thus only
# that target is instrumented. This applies to both libraries and executables.
#
# To add coverage targets, such as calling `make ccov` to generate the actual
# coverage information for perusal or consumption, call
# `target_code_coverage(<TARGET_NAME>)` on an *executable* target.
#
# Example 1: All targets instrumented
#
# In this case, the coverage information reported will will be that of the
# `theLib` library target and `theExe` executable.
#
# 1a: Via global command
#
# ~~~
# add_code_coverage() # Adds instrumentation to all targets
#
# add_library(theLib lib.cpp)
#
# add_executable(theExe main.cpp)
# target_link_libraries(theExe PRIVATE theLib)
# target_code_coverage(theExe) # As an executable target, adds the 'ccov-theExe' target (instrumentation already added via global anyways) for generating code coverage reports.
# ~~~
#
# 1b: Via target commands
#
# ~~~
# add_library(theLib lib.cpp)
# target_code_coverage(theLib) # As a library target, adds coverage instrumentation but no targets.
#
# add_executable(theExe main.cpp)
# target_link_libraries(theExe PRIVATE theLib)
# target_code_coverage(theExe) # As an executable target, adds the 'ccov-theExe' target and instrumentation for generating code coverage reports.
# ~~~
#
# Example 2: Target instrumented, but with regex pattern of files to be excluded
# from report
#
# ~~~
# add_executable(theExe main.cpp non_covered.cpp)
# target_code_coverage(theExe EXCLUDE non_covered.cpp test/*) # As an executable target, the reports will exclude the non-covered.cpp file, and any files in a test/ folder.
# ~~~
#
# Example 3: Target added to the 'ccov' and 'ccov-all' targets
#
# ~~~
# add_code_coverage_all_targets(EXCLUDE test/*) # Adds the 'ccov-all' target set and sets it to exclude all files in test/ folders.
#
# add_executable(theExe main.cpp non_covered.cpp)
# target_code_coverage(theExe AUTO ALL EXCLUDE non_covered.cpp test/*) # As an executable target, adds to the 'ccov' and ccov-all' targets, and the reports will exclude the non-covered.cpp file, and any files in a test/ folder.
# ~~~
# Options
option(
CODE_COVERAGE
"Builds targets with code coverage instrumentation. (Requires GCC or Clang)"
OFF)
# Programs
find_program(LLVM_COV_PATH NAMES llvm-cov llvm-cov-8 llvm-cov-7 llvm-cov-6)
find_program(LLVM_PROFDATA_PATH NAMES llvm-profdata llvm-profdata-8 llvm-profdata-7 llvm-profdata-6)
find_program(LCOV_PATH lcov)
find_program(GENHTML_PATH genhtml)
# Variables
set(CMAKE_COVERAGE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/ccov)
# Common initialization/checks
if(CODE_COVERAGE AND NOT CODE_COVERAGE_ADDED)
set(CODE_COVERAGE_ADDED ON)
# Common Targets
add_custom_target(ccov-preprocessing
COMMAND ${CMAKE_COMMAND}
-E
make_directory
${CMAKE_COVERAGE_OUTPUT_DIRECTORY}
DEPENDS ccov-clean)
if("${CMAKE_C_COMPILER_ID}" MATCHES "(Apple)?[Cc]lang"
OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "(Apple)?[Cc]lang")
# Messages
message(STATUS "Building with llvm Code Coverage Tools")
if(NOT LLVM_COV_PATH)
message(FATAL_ERROR "llvm-cov not found! Aborting.")
else()
# Version number checking for 'EXCLUDE' compatability
execute_process(COMMAND ${LLVM_COV_PATH} --version
OUTPUT_VARIABLE LLVM_COV_VERSION_CALL_OUTPUT)
string(REGEX MATCH
"[0-9]+\\.[0-9]+\\.[0-9]+"
LLVM_COV_VERSION
${LLVM_COV_VERSION_CALL_OUTPUT})
if(LLVM_COV_VERSION VERSION_LESS "7.0.0")
message(
WARNING
"target_code_coverage()/add_code_coverage_all_targets() 'EXCLUDE' option only available on llvm-cov >= 7.0.0"
)
endif()
endif()
# Targets
add_custom_target(ccov-clean
COMMAND rm -f
${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list
COMMAND rm -f
${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/profraw.list)
# Used to get the shared object file list before doing the main all-processing
add_custom_target(ccov-libs
COMMAND ;
COMMENT "libs ready for coverage report.")
elseif(CMAKE_COMPILER_IS_GNUCXX)
# Messages
message(STATUS "Building with lcov Code Coverage Tools")
if(CMAKE_BUILD_TYPE)
string(TOUPPER ${CMAKE_BUILD_TYPE} upper_build_type)
if(NOT ${upper_build_type} STREQUAL "DEBUG")
message(
WARNING
"Code coverage results with an optimized (non-Debug) build may be misleading"
)
endif()
else()
message(
WARNING
"Code coverage results with an optimized (non-Debug) build may be misleading"
)
endif()
if(NOT LCOV_PATH)
message(FATAL_ERROR "lcov not found! Aborting...")
endif()
if(NOT GENHTML_PATH)
message(FATAL_ERROR "genhtml not found! Aborting...")
endif()
# Targets
add_custom_target(ccov-clean
COMMAND ${LCOV_PATH}
--directory
${CMAKE_BINARY_DIR}
--zerocounters)
else()
message(FATAL_ERROR "Code coverage requires Clang or GCC. Aborting.")
endif()
endif()
# Adds code coverage instrumentation to a library, or instrumentation/targets
# for an executable target.
# ~~~
# EXECUTABLE ADDED TARGETS:
# GCOV/LCOV:
# ccov : Generates HTML code coverage report for every target added with 'AUTO' parameter.
# ccov-${TARGET_NAME} : Generates HTML code coverage report for the associated named target.
# ccov-all : Generates HTML code coverage report, merging every target added with 'ALL' parameter into a single detailed report.
#
# LLVM-COV:
# ccov : Generates HTML code coverage report for every target added with 'AUTO' parameter.
# ccov-report : Generates HTML code coverage report for every target added with 'AUTO' parameter.
# ccov-${TARGET_NAME} : Generates HTML code coverage report.
# ccov-report-${TARGET_NAME} : Prints to command line summary per-file coverage information.
# ccov-show-${TARGET_NAME} : Prints to command line detailed per-line coverage information.
# ccov-all : Generates HTML code coverage report, merging every target added with 'ALL' parameter into a single detailed report.
# ccov-all-report : Prints summary per-file coverage information for every target added with ALL' parameter to the command line.
#
# Required:
# TARGET_NAME - Name of the target to generate code coverage for.
# Optional:
# AUTO - Adds the target to the 'ccov' target so that it can be run in a batch with others easily. Effective on executable targets.
# ALL - Adds the target to the 'ccov-all' and 'ccov-all-report' targets, which merge several executable targets coverage data to a single report. Effective on executable targets.
# EXTERNAL - For GCC's lcov, allows the profiling of 'external' files from the processing directory
# EXCLUDE <REGEX_PATTERNS> - Excludes files of the patterns provided from coverage. **These do not copy to the 'all' targets.**
# OBJECTS <TARGETS> - For executables ONLY, if the provided targets are shared libraries, adds coverage information to the output
# ~~~
function(target_code_coverage TARGET_NAME)
# Argument parsing
set(options AUTO ALL EXTERNAL)
set(multi_value_keywords EXCLUDE OBJECTS)
cmake_parse_arguments(target_code_coverage
"${options}"
""
"${multi_value_keywords}"
${ARGN})
if(CODE_COVERAGE)
# Add code coverage instrumentation to the target's linker command
if("${CMAKE_C_COMPILER_ID}" MATCHES "(Apple)?[Cc]lang"
OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "(Apple)?[Cc]lang")
target_compile_options(
${TARGET_NAME}
PRIVATE -fprofile-instr-generate -fcoverage-mapping)
set_property(TARGET ${TARGET_NAME}
APPEND_STRING
PROPERTY LINK_FLAGS "-fprofile-instr-generate ")
set_property(TARGET ${TARGET_NAME}
APPEND_STRING
PROPERTY LINK_FLAGS "-fcoverage-mapping ")
elseif(CMAKE_COMPILER_IS_GNUCXX)
target_compile_options(${TARGET_NAME}
PRIVATE -fprofile-arcs -ftest-coverage)
target_link_libraries(${TARGET_NAME} PRIVATE gcov)
endif()
# Targets
get_target_property(target_type ${TARGET_NAME} TYPE)
# Add shared library to processing for 'all' targets
if(target_type STREQUAL "SHARED_LIBRARY" AND target_code_coverage_ALL)
if("${CMAKE_C_COMPILER_ID}" MATCHES "(Apple)?[Cc]lang"
OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "(Apple)?[Cc]lang")
add_custom_target(
ccov-run-${TARGET_NAME}
COMMAND echo
"-object=$<TARGET_FILE:${TARGET_NAME}>"
>>
${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list
DEPENDS ccov-preprocessing ${TARGET_NAME})
if(NOT TARGET ccov-libs)
message(
FATAL_ERROR
"Calling target_code_coverage with 'ALL' must be after a call to 'add_code_coverage_all_targets'."
)
endif()
add_dependencies(ccov-libs ccov-run-${TARGET_NAME})
endif()
endif()
# For executables add targets to run and produce output
if(target_type STREQUAL "EXECUTABLE")
if("${CMAKE_C_COMPILER_ID}" MATCHES "(Apple)?[Cc]lang"
OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "(Apple)?[Cc]lang")
# If there are shared objects to also work with, generate the string to add them here
foreach(SO_TARGET ${target_code_coverage_OBJECTS})
# Check to see if the target is a shared object
if(TARGET ${SO_TARGET})
get_target_property(SO_TARGET_TYPE ${SO_TARGET} TYPE)
if(${SO_TARGET_TYPE} STREQUAL "SHARED_LIBRARY")
set(SO_OBJECTS ${SO_OBJECTS} -object=$<TARGET_FILE:${SO_TARGET}>)
endif()
endif()
endforeach()
# Run the executable, generating raw profile data
add_custom_target(
ccov-run-${TARGET_NAME}
COMMAND LLVM_PROFILE_FILE=${TARGET_NAME}.profraw
$<TARGET_FILE:${TARGET_NAME}>
COMMAND echo
"-object=$<TARGET_FILE:${TARGET_NAME}>"
>>
${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list
COMMAND echo
"${CMAKE_CURRENT_BINARY_DIR}/${TARGET_NAME}.profraw "
>>
${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/profraw.list
DEPENDS ccov-preprocessing ccov-libs ${TARGET_NAME})
# Merge the generated profile data so llvm-cov can process it
add_custom_target(ccov-processing-${TARGET_NAME}
COMMAND ${LLVM_PROFDATA_PATH}
merge
-sparse
${TARGET_NAME}.profraw
-o
${TARGET_NAME}.profdata
DEPENDS ccov-run-${TARGET_NAME})
# Ignore regex only works on LLVM >= 7
if(LLVM_COV_VERSION VERSION_GREATER_EQUAL "7.0.0")
foreach(EXCLUDE_ITEM ${target_code_coverage_EXCLUDE})
set(EXCLUDE_REGEX ${EXCLUDE_REGEX}
-ignore-filename-regex='${EXCLUDE_ITEM}')
endforeach()
endif()
# Print out details of the coverage information to the command line
add_custom_target(ccov-show-${TARGET_NAME}
COMMAND ${LLVM_COV_PATH}
show
$<TARGET_FILE:${TARGET_NAME}>
${SO_OBJECTS}
-instr-profile=${TARGET_NAME}.profdata
-show-line-counts-or-regions
${EXCLUDE_REGEX}
DEPENDS ccov-processing-${TARGET_NAME})
# Print out a summary of the coverage information to the command line
add_custom_target(ccov-report-${TARGET_NAME}
COMMAND ${LLVM_COV_PATH}
report
$<TARGET_FILE:${TARGET_NAME}>
${SO_OBJECTS}
-instr-profile=${TARGET_NAME}.profdata
${EXCLUDE_REGEX}
DEPENDS ccov-processing-${TARGET_NAME})
# Generates HTML output of the coverage information for perusal
add_custom_target(
ccov-${TARGET_NAME}
COMMAND ${LLVM_COV_PATH}
show
$<TARGET_FILE:${TARGET_NAME}>
${SO_OBJECTS}
-instr-profile=${TARGET_NAME}.profdata
-show-line-counts-or-regions
-output-dir=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/${TARGET_NAME}
-format="html"
${EXCLUDE_REGEX}
DEPENDS ccov-processing-${TARGET_NAME})
elseif(CMAKE_COMPILER_IS_GNUCXX)
set(COVERAGE_INFO
"${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/${TARGET_NAME}.info")
# Run the executable, generating coverage information
add_custom_target(ccov-run-${TARGET_NAME}
COMMAND $<TARGET_FILE:${TARGET_NAME}>
DEPENDS ccov-preprocessing ${TARGET_NAME})
# Generate exclusion string for use
foreach(EXCLUDE_ITEM ${target_code_coverage_EXCLUDE})
set(EXCLUDE_REGEX
${EXCLUDE_REGEX}
--remove
${COVERAGE_INFO}
'${EXCLUDE_ITEM}')
endforeach()
if(EXCLUDE_REGEX)
set(EXCLUDE_COMMAND
${LCOV_PATH}
${EXCLUDE_REGEX}
--output-file
${COVERAGE_INFO})
else()
set(EXCLUDE_COMMAND ;)
endif()
if(NOT ${target_code_coverage_EXTERNAL})
set(EXTERNAL_OPTION --no-external)
endif()
# Generates HTML output of the coverage information for perusal
add_custom_target(
ccov-${TARGET_NAME}
COMMAND ${CMAKE_COMMAND}
-E
remove
${COVERAGE_INFO}
COMMAND ${LCOV_PATH}
--directory
${CMAKE_BINARY_DIR}
--zerocounters
COMMAND $<TARGET_FILE:${TARGET_NAME}>
COMMAND ${LCOV_PATH}
--directory
${CMAKE_BINARY_DIR}
--base-directory
${CMAKE_SOURCE_DIR}
--capture
${EXTERNAL_OPTION}
--output-file
${COVERAGE_INFO}
COMMAND ${EXCLUDE_COMMAND}
COMMAND ${GENHTML_PATH}
-o
${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/${TARGET_NAME}
${COVERAGE_INFO}
DEPENDS ccov-preprocessing ${TARGET_NAME})
endif()
add_custom_command(
TARGET ccov-${TARGET_NAME} POST_BUILD
COMMAND ;
COMMENT
"Open ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/${TARGET_NAME}/index.html in your browser to view the coverage report."
)
# AUTO
if(target_code_coverage_AUTO)
if(NOT TARGET ccov)
add_custom_target(ccov)
endif()
add_dependencies(ccov ccov-${TARGET_NAME})
if(NOT CMAKE_COMPILER_IS_GNUCXX)
if(NOT TARGET ccov-report)
add_custom_target(ccov-report)
endif()
add_dependencies(ccov-report ccov-report-${TARGET_NAME})
endif()
endif()
# ALL
if(target_code_coverage_ALL)
if(NOT TARGET ccov-all-processing)
message(
FATAL_ERROR
"Calling target_code_coverage with 'ALL' must be after a call to 'add_code_coverage_all_targets'."
)
endif()
add_dependencies(ccov-all-processing ccov-run-${TARGET_NAME})
endif()
endif()
endif()
endfunction()
# Adds code coverage instrumentation to all targets in the current directory and
# any subdirectories. To add coverage instrumentation to only specific targets,
# use `target_code_coverage`.
function(add_code_coverage)
if("${CMAKE_C_COMPILER_ID}" MATCHES "(Apple)?[Cc]lang"
OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "(Apple)?[Cc]lang")
add_compile_options(-fprofile-instr-generate -fcoverage-mapping)
add_link_options(-fprofile-instr-generate -fcoverage-mapping)
elseif(CMAKE_COMPILER_IS_GNUCXX)
add_compile_options(-fprofile-arcs -ftest-coverage)
link_libraries(gcov)
endif()
endfunction()
# Adds the 'ccov-all' type targets that calls all targets added via
# `target_code_coverage` with the `ALL` parameter, but merges all the coverage
# data from them into a single large report instead of the numerous smaller
# reports.
# ~~~
# Optional:
# EXCLUDE <REGEX_PATTERNS> - Excludes files of the regex patterns provided from coverage.
# ~~~
function(add_code_coverage_all_targets)
# Argument parsing
set(multi_value_keywords EXCLUDE)
cmake_parse_arguments(add_code_coverage_all_targets
""
""
"${multi_value_keywords}"
${ARGN})
if(CODE_COVERAGE)
if("${CMAKE_C_COMPILER_ID}" MATCHES "(Apple)?[Cc]lang"
OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "(Apple)?[Cc]lang")
# Merge the profile data for all of the run executables
add_custom_target(
ccov-all-processing
COMMAND ${LLVM_PROFDATA_PATH}
merge
-o
${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.profdata
-sparse
`cat
${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/profraw.list`)
# Regex exclude only available for LLVM >= 7
if(LLVM_COV_VERSION VERSION_GREATER_EQUAL "7.0.0")
foreach(EXCLUDE_ITEM ${add_code_coverage_all_targets_EXCLUDE})
set(EXCLUDE_REGEX ${EXCLUDE_REGEX}
-ignore-filename-regex='${EXCLUDE_ITEM}')
endforeach()
endif()
# Print summary of the code coverage information to the command line
add_custom_target(
ccov-all-report
COMMAND
${LLVM_COV_PATH}
report
`cat
${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list`
-instr-profile=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.profdata
${EXCLUDE_REGEX}
DEPENDS ccov-all-processing)
# Export coverage information so continuous integration tools (e.g. Jenkins) can consume it
add_custom_target(
ccov-all-export
COMMAND
${LLVM_COV_PATH}
export
`cat
${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list`
-instr-profile=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.profdata
-format="text"
${EXCLUDE_REGEX} > ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/coverage.json
DEPENDS ccov-all-processing)
# Generate HTML output of all added targets for perusal
add_custom_target(
ccov-all
COMMAND
${LLVM_COV_PATH}
show
`cat
${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/binaries.list`
-instr-profile=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.profdata
-show-line-counts-or-regions
-output-dir=${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged
-format="html"
${EXCLUDE_REGEX}
DEPENDS ccov-all-processing)
elseif(CMAKE_COMPILER_IS_GNUCXX)
set(COVERAGE_INFO "${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged.info")
# Nothing required for gcov
add_custom_target(ccov-all-processing COMMAND ;)
# Exclusion regex string creation
foreach(EXCLUDE_ITEM ${add_code_coverage_all_targets_EXCLUDE})
set(EXCLUDE_REGEX
${EXCLUDE_REGEX}
--remove
${COVERAGE_INFO}
'${EXCLUDE_ITEM}')
endforeach()
if(EXCLUDE_REGEX)
set(EXCLUDE_COMMAND
${LCOV_PATH}
${EXCLUDE_REGEX}
--output-file
${COVERAGE_INFO})
else()
set(EXCLUDE_COMMAND ;)
endif()
# Generates HTML output of all targets for perusal
add_custom_target(ccov-all
COMMAND ${LCOV_PATH}
--directory
${CMAKE_BINARY_DIR}
--capture
--output-file
${COVERAGE_INFO}
COMMAND ${EXCLUDE_COMMAND}
COMMAND ${GENHTML_PATH}
-o
${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged
${COVERAGE_INFO}
COMMAND ${CMAKE_COMMAND}
-E
remove
${COVERAGE_INFO}
DEPENDS ccov-all-processing)
endif()
add_custom_command(
TARGET ccov-all POST_BUILD
COMMAND ;
COMMENT
"Open ${CMAKE_COVERAGE_OUTPUT_DIRECTORY}/all-merged/index.html in your browser to view the coverage report."
)
endif()
endfunction()

View File

@@ -0,0 +1,83 @@
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
#
option(CODECHECKER "Turns on codecheck processing if it is found." OFF)
option(CODECHECKER_STORE "Store results on central codechecker server" OFF)
option(CODECHECKER_RUN "Name of the codechecker run" "run-1")
option(CODECHECKER_BRANCH "Name of the branch codecheker is run for" "unknown")
option(CODECHECKER_URL "URL and product link to codechecker server" "http://localhost:8001/Default")
option(CODECHECK_TRIM_PATH "beginning of the path to be removed when storing" "/tmp")
find_program(CODECHECKER_PATH
NAME CodeChecker
PATHS ~/bin/
/usr/bin/
/usr/local/bin
)
if (CODECHECKER_PATH )
message(STATUS "CodeChecker found")
else()
message(STATUS "CodeChecker not found")
endif()
# export compile commands to json file to be used by atom c++ ide (clangd)
set(CMAKE_EXPORT_COMPILE_COMMANDS 1)
set(CODECHECKER_COMPILE_COMMANDS "${CMAKE_BINARY_DIR}/compile_commands.json")
# check if a skip file exists
if(EXISTS ${CMAKE_SOURCE_DIR}/codecheck.skip)
set(CODECHECKER_SKIP "-i${CMAKE_SOURCE_DIR}/codecheck.skip")
endif()
# output directory for codecheck analysis
set(CODECHECKER_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/codechecker_results)
# html output directory
set(CODECHECKER_HTML_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/codechecker_html)
# Common initialization/checks
if(CODECHECKER AND CODECHECKER_PATH AND NOT CODECHECKER_ADDED)
set(CODECHECKER_ADDED ON)
IF(NOT TARGET codechecker)
add_custom_target(codechecker
COMMAND ${CODECHECKER_PATH}
analyze
${CODECHECKER_SKIP}
-j 4
-o ${CODECHECKER_OUTPUT_DIRECTORY}
${CODECHECKER_COMPILE_COMMANDS}
)
add_custom_target(codechecker-clean
COMMAND rm -rf ${CODECHECKER_OUTPUT_DIRECTORY}
)
add_custom_target(codechecker-html
COMMAND ${CODECHECKER_PATH}
parse
${CODECHECKER_OUTPUT_DIRECTORY}
-e html
-o ${CODECHECKER_HTML_OUTPUT_DIRECTORY}
)
if (CODECHECKER_STORE)
add_custom_target(codechecker-store
COMMAND ${CODECHECKER_PATH}
store
--trim-path-prefix \"${CODECHECK_TRIM_PATH}\"
--tag \"${CODECHECKER_BRANCH}\"
-n \"${CODECHECKER_RUN}\"
--url ${CODECHECKER_URL}
${CODECHECKER_OUTPUT_DIRECTORY}
)
endif()
endif()
endif()

View File

@@ -0,0 +1,23 @@
find_program(COMPDB_PATH
NAME compdb
PATHS ~/.local/bin/
/bin
/sbin
/usr/bin
/usr/sbin
/usr/local/bin
/usr/local/sbin
)
if (COMPDB_PATH)
IF(NOT TARGET COMPD)
add_custom_target(COMPD
ALL
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/compile_commands.json
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
COMMAND ${COMPDB_PATH} -p ${CMAKE_CURRENT_BINARY_DIR} list >compile_commands.json
)
endif()
endif()

View File

@@ -0,0 +1,46 @@
#
# Copyright (C) 2018 by George Cave - gcave@stablecoder.ca
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy of
# the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under
# the License.
option(ENABLE_ALL_WARNINGS "Compile with all warnings for the major compilers."
OFF)
option(ENABLE_EFFECTIVE_CXX "Enable Effective C++ warnings." OFF)
if(ENABLE_ALL_WARNINGS)
if(CMAKE_COMPILER_IS_GNUCXX)
# GCC
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra")
elseif("${CMAKE_C_COMPILER_ID}" MATCHES "(Apple)?[Cc]lang"
OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "(Apple)?[Cc]lang")
# Clang
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra")
elseif(MSVC)
# MSVC
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /W4")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4")
endif()
endif()
if(ENABLE_EFFECTIVE_CXX)
if(CMAKE_COMPILER_IS_GNUCXX)
# GCC
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Weffc++")
elseif("${CMAKE_C_COMPILER_ID}" MATCHES "(Apple)?[Cc]lang"
OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "(Apple)?[Cc]lang")
# Clang
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Weffc++")
endif()
endif()

View File

@@ -0,0 +1,15 @@
include(c++-standards)
include(compiler-options)
include(sanitizers)
include(codecheck)
include(CppCheck)
include(code-coverage)
include(tools)
include(GNUInstallDirs)
include(CTest)
include(doxygen)
include(ProcessGIT)
include(CheckParent)
include(add_my_test)
include(TestBigEndian)
include(compdb)

View File

@@ -0,0 +1,9 @@
include(defaultIncludes)
find_package(Git)
enable_testing()
cxx_20()
build_docs(PROCESS_DOXYFILE DOXYFILE_PATH "docs/Doxyfile.in" )
add_code_coverage_all_targets(EXCLUDE ${PROJECT_SOURCE_DIR}/libs/* ${PROJECT_SOURCE_DIR}/test/*)
TEST_BIG_ENDIAN(IS_BIG_ENDIAN)

134
cmake/Modules/doxygen.cmake Normal file
View File

@@ -0,0 +1,134 @@
#
# Copyright (C) 2018 by George Cave - gcave@stablecoder.ca
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy of
# the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under
# the License.
find_package(Doxygen)
option(BUILD_DOCUMENTATION "Build API documentation using Doxygen. (make doc)"
${DOXYGEN_FOUND})
# Builds doxygen documentation with a default 'Doxyfile.in' or with a specified
# one, and can make the results installable (under the `doc` install target)
#
# This can only be used once per project, as each target generated is as
# `doc-${PROJECT_NAME}` unless TARGET_NAME is specified.
# ~~~
# Optional Arguments:
#
# ADD_TO_DOC
# If specified, adds this generated target to be a dependency of the more general
# `doc` target.
#
# INSTALLABLE
# Adds the generated documentation to the generic `install` target, under the
# `documentation` installation group.
#
# PROCESS_DOXYFILE
# If set, then will process the found Doxyfile through the CMAKE `configure_file`
# function for macro replacements before using it. (@ONLY)
#
# TARGET_NAME <str>
# The name to give the doc target. (Default: doc-${PROJECT_NAME})
#
# OUTPUT_DIR <str>
# The directory to place the generated output. (Default: ${CMAKE_CURRENT_BINARY_DIR}/doc)
#
# INSTALL_PATH <str>
# The path to install the documenttation under. (if not specified, defaults to
# 'share/${PROJECT_NAME})
#
# DOXYFILE_PATH <str>
# The given doxygen file to use/process. (Defaults to'${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile')
# ~~~
function(build_docs)
set(OPTIONS ADD_TO_DOC INSTALLABLE PROCESS_DOXYFILE)
set(SINGLE_VALUE_KEYWORDS
TARGET_NAME
INSTALL_PATH
DOXYFILE_PATH
OUTPUT_DIR)
set(MULTI_VALUE_KEYWORDS)
cmake_parse_arguments(build_docs
"${OPTIONS}"
"${SINGLE_VALUE_KEYWORDS}"
"${MULTI_VALUE_KEYWORDS}"
${ARGN})
if(BUILD_DOCUMENTATION)
if(NOT DOXYGEN_FOUND)
message(FATAL_ERROR "Doxygen is needed to build the documentation.")
endif()
if(NOT build_docs_DOXYFILE_PATH)
set(DOXYFILE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile")
elseif(EXISTS "${build_docs_DOXYFILE_PATH}")
set(DOXYFILE_PATH "${build_docs_DOXYFILE_PATH}")
else()
set(DOXYFILE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/${build_docs_DOXYFILE_PATH}")
endif()
if(NOT EXISTS "${DOXYFILE_PATH}")
message(
SEND_ERROR
"Could not find Doxyfile to use for procesing documentation at: ${DOXYFILE_PATH}"
)
return()
endif()
if(build_docs_PROCESS_DOXYFILE)
set(DOXYFILE "${CMAKE_CURRENT_BINARY_DIR}/Doxyfile")
configure_file("${DOXYFILE_PATH}" "${DOXYFILE}" @ONLY)
else()
set(DOXYFILE "${DOXYFILE_PATH}")
endif()
if(build_docs_OUTPUT_DIR)
set(OUT_DIR "${build_docs_OUTPUT_DIR}")
else()
set(OUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/doc")
endif()
file(MAKE_DIRECTORY "${OUT_DIR}")
if(build_docs_TARGET_NAME)
set(TARGET_NAME ${build_docs_TARGET_NAME})
else()
set(TARGET_NAME doc-${PROJECT_NAME})
endif()
IF(NOT TARGET ${TARGET_NAME})
add_custom_target(${TARGET_NAME}
COMMAND ${DOXYGEN_EXECUTABLE} "${DOXYFILE}"
WORKING_DIRECTORY "${OUT_DIR}"
VERBATIM)
ENDIF()
if(build_docs_ADD_TO_DOC)
if(NOT TARGET doc)
add_custom_target(doc)
endif()
add_dependencies(doc ${TARGET_NAME})
endif()
if(build_docs_INSTALLABLE)
if(NOT build_docs_INSTALL_PATH)
set(build_docs_INSTALL_PATH share/${PROJECT_NAME})
endif()
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/doc/
COMPONENT documentation
DESTINATION ${build_docs_INSTALL_PATH})
endif()
endif()
endfunction()

View File

@@ -0,0 +1,34 @@
function(protobuf_generate_cpp)
set(OPTIONS)
set(SINGLE_VALUE_KEYWORDS
PROTO_PATH
CPP_PATH
HPP_PATH
)
set(MULTI_VALUE_KEYWORDS)
cmake_parse_arguments(protobuf
"${OPTIONS}"
"${SINGLE_VALUE_KEYWORDS}"
"${MULTI_VALUE_KEYWORDS}"
${ARGN})
FILE(GLOB PROTO_FILES ${protobuf_PROTO_PATH}/*.proto)
set(PROTOC ${CMAKE_BINARY_DIR}/libs/protobuf/protoc)
# set(PROTOC protoc)
FOREACH(proto ${PROTO_FILES})
FILE(TO_NATIVE_PATH ${proto} proto_native)
get_filename_component(protoFILENAME ${proto} NAME_WLE )
get_filename_component(protoDIR ${proto} DIRECTORY)
add_custom_command(
OUTPUT "${protoDIR}/${protoFILENAME}.pb.cc"
DEPENDS "${protoDIR}/${protoFILENAME}.proto"
COMMAND ${PROTOC} --cpp_out=${protoDIR} --proto_path=${protoDIR} --proto_path="${CMAKE_SOURCE_DIR}/libs/protobuf/src" "${protoDIR}/${protoFILENAME}.proto"
)
ENDFOREACH(proto)
endfunction()

View File

@@ -0,0 +1,87 @@
#
# Copyright (C) 2018 by George Cave - gcave@stablecoder.ca
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy of
# the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under
# the License.
set(
USE_SANITIZER
""
CACHE
STRING
"Compile with a sanitizer. Options are: Address, Memory, MemoryWithOrigins, Undefined, Thread, Leak, 'Address;Undefined'"
)
function(append value)
foreach(variable ${ARGN})
set(${variable} "${${variable}} ${value}" PARENT_SCOPE)
endforeach(variable)
endfunction()
if(USE_SANITIZER)
append("-fno-omit-frame-pointer" CMAKE_C_FLAGS CMAKE_CXX_FLAGS)
if(UNIX)
if(uppercase_CMAKE_BUILD_TYPE STREQUAL "DEBUG")
append("-O1" CMAKE_C_FLAGS CMAKE_CXX_FLAGS)
endif()
if(USE_SANITIZER MATCHES "([Aa]ddress);([Uu]ndefined)"
OR USE_SANITIZER MATCHES "([Uu]ndefined);([Aa]ddress)")
message(STATUS "Building with Address, Undefined sanitizers")
append("-fsanitize=address,undefined" CMAKE_C_FLAGS CMAKE_CXX_FLAGS)
elseif("${USE_SANITIZER}" MATCHES "([Aa]ddress)")
# Optional: -fno-optimize-sibling-calls -fsanitize-address-use-after-scope
message(STATUS "Building with Address sanitizer")
append("-fsanitize=address" CMAKE_C_FLAGS CMAKE_CXX_FLAGS)
elseif(USE_SANITIZER MATCHES "([Mm]emory([Ww]ith[Oo]rigins)?)")
# Optional: -fno-optimize-sibling-calls -fsanitize-memory-track-origins=2
append("-fsanitize=memory" CMAKE_C_FLAGS CMAKE_CXX_FLAGS)
if(USE_SANITIZER MATCHES "([Mm]emory[Ww]ith[Oo]rigins)")
message(STATUS "Building with MemoryWithOrigins sanitizer")
append("-fsanitize-memory-track-origins" CMAKE_C_FLAGS CMAKE_CXX_FLAGS)
else()
message(STATUS "Building with Memory sanitizer")
endif()
elseif(USE_SANITIZER MATCHES "([Uu]ndefined)")
message(STATUS "Building with Undefined sanitizer")
append("-fsanitize=undefined" CMAKE_C_FLAGS CMAKE_CXX_FLAGS)
if(EXISTS "${BLACKLIST_FILE}")
append("-fsanitize-blacklist=${BLACKLIST_FILE}" CMAKE_C_FLAGS
CMAKE_CXX_FLAGS)
endif()
elseif(USE_SANITIZER MATCHES "([Tt]hread)")
message(STATUS "Building with Thread sanitizer")
append("-fsanitize=thread" CMAKE_C_FLAGS CMAKE_CXX_FLAGS)
elseif(USE_SANITIZER MATCHES "([Ll]eak)")
message(STATUS "Building with Leak sanitizer")
append("-fsanitize=leak" CMAKE_C_FLAGS CMAKE_CXX_FLAGS)
else()
message(
FATAL_ERROR "Unsupported value of USE_SANITIZER: ${USE_SANITIZER}")
endif()
elseif(MSVC)
if(USE_SANITIZER MATCHES "([Aa]ddress)")
message(STATUS "Building with Address sanitizer")
append("-fsanitize=address" CMAKE_C_FLAGS CMAKE_CXX_FLAGS)
else()
message(
FATAL_ERROR
"This sanitizer not yet supported in the MSVC environment: ${USE_SANITIZER}"
)
endif()
else()
message(FATAL_ERROR "USE_SANITIZER is not supported on this platform.")
endif()
endif()

64
cmake/Modules/tools.cmake Normal file
View File

@@ -0,0 +1,64 @@
#
# Copyright (C) 2018 by George Cave - gcave@stablecoder.ca
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy of
# the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under
# the License.
option(CLANG_TIDY "Turns on clang-tidy processing if it is found." OFF)
option(IWYU "Turns on include-what-you-use processing if it is found." OFF)
# Adds clang-tidy checks to the compilation, with the given arguments being used
# as the options set.
macro(clang_tidy)
if(CLANG_TIDY AND CLANG_TIDY_EXE)
set(CMAKE_CXX_CLANG_TIDY ${CLANG_TIDY_EXE} ${ARGN})
endif()
endmacro()
# Adds include_what_you_use to the compilation, with the given arguments being
# used as the options set.
macro(include_what_you_use)
if(IWYU AND IWYU_EXE)
set(CMAKE_CXX_INCLUDE_WHAT_YOU_USE "${IWYU_EXE};${ARGN}")
endif()
endmacro()
find_program(CLANG_TIDY_EXE NAMES clang-tidy clang-tidy-8 clang-tidy-7 clang-tidy-6)
if(CLANG_TIDY_EXE)
message(STATUS "clang-tidy found: ${CLANG_TIDY_EXE}")
if(NOT CLANG_TIDY)
message(STATUS "clang-tidy NOT ENABLED via 'CLANG_TIDY' variable!")
set(CMAKE_CXX_CLANG_TIDY "" CACHE STRING "" FORCE) # delete it
endif()
elseif(CLANG_TIDY)
message(SEND_ERROR "Cannot enable clang-tidy, as executable not found!")
set(CMAKE_CXX_CLANG_TIDY "" CACHE STRING "" FORCE) # delete it
else()
message(STATUS "clang-tidy not found!")
set(CMAKE_CXX_CLANG_TIDY "" CACHE STRING "" FORCE) # delete it
endif()
find_program(IWYU_EXE NAMES "include-what-you-use")
if(IWYU_EXE)
message(STATUS "include-what-you-use found: ${IWYU_EXE}")
if(NOT IWYU)
message(STATUS "include-what-you-use NOT ENABLED via 'IWYU' variable!")
set(CMAKE_CXX_INCLUDE_WHAT_YOU_USE "" CACHE STRING "" FORCE) # delete it
endif()
elseif(IWYU)
message(
SEND_ERROR "Cannot enable include-what-you-use, as executable not found!")
set(CMAKE_CXX_INCLUDE_WHAT_YOU_USE "" CACHE STRING "" FORCE) # delete it
else()
message(STATUS "include-what-you-use not found!")
set(CMAKE_CXX_INCLUDE_WHAT_YOU_USE "" CACHE STRING "" FORCE) # delete it
endif()

43
cmake/Modules/xslt.cmake Normal file
View File

@@ -0,0 +1,43 @@
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
#
IF(NOT CMAKE_SET_XSLT)
set(CMAKE_SET_XSLT true)
find_program(CMAKE_XSLTPROC NAME xsltproc HINTS ${CMAKE_SYSTEM_PROGRAM_PATH})
if (CMAKE_XSLTPROC)
message(STATUS "XSLTPROC found")
else()
message(FATAL_ERROR "XSLTPROC not found")
endif()
function(xslt_generate)
set(OPTIONS )
set(SINGLE_VALUE_KEYWORDS OUT_FILE XML_FILE XSL_FILE)
set(MULTI_VALUE_KEYWORDS)
cmake_parse_arguments(XSLT
"${OPTIONS}"
"${SINGLE_VALUE_KEYWORDS}"
"${MULTI_VALUE_KEYWORDS}"
${ARGN})
message(STATUS "generating ${CMAKE_XSLTPROC} --nonet -o ${XSLT_OUT_FILE} ${XSLT_XSL_FILE} ${XSLT_XML_FILE} ")
execute_process(
COMMAND ${CMAKE_XSLTPROC} --nonet -o ${XSLT_OUT_FILE} ${XSLT_XSL_FILE} ${XSLT_XML_FILE}
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE OUT
ERROR_VARIABLE ERR
)
message(STATUS ${OUT})
message(STATUS ${ERR})
endfunction()
ENDIF()

View File

@@ -0,0 +1,27 @@
# the name of the target operating system
SET(CMAKE_SYSTEM_NAME Windows)
# which compilers to use for C and C++
SET(CMAKE_C_COMPILER x86_64-w64-mingw32-gcc-posix)
SET(CMAKE_CXX_COMPILER x86_64-w64-mingw32-g++-posix)
# here is the target environment located
SET(CMAKE_FIND_ROOT_PATH /usr/x86_64-w64-mingw32)
# adjust the default behaviour of the FIND_XXX() commands:
# search headers and libraries in the target environment, search
# programs in the host environment
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
find_program(WINE_FOUND wine)
if(WINE_FOUND)
message(STATUS "WINE found")
SET(CMAKE_CROSSCOMPILING_EMULATOR wine)
endif()
if (CMAKE_CROSSCOMPILING)
message(STATUS "Crosscompiling for ${CMAKE_SYSTEM_NAME}")
endif()

23
docs/Doxyfile.in Normal file
View File

@@ -0,0 +1,23 @@
DOXYFILE_ENCODING = UTF-8
PROJECT_NAME = @PROJECT_NAME@
PROJECT_NUMBER = "@GIT_BRANCH@ @GIT_COMMIT_HASH@"
PROJECT_BRIEF = "library implementing a floats to libcms gateway"
OUTPUT_DIRECTORY = @CMAKE_CURRENT_BINARY_DIR@/doxygen/
OUTPUT_LANGUAGE = English
BRIEF_MEMBER_DESC = YES
REPEAT_BRIEF = YES
ALWAYS_DETAILED_SEC = NO
INLINE_INHERITED_MEMB = NO
FULL_PATH_NAMES = YES
MULTILINE_CPP_IS_BRIEF = NO
TAB_SIZE = 4
MARKDOWN_SUPPORT = YES
GENERATE_TODOLIST = YES
GENERATE_TESTLIST = YES
GENERATE_BUGLIST = YES
GENERATE_DEPRECATEDLIST= YES
INPUT = @CMAKE_CURRENT_SOURCE_DIR@/README.md @CMAKE_CURRENT_SOURCE_DIR@/src/ @CMAKE_CURRENT_SOURCE_DIR@/tests/ @CMAKE_CURRENT_SOURCE_DIR@/docs @CMAKE_CURRENT_SOURCE_DIR@/include
INPUT_ENCODING = UTF-8
FILE_PATTERNS = *.hpp *.cpp *.md
USE_MDFILE_AS_MAINPAGE = README.md
RECURSIVE = YES

View File

View File

@@ -0,0 +1,483 @@
{
"projectName": "CLI11",
"projectOwner": "CLIUtils",
"repoType": "github",
"repoHost": "https://github.com",
"files": [
"README.md"
],
"imageSize": 100,
"commit": true,
"commitConvention": "atom",
"contributors": [
{
"login": "henryiii",
"name": "Henry Schreiner",
"avatar_url": "https://avatars1.githubusercontent.com/u/4616906?v=4",
"profile": "http://iscinumpy.gitlab.io",
"contributions": [
"bug",
"doc",
"code"
]
},
{
"login": "phlptp",
"name": "Philip Top",
"avatar_url": "https://avatars0.githubusercontent.com/u/20667153?v=4",
"profile": "https://github.com/phlptp",
"contributions": [
"bug",
"doc",
"code"
]
},
{
"login": "cbachhuber",
"name": "Christoph Bachhuber",
"avatar_url": "https://avatars0.githubusercontent.com/u/27212661?v=4",
"profile": "https://www.linkedin.com/in/cbachhuber/",
"contributions": [
"example",
"code"
]
},
{
"login": "lambdafu",
"name": "Marcus Brinkmann",
"avatar_url": "https://avatars1.githubusercontent.com/u/1138455?v=4",
"profile": "https://lambdafu.net/",
"contributions": [
"bug",
"code"
]
},
{
"login": "SkyToGround",
"name": "Jonas Nilsson",
"avatar_url": "https://avatars1.githubusercontent.com/u/58835?v=4",
"profile": "https://github.com/SkyToGround",
"contributions": [
"bug",
"code"
]
},
{
"login": "dvj",
"name": "Doug Johnston",
"avatar_url": "https://avatars2.githubusercontent.com/u/77217?v=4",
"profile": "https://github.com/dvj",
"contributions": [
"bug",
"code"
]
},
{
"login": "lczech",
"name": "Lucas Czech",
"avatar_url": "https://avatars0.githubusercontent.com/u/4741887?v=4",
"profile": "http://lucas-czech.de",
"contributions": [
"bug",
"code"
]
},
{
"login": "rafiw",
"name": "Rafi Wiener",
"avatar_url": "https://avatars3.githubusercontent.com/u/3034707?v=4",
"profile": "https://github.com/rafiw",
"contributions": [
"bug",
"code"
]
},
{
"login": "mensinda",
"name": "Daniel Mensinger",
"avatar_url": "https://avatars3.githubusercontent.com/u/3407462?v=4",
"profile": "https://github.com/mensinda",
"contributions": [
"platform"
]
},
{
"login": "jbriales",
"name": "Jesus Briales",
"avatar_url": "https://avatars1.githubusercontent.com/u/6850478?v=4",
"profile": "https://github.com/jbriales",
"contributions": [
"code",
"bug"
]
},
{
"login": "seanfisk",
"name": "Sean Fisk",
"avatar_url": "https://avatars0.githubusercontent.com/u/410322?v=4",
"profile": "https://seanfisk.com/",
"contributions": [
"bug",
"code"
]
},
{
"login": "fpeng1985",
"name": "fpeng1985",
"avatar_url": "https://avatars1.githubusercontent.com/u/87981?v=4",
"profile": "https://github.com/fpeng1985",
"contributions": [
"code"
]
},
{
"login": "almikhayl",
"name": "almikhayl",
"avatar_url": "https://avatars2.githubusercontent.com/u/6747040?v=4",
"profile": "https://github.com/almikhayl",
"contributions": [
"code",
"platform"
]
},
{
"login": "andrew-hardin",
"name": "Andrew Hardin",
"avatar_url": "https://avatars0.githubusercontent.com/u/16496326?v=4",
"profile": "https://github.com/andrew-hardin",
"contributions": [
"code"
]
},
{
"login": "SX91",
"name": "Anton",
"avatar_url": "https://avatars2.githubusercontent.com/u/754754?v=4",
"profile": "https://github.com/SX91",
"contributions": [
"code"
]
},
{
"login": "helmesjo",
"name": "Fred Helmesjö",
"avatar_url": "https://avatars0.githubusercontent.com/u/2501070?v=4",
"profile": "https://github.com/helmesjo",
"contributions": [
"bug",
"code"
]
},
{
"login": "skannan89",
"name": "Kannan",
"avatar_url": "https://avatars0.githubusercontent.com/u/11918764?v=4",
"profile": "https://github.com/skannan89",
"contributions": [
"bug",
"code"
]
},
{
"login": "kraj",
"name": "Khem Raj",
"avatar_url": "https://avatars3.githubusercontent.com/u/465279?v=4",
"profile": "http://himvis.com",
"contributions": [
"code"
]
},
{
"login": "mogigoma",
"name": "Mak Kolybabi",
"avatar_url": "https://avatars2.githubusercontent.com/u/130862?v=4",
"profile": "https://www.mogigoma.com/",
"contributions": [
"doc"
]
},
{
"login": "msoeken",
"name": "Mathias Soeken",
"avatar_url": "https://avatars0.githubusercontent.com/u/1998245?v=4",
"profile": "http://msoeken.github.io",
"contributions": [
"doc"
]
},
{
"login": "nathanhourt",
"name": "Nathan Hourt",
"avatar_url": "https://avatars2.githubusercontent.com/u/271977?v=4",
"profile": "https://github.com/nathanhourt",
"contributions": [
"bug",
"code"
]
},
{
"login": "pleroux0",
"name": "Paul le Roux",
"avatar_url": "https://avatars2.githubusercontent.com/u/39619854?v=4",
"profile": "https://github.com/pleroux0",
"contributions": [
"code",
"platform"
]
},
{
"login": "chfast",
"name": "Paweł Bylica",
"avatar_url": "https://avatars1.githubusercontent.com/u/573380?v=4",
"profile": "https://github.com/chfast",
"contributions": [
"platform"
]
},
{
"login": "peterazmanov",
"name": "Peter Azmanov",
"avatar_url": "https://avatars0.githubusercontent.com/u/15322318?v=4",
"profile": "https://github.com/peterazmanov",
"contributions": [
"code"
]
},
{
"login": "delpinux",
"name": "Stéphane Del Pino",
"avatar_url": "https://avatars0.githubusercontent.com/u/35096584?v=4",
"profile": "https://github.com/delpinux",
"contributions": [
"code"
]
},
{
"login": "metopa",
"name": "Viacheslav Kroilov",
"avatar_url": "https://avatars2.githubusercontent.com/u/3974178?v=4",
"profile": "https://github.com/metopa",
"contributions": [
"code"
]
},
{
"login": "ChristosT",
"name": "christos",
"avatar_url": "https://avatars0.githubusercontent.com/u/6725596?v=4",
"profile": "http://cs.odu.edu/~ctsolakis",
"contributions": [
"code"
]
},
{
"login": "deining",
"name": "deining",
"avatar_url": "https://avatars3.githubusercontent.com/u/18169566?v=4",
"profile": "https://github.com/deining",
"contributions": [
"doc"
]
},
{
"login": "elszon",
"name": "elszon",
"avatar_url": "https://avatars0.githubusercontent.com/u/2971495?v=4",
"profile": "https://github.com/elszon",
"contributions": [
"code"
]
},
{
"login": "ncihnegn",
"name": "ncihnegn",
"avatar_url": "https://avatars3.githubusercontent.com/u/12021721?v=4",
"profile": "https://github.com/ncihnegn",
"contributions": [
"code"
]
},
{
"login": "nurelin",
"name": "nurelin",
"avatar_url": "https://avatars3.githubusercontent.com/u/5276274?v=4",
"profile": "https://github.com/nurelin",
"contributions": [
"code"
]
},
{
"login": "ryan4729",
"name": "ryan4729",
"avatar_url": "https://avatars3.githubusercontent.com/u/40183301?v=4",
"profile": "https://github.com/ryan4729",
"contributions": [
"test"
]
},
{
"login": "slurps-mad-rips",
"name": "Isabella Muerte",
"avatar_url": "https://avatars0.githubusercontent.com/u/63051?v=4",
"profile": "https://izzys.casa",
"contributions": [
"platform"
]
},
{
"login": "KOLANICH",
"name": "KOLANICH",
"avatar_url": "https://avatars1.githubusercontent.com/u/240344?v=4",
"profile": "https://github.com/KOLANICH",
"contributions": [
"platform"
]
},
{
"login": "jgerityneurala",
"name": "James Gerity",
"avatar_url": "https://avatars2.githubusercontent.com/u/57360646?v=4",
"profile": "https://github.com/jgerityneurala",
"contributions": [
"doc"
]
},
{
"login": "jsoref",
"name": "Josh Soref",
"avatar_url": "https://avatars0.githubusercontent.com/u/2119212?v=4",
"profile": "https://github.com/jsoref",
"contributions": [
"tool"
]
},
{
"login": "geir-t",
"name": "geir-t",
"avatar_url": "https://avatars3.githubusercontent.com/u/35292136?v=4",
"profile": "https://github.com/geir-t",
"contributions": [
"platform"
]
},
{
"login": "certik",
"name": "Ondřej Čertík",
"avatar_url": "https://avatars3.githubusercontent.com/u/20568?v=4",
"profile": "https://ondrejcertik.com/",
"contributions": [
"bug"
]
},
{
"login": "samhocevar",
"name": "Sam Hocevar",
"avatar_url": "https://avatars2.githubusercontent.com/u/245089?v=4",
"profile": "http://sam.hocevar.net/",
"contributions": [
"code"
]
},
{
"login": "rcurtin",
"name": "Ryan Curtin",
"avatar_url": "https://avatars0.githubusercontent.com/u/1845039?v=4",
"profile": "http://www.ratml.org/",
"contributions": [
"doc"
]
},
{
"login": "mbhall88",
"name": "Michael Hall",
"avatar_url": "https://avatars3.githubusercontent.com/u/20403931?v=4",
"profile": "https://mbh.sh",
"contributions": [
"doc"
]
},
{
"login": "ferdymercury",
"name": "ferdymercury",
"avatar_url": "https://avatars3.githubusercontent.com/u/10653970?v=4",
"profile": "https://github.com/ferdymercury",
"contributions": [
"doc"
]
},
{
"login": "jakoblover",
"name": "Jakob Lover",
"avatar_url": "https://avatars0.githubusercontent.com/u/14160441?v=4",
"profile": "https://github.com/jakoblover",
"contributions": [
"code"
]
},
{
"login": "ZeeD26",
"name": "Dominik Steinberger",
"avatar_url": "https://avatars2.githubusercontent.com/u/2487468?v=4",
"profile": "https://github.com/ZeeD26",
"contributions": [
"code"
]
},
{
"login": "dfleury2",
"name": "D. Fleury",
"avatar_url": "https://avatars1.githubusercontent.com/u/4805384?v=4",
"profile": "https://github.com/dfleury2",
"contributions": [
"code"
]
},
{
"login": "dbarowy",
"name": "Dan Barowy",
"avatar_url": "https://avatars3.githubusercontent.com/u/573142?v=4",
"profile": "https://github.com/dbarowy",
"contributions": [
"doc"
]
},
{
"login": "paddy-hack",
"name": "Olaf Meeuwissen",
"avatar_url": "https://avatars.githubusercontent.com/u/6804372?v=4",
"profile": "https://github.com/paddy-hack",
"contributions": [
"code"
]
},
{
"login": "dryleev",
"name": "dryleev",
"avatar_url": "https://avatars.githubusercontent.com/u/83670813?v=4",
"profile": "https://github.com/dryleev",
"contributions": [
"code"
]
},
{
"login": "AnticliMaxtic",
"name": "Max",
"avatar_url": "https://avatars.githubusercontent.com/u/43995389?v=4",
"profile": "https://github.com/AnticliMaxtic",
"contributions": [
"code"
]
},
{
"login": "alexdewar",
"name": "Alex Dewar",
"avatar_url": "https://avatars.githubusercontent.com/u/23149834?v=4",
"profile": "https://profiles.sussex.ac.uk/p281168-alex-dewar/publications",
"contributions": [
"code"
]
}
],
"contributorsPerLine": 7,
"skipCi": true
}

39
libs/CLI11/.appveyor.yml Normal file
View File

@@ -0,0 +1,39 @@
version: 2.3.1.{build}
branches:
only:
- main
- v1
install:
- git submodule update --init --recursive
- py -3 --version
- set PATH=C:\Python38-x64;C:\Python38-x64\Scripts;%PATH%
- cmake --version
- python --version
- python -m pip --version
- python -m pip install conan
- conan user
- conan --version
build_script:
- mkdir build
- cd build
- ps:
cmake .. -DCLI11_WARNINGS_AS_ERRORS=ON -DCLI11_SINGLE_FILE_TESTS=ON
-DCMAKE_BUILD_TYPE=Debug -DCMAKE_GENERATOR="Visual Studio 14 2015"
- ps: cmake --build .
- cd ..
- ps: set CTEST_OUTPUT_ON_FAILURE=1
- conan create . CLIUtils/CLI11
test_script:
- cd build
- ps: ctest --output-on-failure -C Debug
notifications:
- provider: Webhook
url: https://webhooks.gitter.im/e/0185e91c5d989a476d7b
on_build_success: false
on_build_failure: true
on_build_status_changed: true

View File

@@ -0,0 +1,23 @@
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)
-DCMAKE_BUILD_TYPE=$(cli11.build_type) $(cli11.options)
displayName: "Configure"
- script: cmake --build . -- -j2 --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' )

View File

@@ -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

View File

@@ -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"

86
libs/CLI11/.clang-format Normal file
View File

@@ -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

80
libs/CLI11/.clang-tidy Normal file
View File

@@ -0,0 +1,80 @@
# 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
# 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,
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,
*performance*,
-performance-unnecessary-value-param,
-performance-inefficient-string-concatenation,
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,
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: "*"

View File

@@ -0,0 +1,6 @@
format:
line_width: 99
# Causes a few issues - can be solved later, possibly.
markup:
enable_markup: false

3
libs/CLI11/.codecov.yml Normal file
View File

@@ -0,0 +1,3 @@
ignore:
- "tests"
- "examples"

13
libs/CLI11/.editorconfig Normal file
View File

@@ -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

103
libs/CLI11/.github/CONTRIBUTING.md vendored Normal file
View File

@@ -0,0 +1,103 @@
# 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 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. You should install it (or use
[pipx](https://pypa.github.io/pipx/)):
```bash
python3 -m pip install pre-commit
```
Then, you can run it on the items you've added to your staging area, or all
files:
```bash
pre-commit run
# OR
pre-commit run --all-files
```
And, if you want to always use it, you can install it as a git hook (hence the
name, pre-commit):
```bash
pre-commit install
```
## For developers releasing to Conan.io
This is now done by the CI system on tagged releases. Previously, the steps to
make a Conan.io release were:
```bash
conan remove '*' # optional, I like to be clean
conan create . cliutils/stable
conan upload "*" -r cli11 --all
```
Here I've assumed that the remote is `cli11`.
## 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 `.appveyor.yml` and `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"

View File

@@ -0,0 +1,25 @@
name: Quick CMake config
description: "Runs CMake 3.4+ (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@v1.12
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

7
libs/CLI11/.github/codecov.yml vendored Normal file
View File

@@ -0,0 +1,7 @@
codecov:
notify:
after_n_builds: 4
coverage:
status:
project:
informational: true

7
libs/CLI11/.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,7 @@
version: 2
updates:
# Maintain dependencies for GitHub Actions
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "daily"

4
libs/CLI11/.github/labeler_merged.yml vendored Normal file
View File

@@ -0,0 +1,4 @@
needs changelog:
- all: ["!CHANGELOG.md"]
needs README:
- all: ["!README.md"]

59
libs/CLI11/.github/workflows/build.yml vendored Normal file
View File

@@ -0,0 +1,59 @@
name: Build
on:
push:
branches:
- main
- v*
tags:
- "*"
pull_request:
jobs:
single-header:
name: Single header
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
submodules: true
- uses: actions/setup-python@v4
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
cp build/CLI11-*-Source.* .
- name: Make header
run: cmake --build build --target CLI11-generate-single-file
- name: Copy file to main folder
run: cp build/include/CLI11.hpp CLI11.hpp
- uses: actions/upload-artifact@v3
with:
name: CLI11.hpp
path: CLI11.hpp
- uses: actions/upload-artifact@v3
with:
name: CLI11-Source
path: CLI11-Source
- name: Release
uses: softprops/action-gh-release@v1
if: startsWith(github.ref, 'refs/tags/')
with:
files: |
CLI11.hpp
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -0,0 +1,15 @@
name: PR merged
on:
pull_request_target:
types: [closed]
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

250
libs/CLI11/.github/workflows/tests.yml vendored Normal file
View File

@@ -0,0 +1,250 @@
name: Tests
on:
push:
branches:
- main
- v*
pull_request:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
coverage:
name: Coverage
runs-on: ubuntu-latest
strategy:
matrix:
std: ["11", "14", "17", "20"]
precompile: ["ON", "OFF"]
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Get LCov
run: |
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_EXAMPLES=OFF \
-DCLI11_PRECOMPILED=${{matrix.precompile}} \
-DCMAKE_BUILD_TYPE=Coverage
- name: Build
run: cmake --build build -j4
- name: Test
run: cmake --build build --target CLI11_coverage
- name: Prepare coverage
run: |
lcov --directory . --capture --output-file coverage.info
lcov --remove coverage.info '*/tests/*' '*/examples/*' '/usr/*' --output-file coverage.info
lcov --list coverage.info
working-directory: build
- uses: codecov/codecov-action@v3
with:
fail_ci_if_error: true
working-directory: build
clang-tidy:
name: Clang-Tidy
runs-on: ubuntu-latest
container: silkeh/clang:14
steps:
- uses: actions/checkout@v3
- name: Configure
run: >
cmake -S . -B build -DCMAKE_CXX_STANDARD=17
-DCMAKE_CXX_CLANG_TIDY="$(which
clang-tidy);--use-color;--warnings-as-errors=*"
- name: Build
run: cmake --build build -j4 -- --keep-going
cuda-build:
name: CUDA build only
runs-on: ubuntu-latest
container: nvidia/cuda:10.2-devel-ubuntu18.04
steps:
- uses: actions/checkout@v1
with:
submodules: true
- name: Add wget
run: apt-get update && apt-get install -y wget
- name: Get cmake
uses: jwlawson/actions-setup-cmake@v1.13
- name: Configure
run: cmake -S . -B build -DCLI11_CUDA_TESTS=ON
- name: Build
run: cmake --build build -j2
boost-build:
name: Boost build
runs-on: ubuntu-latest
container: zouzias/boost:1.76.0
steps:
- uses: actions/checkout@v1
with:
submodules: true
- name: Add deps
run: apt-get update && apt-get install make
- name: Get CMake
uses: jwlawson/actions-setup-cmake@v1.13
- name: Configure
run: cmake -S . -B build -DCLI11_BOOST=ON
- name: Build
run: cmake --build build -j2
- name: Run tests
run: ctest --output-on-failure
working-directory: build
meson-build:
name: Meson build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Prepare commands
run: |
pipx install meson
pipx install ninja
- name: Configure
run: meson setup build-meson . -Dtests=true
- name: Build
run: meson compile -C build-meson
cmake-config:
name: CMake config check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Check CMake 3.4
with:
cmake-version: "3.4"
uses: ./.github/actions/quick_cmake
- name: Check CMake 3.5
uses: ./.github/actions/quick_cmake
with:
cmake-version: "3.5"
if: success() || failure()
- name: Check CMake 3.6
uses: ./.github/actions/quick_cmake
with:
cmake-version: "3.6"
if: success() || failure()
- name: Check CMake 3.7
uses: ./.github/actions/quick_cmake
with:
cmake-version: "3.7"
if: success() || failure()
- name: Check CMake 3.8
uses: ./.github/actions/quick_cmake
with:
cmake-version: "3.8"
if: success() || failure()
- name: Check CMake 3.9
uses: ./.github/actions/quick_cmake
with:
cmake-version: "3.9"
if: success() || failure()
- name: Check CMake 3.10
uses: ./.github/actions/quick_cmake
with:
cmake-version: "3.10"
if: success() || failure()
- name: Check CMake 3.11 (full)
uses: ./.github/actions/quick_cmake
with:
cmake-version: "3.11"
args: -DCLI11_SANITIZERS=ON -DCLI11_BUILD_EXAMPLES_JSON=ON
if: success() || failure()
- name: Check CMake 3.12
uses: ./.github/actions/quick_cmake
with:
cmake-version: "3.12"
if: success() || failure()
- name: Check CMake 3.13
uses: ./.github/actions/quick_cmake
with:
cmake-version: "3.13"
if: success() || failure()
- name: Check CMake 3.14
uses: ./.github/actions/quick_cmake
with:
cmake-version: "3.14"
if: success() || failure()
- name: Check CMake 3.15
uses: ./.github/actions/quick_cmake
with:
cmake-version: "3.15"
if: success() || failure()
- name: Check CMake 3.16
uses: ./.github/actions/quick_cmake
with:
cmake-version: "3.16"
if: success() || failure()
- name: Check CMake 3.17
uses: ./.github/actions/quick_cmake
with:
cmake-version: "3.17"
if: success() || failure()
- 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 (full)
uses: ./.github/actions/quick_cmake
with:
cmake-version: "3.21"
args: -DCLI11_SANITIZERS=ON -DCLI11_BUILD_EXAMPLES_JSON=ON
if: success() || failure()
- name: Check CMake 3.22 (full)
uses: ./.github/actions/quick_cmake
with:
cmake-version: "3.22"
args: -DCLI11_SANITIZERS=ON -DCLI11_BUILD_EXAMPLES_JSON=ON
if: success() || failure()

15
libs/CLI11/.gitignore vendored Normal file
View File

@@ -0,0 +1,15 @@
a.out*
*.swp
/*build*
/test_package/build
/Makefile
/CMakeFiles/*
/cmake_install.cmake
/*.kdev4
/html/*
!/meson.build
/node_modules/*
/package.json
/yarn.lock
/CLI11.hpp

View File

@@ -0,0 +1,88 @@
ci:
autoupdate_commit_msg: "chore(deps): pre-commit.ci autoupdate"
autofix_commit_msg: "style: pre-commit.ci fixes"
repos:
- repo: https://github.com/psf/black
rev: 22.10.0
hooks:
- id: black
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
hooks:
- id: check-added-large-files
- id: check-case-conflict
- id: check-merge-conflict
- id: check-symlinks
- id: check-yaml
- id: end-of-file-fixer
- id: mixed-line-ending
- id: trailing-whitespace
- repo: https://github.com/pre-commit/mirrors-clang-format
rev: v15.0.4
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/pre-commit/mirrors-prettier
rev: "v3.0.0-alpha.4"
hooks:
- id: prettier
types_or: [yaml, markdown, html, css, scss, javascript, json]
args: [--prose-wrap=always]
- repo: https://github.com/markdownlint/markdownlint
rev: v0.12.0
hooks:
- id: markdownlint
args: ["--style=scripts/mdlint_style.rb"]
# Uncomment on macOS - Apple has deprecated Ruby, so macOS is stuck on 2.6
# language_version: 3.1.2
# - 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.2.2
hooks:
- id: codespell
args: ["-L", "atleast,ans,doub,inout"]

7
libs/CLI11/.remarkrc Normal file
View File

@@ -0,0 +1,7 @@
{
"plugins": [
"remark-preset-lint-recommended",
["remark-lint-list-item-indent", "space"],
["remark-lint-no-undefined-references", {"allow": ["^1"]}]
]
}

1009
libs/CLI11/CHANGELOG.md Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -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.

83
libs/CLI11/CLI11.hpp.in Normal file
View File

@@ -0,0 +1,83 @@
// CLI11: Version {version}
// Originally designed by Henry Schreiner
// https://github.com/CLIUtils/CLI11
//
// This is a standalone header file generated by MakeSingleHeader.py in CLI11/scripts
// from: {git}
//
// CLI11 {version} Copyright (c) 2017-2022 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.
#pragma once
// Standard combined includes:
{public_includes}
{version_hpp}
{macros_hpp}
{validators_hpp_filesystem}
namespace {namespace} {{
{string_tools_hpp}
{string_tools_inl_hpp}
{error_hpp}
{type_tools_hpp}
{split_hpp}
{split_inl_hpp}
{config_fwd_hpp}
{validators_hpp}
{validators_inl_hpp}
{formatter_fwd_hpp}
{option_hpp}
{option_inl_hpp}
{app_hpp}
{app_inl_hpp}
{config_hpp}
{config_inl_hpp}
{formatter_hpp}
{formatter_inl_hpp}
}} // namespace {namespace}

233
libs/CLI11/CMakeLists.txt Normal file
View File

@@ -0,0 +1,233 @@
cmake_minimum_required(VERSION 3.4)
# Note: this is a header only library. If you have an older CMake than 3.4,
# just add the CLI11/include directory and that's all you need to do.
# Make sure users don't get warnings on a tested (3.4 to 3.22) version
# of CMake. For most of the policies, the new version is better (hence the change).
# We don't use the 3.4...3.21 syntax because of a bug in an older MSVC's
# built-in and modified CMake 3.11
if(${CMAKE_VERSION} VERSION_LESS 3.22)
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
else()
cmake_policy(VERSION 3.22)
endif()
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(CMAKE_VERSION VERSION_LESS 3.10)
message(STATUS "CMake 3.10+ adds Doxygen support. Update CMake to build documentation")
elseif(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)
if(NOT CMAKE_VERSION VERSION_LESS 3.11)
include(FetchContent)
endif()
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 "NOT CMAKE_VERSION VERSION_LESS 3.11")
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)
cmake_dependent_option(CLI11_SANITIZERS "Download the sanitizers CMake config" OFF
"NOT CMAKE_VERSION VERSION_LESS 3.11" 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.11" 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(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 AND NOT DEFINED CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 11)
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()
# Allow IDE's to group targets into folders
if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
endif()
include(CLI11Warnings)
add_subdirectory(src)
# 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)
# Use find_package on the installed package
# Since we have no custom code, we can directly write this
# to Config.cmake (otherwise we'd have a custom config and would
# import Targets.cmake
# Add the version in a CMake readable way
configure_file("cmake/CLI11ConfigVersion.cmake.in" "CLI11ConfigVersion.cmake" @ONLY)
# Make version available in the install
install(FILES "${PROJECT_BINARY_DIR}/CLI11ConfigVersion.cmake"
DESTINATION "${CMAKE_INSTALL_DATADIR}/cmake/CLI11")
# Install the export target as a file
install(
EXPORT CLI11Targets
FILE CLI11Config.cmake
NAMESPACE CLI11::
DESTINATION "${CMAKE_INSTALL_DATADIR}/cmake/CLI11")
# Use find_package on the installed package
export(
TARGETS CLI11
NAMESPACE CLI11::
FILE CLI11Targets.cmake)
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
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}/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
/.appveyor.yml
/.pre-commit.*yaml)
set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE "all")
set(CPACK_DEBIAN_COMPRESSION_TYPE "xz")
set(CPACK_DEBIAN_PACKAGE_NAME "libcli11-dev")
include(CPack)

13
libs/CLI11/CPPLINT.cfg Normal file
View File

@@ -0,0 +1,13 @@
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/include_order # Requires unusual include order that encourages creating not self-contained headers
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=-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

25
libs/CLI11/LICENSE Normal file
View File

@@ -0,0 +1,25 @@
CLI11 2.2 Copyright (c) 2017-2022 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.

1767
libs/CLI11/README.md Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,139 @@
# C/C++ with GCC
# Build your C/C++ project with GCC using make.
# Add steps that publish test results, save build artifacts, deploy, and more:
# https://docs.microsoft.com/azure/devops/pipelines/apps/c-cpp/gcc
trigger:
- main
- "v*"
pr:
- main
- "v*"
variables:
cli11.single: ON
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: sharaku/cpplint:latest
steps:
- bash: cpplint --counting=detailed --recursive examples include/CLI tests
displayName: Checking against google style guide
# TODO: Fix macOS error and windows warning in c++17 mode
- job: Native
strategy:
matrix:
Linux14:
vmImage: "ubuntu-latest"
Linux14PC:
vmImage: "ubuntu-latest"
cli11.precompile: ON
macOS17:
vmImage: "macOS-latest"
cli11.std: 17
macOS11:
vmImage: "macOS-latest"
cli11.std: 11
macOS11PC:
vmImage: "macOS-latest"
cli11.std: 11
cli11.precompile: ON
Windows17:
vmImage: "windows-2019"
cli11.std: 17
Windows17PC:
vmImage: "windows-2019"
cli11.std: 17
cli11.precompile: ON
Windows11:
vmImage: "windows-2019"
cli11.std: 11
Windows20:
vmImage: "windows-2022"
cli11.std: 20
cli11.options: -DCMAKE_CXX_FLAGS="/EHsc"
WindowsLatest:
vmImage: "windows-2022"
cli11.std: 23
cli11.options: -DCMAKE_CXX_FLAGS="/EHsc"
Linux17nortti:
vmImage: "ubuntu-latest"
cli11.std: 17
cli11.options: -DCMAKE_CXX_FLAGS="-fno-rtti"
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.7"
- 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
- script: meson build
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"
gcc11:
containerImage: gcc:11
cli11.std: 20
gcc8:
containerImage: gcc:8
cli11.std: 17
gcc4.8:
containerImage: helics/buildenv:gcc4-8-builder
cli11.std: 11
cli11.options:
clang3.4:
containerImage: silkeh/clang:3.4
cli11.std: 11
clang8:
containerImage: silkeh/clang:8
cli11.std: 14
cli11.options: -DCLI11_FORCE_LIBCXX=ON
clang8_17:
containerImage: silkeh/clang:8
cli11.std: 17
cli11.options: -DCLI11_FORCE_LIBCXX=ON
clang10_20:
containerImage: silkeh/clang:10
cli11.std: 20
cli11.options: -DCLI11_FORCE_LIBCXX=ON -DCMAKE_CXX_FLAGS=-std=c++20
container: $[ variables['containerImage'] ]
steps:
- template: .ci/azure-cmake.yml
- template: .ci/azure-build.yml
- template: .ci/azure-test.yml

20
libs/CLI11/book/.gitignore vendored Normal file
View File

@@ -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*

View File

@@ -0,0 +1,7 @@
set(book_sources README.md SUMMARY.md)
file(
GLOB book_chapters
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
chapters/*.md)
add_custom_target(cli_book SOURCES ${book_sources} ${book_chapters})

94
libs/CLI11/book/README.md Normal file
View File

@@ -0,0 +1,94 @@
# CLI11: An introduction
This gitbook is designed to provide an introduction to using the CLI11 library
to write your own command line programs. 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 the Python libraries [Plumbum][] and [Click][], and
incorporates many of their user friendly features. The library is extensively
documented, with a [friendly introduction][readme], this tutorial book, and more
technical [API docs][].
> Feel free to contribute to [this documentation here][cli11tutorial] if
> something can be improved!
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:
```term
gitbook $ ./a.out
Parameter value: 0
gitbook $ ./a.out -p 4
Parameter value: 4
gitbook $ ./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 10 lines:
[include](code/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:
```term
gitbook:examples $ 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.
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
[cli11tutorial]: https://cliutils.github.io/CLI11/book
[releases]: https://github.com/CLIUtils/CLI11/releases
[api docs]: https://cliutils.github.io/CLI11
[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
[plumbum]: https://plumbum.readthedocs.io/en/latest/
[click]: https://click.palletsprojects.com

View File

@@ -0,0 +1,15 @@
# Summary
- [Introduction](/README.md)
- [Installation](/chapters/installation.md)
- [Basics](/chapters/basics.md)
- [Flags](/chapters/flags.md)
- [Options](/chapters/options.md)
- [Validators](/chapters/validators.md)
- [Subcommands and the App](/chapters/subcommands.md)
- [An advanced example](/chapters/an-advanced-example.md)
- [Configuration files](/chapters/config.md)
- [Formatting help output](/chapters/formatting.md)
- [Toolkits](/chapters/toolkits.md)
- [Advanced topics](/chapters/advanced-topics.md)
- [Internals](/chapters/internals.md)

12
libs/CLI11/book/book.json Normal file
View File

@@ -0,0 +1,12 @@
{
"title": "CLI11 Tutorial",
"description": "A set of examples and detailed information about CLI11",
"author": "Henry Schreiner",
"plugins": ["include-codeblock", "term", "hints"],
"pluginsConfig": {
"include-codeblock": {
"unindent": true,
"fixlang": true
}
}
}

View File

@@ -0,0 +1,152 @@
# 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->set_custom_option("COMPLEX", 2);
if(defaulted) {
std::stringstream out;
out << variable;
opt->set_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);
```
## 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. Here's how you could add
`boost::optional` for a compiler that does not have `__has_include`:
```cpp
// CLI11 already does this if __has_include is defined
#ifndef __has_include
#include <boost/optional.hpp>
// Use CLI namespace to avoid the conversion leaking into your other code
namespace CLI {
template <typename T> std::istringstream &operator>>(std::istringstream &in, boost::optional<T> &val) {
T v;
in >> v;
val = v;
return in;
}
}
#endif
#include <CLI11.hpp>
```
This is an example of how to use the system only; if you are just looking for a
way to activate `boost::optional` support on older compilers, you should define
`CLI11_BOOST_OPTIONAL` before including a CLI11 file, you'll get the
`boost::optional` support.
## 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.

View File

@@ -0,0 +1,39 @@
# Making a git clone
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:
[include:"Intro"](../code/geet.cpp)
Now, let's define the first subcommand, `add`, along with a few options:
[include:"Add"](../code/geet.cpp)
Now, let's add `commit`:
[include:"Commit"](../code/geet.cpp)
All that's need now is the parse call. We'll print a little message after the
code runs, and then return:
[include:"Parse"](../code/geet.cpp)
[Source code](https://github.com/CLIUtils/CLI11/tree/main/book/code/geet.cpp)
If you compile and run:
```term
gitbook:examples $ 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.

View File

@@ -0,0 +1,39 @@
# The Basics
The simplest CLI11 program looks like this:
[include](../code/simplest.cpp)
The first line includes the library; this explicitly uses the single file
edition (see [Selecting an edition](/chapters/installation)).
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 we have the
`CLI11.hpp` file in the current directory and that we are creating an output
executable `a.out` on a macOS or Linux system. The commands to compile and test
this example would be:
```term
gitbook:examples $ g++ -std=c++11 simplest.cpp
gitbook:examples $ ./a.out -h
Usage: ./a.out [OPTIONS]
Options:
-h,--help Print this help message and exit
```

View File

@@ -0,0 +1,340 @@
# Accepting configure files
## Reading a configure file
You can tell your app to allow configure files with `set_config("--config")`.
There are 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.
### 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
### 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.
## Configure file format
Here is an example configuration file, in
[TOML](https://github.com/toml-lang/toml) 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
[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.
## 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
`.config_to_str(default_also=false, write_description=false)`, where
`default_also` will also show any defaulted arguments, and `write_description`
will include option descriptions and the App description
```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."
```
### 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
char characterQuote = '\'';
/// the maximum number of layers to allow
uint8_t maximumLayers{255};
/// the separator used to separator parent layers
char parentSeparatorChar{'.'};
/// Specify the configuration index to use for arrayed sections
uint16_t configIndex{0};
/// 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 qChar)` :specify the characters
to use around strings and single characters
- `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 &sectionName)` : specify the section
name to use to get the option values, only this section will be processed
- `ConfigBase *index(uint16_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. Positional name
3. First short name
4. Environment name

View File

@@ -0,0 +1,167 @@
# Adding 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. By default, it will be allowed any number of times, but if you
explicitly\[^1\] request `->take_last(false)`, it will only be allowed once;
passing something like `./my_app -f -f` or `./my_app -ff` will throw a
`ParseError` with a nice help description. 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. An 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::size_t)`.
This could be useful for a version printout, etc.
```cpp
auto callback = [](int 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:
```term
gitbook $ ./my_app --fLaG
```
## Example
The following program will take several flags:
[include:"define"](../code/flags.cpp)
The values would be used like this:
[include:"usage"](../code/flags.cpp)
[Source code](https://github.com/CLIUtils/CLI11/tree/main/book/code/flags.cpp)
If you compile and run:
```term
gitbook:examples $ g++ -std=c++11 flags.cpp
gitbook:examples $ ./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
gitbook:examples $ ./a.out -bii --plain -i
The flags program
Bool flag passed
Flag int: 3
Flag plain: 1
```
\[^1\]: It will not inherit this from the parent defaults, since this is often
useful even if you don't want all options to allow multiple passed options.

View File

@@ -0,0 +1,86 @@
# Formatting help output
{% hint style='info' %} New in CLI11 1.6 {% endhint %}
## 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 | Both |
| `label(key, value)` | Set a label to a different value | 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)");
```
## 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
`Formatter.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)>
<make_positionals(app)>
<make_group(app, "Positionals", true, filter>
<make_groups(app, mode)>
<make_group(app, "Option Group 1"), false, filter>
<make_group(app, "Option Group 2"), false, filter>
...
<make_subcommands(app)>
<make_subcommand(sub1, Mode::Normal)>
<make_subcommand(sub2, Mode::Normal)>
<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:
- `*1`: This signature depends on whether the call is from a positional or
optional.
- `o` is opt pointer, `p` is true if positional.

View File

@@ -0,0 +1,114 @@
# 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.
## 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.4+ for your project (highly recommended), CLI11 comes with a
powerful CMakeLists.txt file that was designed to also be used with
`add_subproject`. 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.
### 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.
As an example of the build system, the following code will download and test
CLI11 in a simple Alpine Linux docker container [^1]:
```term
gitbook:~ $ docker run -it alpine
root:/ # apk add --no-cache g++ cmake make git
fetch ...
root:/ # git clone https://github.com/CLIUtils/CLI11.git
Cloning into 'CLI11' ...
root:/ # cd CLI11
root:CLI11 # mkdir build
root:CLI11 # cd build
root:build # cmake ..
-- The CXX compiler identification is GNU 6.3.0 ...
root:build # make
Scanning dependencies ...
root:build # make test
[warning]Running tests...
Test project /CLI11/build
Start 1: HelpersTest
1/10 Test #1: HelpersTest ...................... Passed 0.01 sec
Start 2: IniTest
2/10 Test #2: IniTest .......................... Passed 0.01 sec
Start 3: SimpleTest
3/10 Test #3: SimpleTest ....................... Passed 0.01 sec
Start 4: AppTest
4/10 Test #4: AppTest .......................... Passed 0.02 sec
Start 5: CreationTest
5/10 Test #5: CreationTest ..................... Passed 0.01 sec
Start 6: SubcommandTest
6/10 Test #6: SubcommandTest ................... Passed 0.01 sec
Start 7: HelpTest
7/10 Test #7: HelpTest ......................... Passed 0.01 sec
Start 8: NewParseTest
8/10 Test #8: NewParseTest ..................... Passed 0.01 sec
Start 9: TimerTest
9/10 Test #9: TimerTest ........................ Passed 0.24 sec
Start 10: link_test_2
10/10 Test #10: link_test_2 ...................... Passed 0.00 sec
100% tests passed, 0 tests failed out of 10
Total Test time (real) = 0.34 sec
```
For the curious, the CMake options and defaults are listed below. Most options
default to off if CLI11 is used as a subdirectory in another project.
| Option | Description |
| ----------------------------- | ----------------------------------------------------------------------------------------------- |
| `CLI11_SINGLE_FILE=ON` | Build the `CLI11.hpp` file from the sources. Requires Python (version 3 or 2.7). |
| `CLI11_SINGLE_FILE_TESTS=OFF` | Run the tests on the generated single file version as well |
| `CLI11_EXAMPLES=ON` | Build the example programs. |
| `CLI11_TESTING=ON` | Build the tests. |
| `CLI11_CLANG_TIDY=OFF` | Run `clang-tidy` on the examples and headers. Requires CMake 3.6+. |
| `CLI11_CLANG_TIDY_OPTIONS=""` | Options to pass to `clang-tidy`, such as `-fix` (single threaded build only if applying fixes!) |
[^1]:
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.

View File

@@ -0,0 +1,54 @@
# CLI11 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){
item = detail::lexical_cast<T>(value);
}
}
```
Obviously, you can't access `T` after the `add_` method is over, so it stores
the string representation of the default value if it receives the special `true`
value as the final argument (not shown above).
## 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_extra`: 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 (new in 1.7). This
ensures that interactions between options and subcommand options is consistent.
1. `_process_ini`: This reads an INI file and fills/changes options as needed.
2. `_process_env`: Look for environment variables.
3. `_process_callbacks`: Run the callback functions - this fills out the
variables.
4. `_process_help_flags`: Run help flags if present (and quit).
5. `_process_requirements`: Make sure needs/excludes, required number of options
present.
## Exceptions
The library immediately returns a C++ exception when it detects a problem, such
as an incorrect construction or a malformed command line.

View File

@@ -0,0 +1,459 @@
# 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, and decimal |
| 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:
```term
gitbook:examples $ ./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_iist`, `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_of_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. |
| `->group(name)` | The help group to put the option in. No effect for positional options. Defaults to `"Options"`. `"Hidden"` 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. |
| `->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 ';' |
| `->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`, 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](./validators.md) for more info |
| `->transform(Validator)` | Run a transforming validator on each value passed. See [Validators](./validators.md) 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 of the form `bool function(std::string)` that runs on every value that
the option receives, and returns a value that tells CLI11 whether the check
passed or failed.
## 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, argv, argc);
if(* opt)
std::cout << "Flag received " << opt->count() << " times." << std::endl;
```
## 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");
app.add_flag("--CaSeLeSs");
app.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.
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.
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.
## 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.

View File

@@ -0,0 +1,194 @@
# Subcommands and the App
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.set_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.
## 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.
## 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 failure message printer function
- Option defaults
- Allow extras
- Prefix command
- Ignore case
- Ignore underscore
- Allow Windows style options
- Fallthrough
- Group name
- Max required subcommands
- validate positional arguments
- validate optional arguments
## 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:
```term
gitbook:code $ ./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.
### 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.
### 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 legitamately applied to more
than 1 positional argument. In this context enabling `positional_validation` 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 go into the `extra_args` field 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.

View File

@@ -0,0 +1,40 @@
# Using CLI11 in a Toolkit
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, argv, argc) \
try { \
app.parse(argv, argc); \
} 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.

View File

@@ -0,0 +1,66 @@
# 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 the
modified version of the string. If there is an error, the function should throw
a `CLI::ValidationError` with the appropriate reason as a message.
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 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.
An example of a custom validator:
```cpp
struct LowerCaseValidator : public 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. |
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 (uses `description_` instead 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 |

View File

@@ -0,0 +1,32 @@
cmake_minimum_required(VERSION 3.11)
project(CLI11_Examples LANGUAGES CXX)
# Using CMake 3.11's 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")

View File

@@ -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]
}

View File

@@ -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]
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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}

View File

@@ -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()

View File

@@ -0,0 +1,3 @@
configure_file("cmake/CLI11.pc.in" "CLI11.pc" @ONLY)
install(FILES "${PROJECT_BINARY_DIR}/CLI11.pc" DESTINATION "${CMAKE_INSTALL_DATADIR}/pkgconfig")

View File

@@ -0,0 +1,37 @@
# 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
-Wimplicit-atomic-properties
-Wmissing-declarations
-Woverlength-strings
-Wshadow
-Wstrict-selector-match
-Wundeclared-selector)
# -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>>)
if(NOT CMAKE_VERSION VERSION_LESS 3.13)
target_link_options(CLI11_warnings INTERFACE $<$<BOOL:${CLI11_FORCE_LIBCXX}>:-stdlib=libc++>)
endif()

View File

@@ -0,0 +1,243 @@
# 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
#
#
# USAGE:
#
# 1. Copy this file into your cmake modules path.
#
# 2. Add the following line to your CMakeLists.txt:
# include(CodeCoverage)
#
# 3. Append necessary compiler flags:
# APPEND_COVERAGE_COMPILER_FLAGS()
#
# 4. If you need to exclude additional directories from the report, specify them
# using the COVERAGE_EXCLUDES variable before calling SETUP_TARGET_FOR_COVERAGE.
# Example:
# set(COVERAGE_EXCLUDES 'dir1/*' '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)
# Check prereqs
find_program(GCOV_PATH gcov)
find_program(LCOV_PATH NAMES lcov lcov.bat lcov.exe lcov.perl)
find_program(GENHTML_PATH NAMES genhtml genhtml.perl genhtml.bat)
find_program(GCOVR_PATH gcovr PATHS ${CMAKE_SOURCE_DIR}/scripts/test)
find_program(SIMPLE_PYTHON_EXECUTABLE python)
if(NOT GCOV_PATH)
message(FATAL_ERROR "gcov not found! Aborting...")
endif() # NOT GCOV_PATH
if("${CMAKE_CXX_COMPILER_ID}" MATCHES "(Apple)?[Cc]lang")
if("${CMAKE_CXX_COMPILER_VERSION}" VERSION_LESS 3)
message(FATAL_ERROR "Clang version must be 3.0.0 or greater! Aborting...")
endif()
elseif(NOT CMAKE_COMPILER_IS_GNUCXX)
message(FATAL_ERROR "Compiler is not GNU gcc! Aborting...")
endif()
set(COVERAGE_COMPILER_FLAGS
"-g -O0 --coverage -fprofile-arcs -ftest-coverage -fno-inline -fno-inline-small-functions -fno-default-inline"
CACHE INTERNAL "")
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_CXX_FLAGS_COVERAGE CMAKE_C_FLAGS_COVERAGE CMAKE_EXE_LINKER_FLAGS_COVERAGE
CMAKE_SHARED_LINKER_FLAGS_COVERAGE)
if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
message(WARNING "Code coverage results with an optimised (non-Debug) build may be misleading")
endif() # NOT CMAKE_BUILD_TYPE STREQUAL "Debug"
if(CMAKE_C_COMPILER_ID STREQUAL "GNU")
link_libraries(gcov)
else()
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --coverage")
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(
# NAME testrunner_coverage # New target name
# EXECUTABLE testrunner -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR
# DEPENDENCIES testrunner # Dependencies to build first
# )
function(SETUP_TARGET_FOR_COVERAGE)
set(options NONE)
set(oneValueArgs NAME)
set(multiValueArgs EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES)
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
# Setup target
add_custom_target(
${Coverage_NAME}
# Cleanup lcov
COMMAND ${LCOV_PATH} --directory . --zerocounters
# Create baseline to make sure untouched files show up in the report
COMMAND ${LCOV_PATH} -c -i -d . -o ${Coverage_NAME}.base
# Run tests
COMMAND ${Coverage_EXECUTABLE}
# Capturing lcov counters and generating report
COMMAND ${LCOV_PATH} --directory . --capture --output-file ${Coverage_NAME}.info
# add baseline counters
COMMAND ${LCOV_PATH} -a ${Coverage_NAME}.base -a ${Coverage_NAME}.info --output-file
${Coverage_NAME}.total
COMMAND ${LCOV_PATH} --remove ${Coverage_NAME}.total ${COVERAGE_EXCLUDES} --output-file
${PROJECT_BINARY_DIR}/${Coverage_NAME}.info.cleaned
COMMAND ${GENHTML_PATH} -o ${Coverage_NAME} ${PROJECT_BINARY_DIR}/${Coverage_NAME}.info.cleaned
COMMAND ${CMAKE_COMMAND} -E remove ${Coverage_NAME}.base ${Coverage_NAME}.total
${PROJECT_BINARY_DIR}/${Coverage_NAME}.info.cleaned
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
DEPENDS ${Coverage_DEPENDENCIES}
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 ;
COMMENT "Lcov code coverage info report saved in ${Coverage_NAME}.info.")
# 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
# 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_COBERTURA(
# NAME ctest_coverage # New target name
# EXECUTABLE ctest -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR
# DEPENDENCIES executable_target # Dependencies to build first
# )
function(SETUP_TARGET_FOR_COVERAGE_COBERTURA)
set(options NONE)
set(oneValueArgs NAME)
set(multiValueArgs EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES)
cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
if(NOT SIMPLE_PYTHON_EXECUTABLE)
message(FATAL_ERROR "python not found! Aborting...")
endif() # NOT SIMPLE_PYTHON_EXECUTABLE
if(NOT GCOVR_PATH)
message(FATAL_ERROR "gcovr not found! Aborting...")
endif() # NOT GCOVR_PATH
# Combine excludes to several -e arguments
set(COBERTURA_EXCLUDES "")
foreach(EXCLUDE ${COVERAGE_EXCLUDES})
set(COBERTURA_EXCLUDES "-e ${EXCLUDE} ${COBERTURA_EXCLUDES}")
endforeach()
add_custom_target(
${Coverage_NAME}
# Run tests
${Coverage_EXECUTABLE}
# Running gcovr
COMMAND ${GCOVR_PATH} -x -r ${CMAKE_SOURCE_DIR} ${COBERTURA_EXCLUDES} -o ${Coverage_NAME}.xml
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
DEPENDS ${Coverage_DEPENDENCIES}
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_COBERTURA
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)
message(STATUS "Appending code coverage compiler flags: ${COVERAGE_COMPILER_FLAGS}")
endfunction() # APPEND_COVERAGE_COMPILER_FLAGS

49
libs/CLI11/conanfile.py Normal file
View File

@@ -0,0 +1,49 @@
from conans import ConanFile, CMake
from conans.tools import load, cross_building
import re
def get_version():
try:
content = load("include/CLI/Version.hpp")
version = re.search(r'#define CLI11_VERSION "(.*)"', content).group(1)
return version
except Exception:
return None
class CLI11Conan(ConanFile):
name = "CLI11"
version = get_version()
description = "Command Line Interface toolkit for C++11"
topics = ("cli", "c++11", "parser", "cli11")
url = "https://github.com/CLIUtils/CLI11"
homepage = "https://github.com/CLIUtils/CLI11"
author = "Henry Schreiner <hschrein@cern.ch>"
license = "BSD-3-Clause"
settings = "os", "compiler", "arch", "build_type"
exports_sources = (
"LICENSE",
"README.md",
"include/*",
"src/*",
"extern/*",
"cmake/*",
"CMakeLists.txt",
"CLI11.CPack.Description.txt",
"tests/*",
)
def build(self): # this is not building a library, just tests
cmake = CMake(self)
cmake.definitions["CLI11_BUILD_EXAMPLES"] = "OFF"
cmake.definitions["CLI11_SINGLE_FILE"] = "OFF"
cmake.configure()
cmake.build()
if not cross_building(self.settings):
cmake.test()
cmake.install()
def package_id(self):
self.info.header_only()

2
libs/CLI11/docs/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
/html/*
/latex/*

114
libs/CLI11/docs/CLI11.svg Normal file
View File

@@ -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

View File

@@ -0,0 +1,11 @@
set(DOXYGEN_EXTRACT_ALL YES)
set(DOXYGEN_BUILTIN_STL_SUPPORT YES)
set(PROJECT_BRIEF "C++11 Command Line Interface Parser")
file(
GLOB DOC_LIST
RELATIVE "${PROJECT_SOURCE_DIR}/include"
"${PROJECT_SOURCE_DIR}/include/CLI/*.hpp")
doxygen_add_docs(docs ${DOC_LIST} "${CMAKE_CURRENT_SOURCE_DIR}/mainpage.md"
WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}/include")

2475
libs/CLI11/docs/Doxyfile Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,24 @@
# Introduction {#mainpage}
This is the Doxygen API documentation for CLI11 parser. There is a friendly
introduction to CLI11 on the [GitHub page](https://github.com/CLIUtils/CLI11),
and [a tutorial series](https://cliutils.github.io/CLI11/book/).
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() |

View File

@@ -0,0 +1,251 @@
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++)
endif()
endfunction()
if(CLI11_BUILD_EXAMPLES_JSON)
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_GetProperties(json)
if(NOT json_POPULATED)
FetchContent_Populate(json)
endif()
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 "This is a timer:" "--file is required"
"Run with --help for more information.")
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 "This is a timer:"
"Working on file: this, direct count: 1, opt count: 1"
"Working on count: 2, direct count: 2, opt count: 2" "Some value: 1.2")
# 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,--file TEXT REQUIRED" "-d,--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"
"is required")
add_test(NAME option_groups_extra COMMAND option_groups --csv --binary)
set_property(TEST option_groups_extra PROPERTY PASS_REGULAR_EXPRESSION "and 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")
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" "circle4"
"rectangle2 with edges [2.1,2.1]" "triangel1 with sides [4.5]")
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,--file TEXT:FILE[\\r\\n\\t ]+File name"
" -v,--value INT:INT in [3 - 6][\\r\\n\\t ]+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 to 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 "This is a timer:" "--file is required"
"Run with --help for more information.")
add_test(NAME groups_all COMMAND groups --file this --count --count -d 1.2)
set_property(
TEST groups_all
PROPERTY PASS_REGULAR_EXPRESSION "This is a timer:"
"Working on file: this, direct count: 1, opt count: 1"
"Working on count: 2, direct count: 2, opt count: 2" "Some value: 1.2")
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")
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")
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_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")
#--------------------------------------------
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")
#------------------------------------------------
# This executable is for manual testing and is expected to change regularly
add_cli_exe(tester testEXE.cpp)

View File

@@ -0,0 +1,28 @@
// Copyright (c) 2017-2022, 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';
}

View File

@@ -0,0 +1,50 @@
// Copyright (c) 2017-2022, 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() << std::endl;
std::cout << "Working on count: " << count << ", direct count: " << app.count("--count")
<< ", opt count: " << copt->count() << std::endl;
std::cout << "Received flag: " << v << " (" << flag->count() << ") times\n";
std::cout << "Some value: " << value << std::endl;
return 0;
}

View File

@@ -0,0 +1,39 @@
// Copyright (c) 2017-2022, 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>
// 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 << std::endl;
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;
}

View File

@@ -0,0 +1,21 @@
// Copyright (c) 2017-2022, 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 << std::endl;
return 0;
}

Some files were not shown because too many files have changed in this diff Show More