├── src
├── main.cpp
└── ClaraTests.cpp
├── codecov.yml
├── .gitignore
├── misc
├── appveyorTestRunScript.bat
├── appveyorMergeCoverageScript.py
├── CMakeLists.txt
├── appveyorBuildConfigurationScript.bat
├── installOpenCppCoverage.ps1
└── coverage-helper.cpp
├── docs
└── release-notes.md
├── .gitattributes
├── scripts
├── embedTextFlow.py
├── embed.py
├── release.py
└── stitch.py
├── CMake
├── llvm-cov-wrapper
├── FindGcov.cmake
├── Findcodecov.cmake
└── FindLcov.cmake
├── CMakeLists.txt
├── LICENSE.txt
├── appveyor.yml
├── Roadmap.md
├── CODE_OF_CONDUCT.md
├── README.md
├── .travis.yml
├── third_party
└── TextFlow.hpp
├── include
├── clara_textflow.hpp
└── clara.hpp
└── single_include
└── clara.hpp
/src/main.cpp:
--------------------------------------------------------------------------------
1 | #define CATCH_CONFIG_MAIN
2 | #include "catch.hpp"
3 |
--------------------------------------------------------------------------------
/codecov.yml:
--------------------------------------------------------------------------------
1 | codecov:
2 | branch: master
3 |
4 | coverage:
5 | ignore:
6 | - "src/*"
7 | - "third_party/catch.hpp"
8 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/*.iml
2 | .idea/misc.xml
3 | .idea/modules.xml
4 | .idea/vcs.xml
5 | .idea/workspace.xml
6 | .idea/.name
7 | .idea/dictionaries/*
8 | cmake-build-*
9 | .vs
10 | *.pyc
11 | .idea/markdown-navigator.xml
12 | .idea/codeStyles/Project.xml
13 | .idea/markdown-navigator/profiles_settings.xml
14 |
--------------------------------------------------------------------------------
/misc/appveyorTestRunScript.bat:
--------------------------------------------------------------------------------
1 | cd Build
2 | if "%CONFIGURATION%"=="Debug" (
3 | ctest -j 2 -C %CONFIGURATION% -D ExperimentalMemCheck
4 | python ..\misc\appveyorMergeCoverageScript.py
5 | codecov --root .. --no-color --disable gcov -f cobertura.xml -t %CODECOV_TOKEN%
6 | )
7 | if "%CONFIGURATION%"=="Release" (
8 | ctest -j 2 -C %CONFIGURATION%
9 | )
10 |
--------------------------------------------------------------------------------
/misc/appveyorMergeCoverageScript.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python2
2 |
3 | import glob
4 | import subprocess
5 |
6 | if __name__ == '__main__':
7 | cov_files = list(glob.glob('cov-report*.bin'))
8 | base_cmd = ['OpenCppCoverage', '--quiet', '--export_type=cobertura:cobertura.xml'] + ['--input_coverage={}'.format(f) for f in cov_files]
9 | subprocess.call(base_cmd)
10 |
--------------------------------------------------------------------------------
/docs/release-notes.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # 1.1.3
4 |
5 | ## Improvements
6 | * `Args` now take arguments as `int argc, char const * const * argv`.
7 | * This allows the use of string literals for arguments
8 |
9 |
10 | # 1.1.2
11 | * Fix usage of `dynamic_cast` preventing Clara being used with `-fno-rtti`
12 |
13 |
14 | # Older versions (1.1.1 and earlier)
15 |
16 | No release notes have been kept (Maybe some of it will be backfilled later)
17 |
--------------------------------------------------------------------------------
/misc/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.0)
2 |
3 | project(ClaraCoverageHelper)
4 |
5 | add_executable(CoverageHelper coverage-helper.cpp)
6 | set_property(TARGET CoverageHelper PROPERTY CXX_STANDARD 11)
7 | set_property(TARGET CoverageHelper PROPERTY CXX_STANDARD_REQUIRED ON)
8 | set_property(TARGET CoverageHelper PROPERTY CXX_EXTENSIONS OFF)
9 | if (MSVC)
10 | target_compile_options( CoverageHelper PRIVATE /W4 /w44265 /w44061 /w44062 )
11 | endif()
12 |
--------------------------------------------------------------------------------
/misc/appveyorBuildConfigurationScript.bat:
--------------------------------------------------------------------------------
1 | @REM # In debug build, we want to prebuild memcheck redirecter
2 | @REM # before running the tests
3 | if "%CONFIGURATION%"=="Debug" (
4 | cmake -Hmisc -Bbuild-misc -A%PLATFORM%
5 | cmake --build build-misc
6 | cmake -H. -BBuild -A%PLATFORM% -DMEMORYCHECK_COMMAND=build-misc\Debug\CoverageHelper.exe -DMEMORYCHECK_COMMAND_OPTIONS=--sep-- -DMEMORYCHECK_TYPE=Valgrind
7 | )
8 | if "%CONFIGURATION%"=="Release" (
9 | cmake -H. -BBuild -A%PLATFORM%
10 | )
11 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # This sets the default behaviour, overriding core.autocrlf
2 | * text=auto
3 |
4 | # All source files should have unix line-endings in the repository,
5 | # but convert to native line-endings on checkout
6 | *.cpp text
7 | *.h text
8 | *.hpp text
9 |
10 | # Windows specific files should retain windows line-endings
11 | *.sln text eol=crlf
12 |
13 | # Keep executable scripts with LFs so they can be run after being
14 | # checked out on Windows
15 | *.py text eol=lf
16 |
17 |
18 | # Keep the single include header with LFs to make sure it is uploaded,
19 | # hashed etc with LF
20 | single_include/*.hpp eol=lf
21 | # Also keep the LICENCE file with LFs for the same reason
22 | LICENCE.txt eol=lf
23 |
--------------------------------------------------------------------------------
/scripts/embedTextFlow.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | # Execute this script any time you import a new copy of textflow into the third_party area
4 | import os
5 | import sys
6 | import embed
7 |
8 | rootPath = os.path.dirname(os.path.realpath( os.path.dirname(sys.argv[0])))
9 |
10 | filename = os.path.join( rootPath, "third_party", "TextFlow.hpp" )
11 | outfilename = os.path.join( rootPath, "include", "clara_textflow.hpp" )
12 |
13 |
14 | # Mapping of pre-processor identifiers
15 | idMap = {
16 | "TEXTFLOW_HPP_INCLUDED": "CLARA_TEXTFLOW_HPP_INCLUDED",
17 | "TEXTFLOW_CONFIG_CONSOLE_WIDTH": "CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH"
18 | }
19 |
20 | # outer namespace to add
21 | outerNamespace = "clara"
22 |
23 | mapper = embed.LineMapper( idMap, outerNamespace )
24 | mapper.mapFile( filename, outfilename )
--------------------------------------------------------------------------------
/misc/installOpenCppCoverage.ps1:
--------------------------------------------------------------------------------
1 | # Downloads are done from the oficial github release page links
2 | $downloadUrl = "https://github.com/OpenCppCoverage/OpenCppCoverage/releases/download/release-0.9.6.1/OpenCppCoverageSetup-x64-0.9.6.1.exe"
3 | $installerPath = [System.IO.Path]::Combine($Env:USERPROFILE, "Downloads", "OpenCppCoverageSetup.exe")
4 |
5 | if(-Not (Test-Path $installerPath)) {
6 | Write-Host -ForegroundColor White ("Downloading OpenCppCoverage from: " + $downloadUrl)
7 | Start-FileDownload $downloadUrl -FileName $installerPath
8 | }
9 |
10 | Write-Host -ForegroundColor White "About to install OpenCppCoverage..."
11 |
12 | $installProcess = (Start-Process $installerPath -ArgumentList '/VERYSILENT' -PassThru -Wait)
13 | if($installProcess.ExitCode -ne 0) {
14 | throw [System.String]::Format("Failed to install OpenCppCoverage, ExitCode: {0}.", $installProcess.ExitCode)
15 | }
16 |
17 | # Assume standard, boring, installation path of ".../Program Files/OpenCppCoverage"
18 | $installPath = [System.IO.Path]::Combine(${Env:ProgramFiles}, "OpenCppCoverage")
19 | $env:Path="$env:Path;$installPath"
20 |
--------------------------------------------------------------------------------
/CMake/llvm-cov-wrapper:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | # This file is part of CMake-codecov.
4 | #
5 | # Copyright (c)
6 | # 2015-2017 RWTH Aachen University, Federal Republic of Germany
7 | #
8 | # See the LICENSE file in the package base directory for details
9 | #
10 | # Written by Alexander Haase, alexander.haase@rwth-aachen.de
11 | #
12 |
13 | if [ -z "$LLVM_COV_BIN" ]
14 | then
15 | echo "LLVM_COV_BIN not set!" >& 2
16 | exit 1
17 | fi
18 |
19 |
20 | # Get LLVM version to find out.
21 | LLVM_VERSION=$($LLVM_COV_BIN -version | grep -i "LLVM version" \
22 | | sed "s/^\([A-Za-z ]*\)\([0-9]\).\([0-9]\).*$/\2.\3/g")
23 |
24 | if [ "$1" = "-v" ]
25 | then
26 | echo "llvm-cov-wrapper $LLVM_VERSION"
27 | exit 0
28 | fi
29 |
30 |
31 | if [ -n "$LLVM_VERSION" ]
32 | then
33 | MAJOR=$(echo $LLVM_VERSION | cut -d'.' -f1)
34 | MINOR=$(echo $LLVM_VERSION | cut -d'.' -f2)
35 |
36 | if [ $MAJOR -eq 3 ] && [ $MINOR -le 4 ]
37 | then
38 | if [ -f "$1" ]
39 | then
40 | filename=$(basename "$1")
41 | extension="${filename##*.}"
42 |
43 | case "$extension" in
44 | "gcno") exec $LLVM_COV_BIN --gcno="$1" ;;
45 | "gcda") exec $LLVM_COV_BIN --gcda="$1" ;;
46 | esac
47 | fi
48 | fi
49 |
50 | if [ $MAJOR -eq 3 ] && [ $MINOR -le 5 ]
51 | then
52 | exec $LLVM_COV_BIN $@
53 | fi
54 | fi
55 |
56 | exec $LLVM_COV_BIN gcov $@
57 |
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.8.2)
2 | project(Clara)
3 |
4 | set(SOURCE_FILES src/main.cpp src/ClaraTests.cpp include/clara.hpp)
5 | include_directories( include third_party )
6 | add_executable(ClaraTests ${SOURCE_FILES})
7 |
8 | if(USE_CPP14)
9 | set_property(TARGET ClaraTests PROPERTY CXX_STANDARD 14)
10 | message(STATUS "Enabled C++14")
11 | elseif(USE_CPP17)
12 | set_property(TARGET ClaraTests PROPERTY CXX_STANDARD 17)
13 | message(STATUS "Enabled C++17")
14 | else(USE_CPP11)
15 | set_property(TARGET ClaraTests PROPERTY CXX_STANDARD 11)
16 | message(STATUS "Enabled C++11")
17 | endif()
18 |
19 | set_property(TARGET ClaraTests PROPERTY CXX_STANDARD_REQUIRED ON)
20 | set_property(TARGET ClaraTests PROPERTY CXX_EXTENSIONS OFF)
21 |
22 |
23 | if( CMAKE_CXX_COMPILER_ID MATCHES "Clang|AppleClang|GNU" )
24 | target_compile_options( ClaraTests PRIVATE -Wall -Wextra -pedantic -Werror )
25 | endif()
26 | if( CMAKE_CXX_COMPILER_ID MATCHES "MSVC" )
27 | target_compile_options( ClaraTests PRIVATE /W4 /WX )
28 | endif()
29 |
30 | if (ENABLE_COVERAGE)
31 | list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/CMake")
32 | find_package(codecov)
33 | add_coverage(ClaraTests)
34 | list(APPEND LCOV_REMOVE_PATTERNS "/usr/")
35 | coverage_evaluate()
36 | endif()
37 |
38 | include(CTest)
39 | add_test(NAME RunTests COMMAND $)
40 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Boost Software License - Version 1.0 - August 17th, 2003
2 |
3 | Permission is hereby granted, free of charge, to any person or organization
4 | obtaining a copy of the software and accompanying documentation covered by
5 | this license (the "Software") to use, reproduce, display, distribute,
6 | execute, and transmit the Software, and to prepare derivative works of the
7 | Software, and to permit third-parties to whom the Software is furnished to
8 | do so, all subject to the following:
9 |
10 | The copyright notices in the Software and this entire statement, including
11 | the above license grant, this restriction and the following disclaimer,
12 | must be included in all copies of the Software, in whole or in part, and
13 | all derivative works of the Software, unless such copies or derivative
14 | works are solely in the form of machine-executable object code generated by
15 | a source language processor.
16 |
17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
20 | SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
21 | FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
22 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23 | DEALINGS IN THE SOFTWARE.
24 |
--------------------------------------------------------------------------------
/appveyor.yml:
--------------------------------------------------------------------------------
1 | version: "{build}"
2 |
3 | branches:
4 | except:
5 | - /dev-travis.+/
6 |
7 | os:
8 | - Visual Studio 2017
9 | - Visual Studio 2015
10 |
11 | environment:
12 | matrix:
13 | - additional_flags: "/permissive- /std:c++latest"
14 | - additional_flags: ""
15 |
16 | matrix:
17 | exclude:
18 | - os: Visual Studio 2015
19 | additional_flags: "/permissive- /std:c++latest"
20 |
21 | init:
22 | - git config --global core.autocrlf input
23 |
24 | install:
25 | - ps: if (($env:CONFIGURATION) -eq "Debug" ) { python -m pip --disable-pip-version-check install codecov }
26 | - ps: if (($env:CONFIGURATION) -eq "Debug" ) { .\misc\installOpenCppCoverage.ps1 }
27 |
28 | # Win32 and x64 are CMake-compatible solution platform names.
29 | # This allows us to pass %PLATFORM% to CMake -A.
30 | platform:
31 | - Win32
32 | - x64
33 |
34 | # build Configurations, i.e. Debug, Release, etc.
35 | configuration:
36 | - Debug
37 | - Release
38 |
39 | #Cmake will autodetect the compiler, but we set the arch
40 | before_build:
41 | - set CXXFLAGS=%additional_flags%
42 | # Indirection because appveyor doesn't handle multiline batch scripts properly
43 | # https://stackoverflow.com/questions/37627248/how-to-split-a-command-over-multiple-lines-in-appveyor-yml/37647169#37647169
44 | # https://help.appveyor.com/discussions/questions/3888-multi-line-cmd-or-powershell-warning-ignore
45 | - cmd: .\misc\appveyorBuildConfigurationScript.bat
46 |
47 |
48 | # build with MSBuild
49 | build:
50 | project: Build\Clara.sln # path to Visual Studio solution or project
51 | parallel: true # enable MSBuild parallel builds
52 | verbosity: normal # MSBuild verbosity level {quiet|minimal|normal|detailed}
53 |
54 | test_script:
55 | - set CTEST_OUTPUT_ON_FAILURE=1
56 | - cmd: .\misc\appveyorTestRunScript.bat
57 |
--------------------------------------------------------------------------------
/Roadmap.md:
--------------------------------------------------------------------------------
1 | # Roadmap
2 |
3 | ## June 2019 Update
4 |
5 | Previously this roadmap said, "I'm not quite ready to throw development fully open to the community effort". The intention had been to clarify the direction in the codebase in some key areas, as many of the PRs were taking it in different directions.
6 |
7 | Unfortunuately the time intended for working on this has not appeared and this project has languished in neglect as a result. I should have acknowledged that sooner, but as of now I (@philsquared), am opening this up fully to the community (anyone who is still interested). That means, of course, that maintainers such as @horenmar, will be able to merge PRs as they see fit (as well as make any other changes). It also means that we'd be keen to hear from anyone that wants to take the lead in maintaining and evolving the library.
8 |
9 | Personally, I still hope to contribute further to the project, but am no longer in a position I can commit to leading it.
10 |
11 | Here are the items I had on the original roadmap, for reference. It is not a hard requirement to follow them:
12 |
13 | - Add more documentation (includes [#49](https://github.com/catchorg/Clara/issues/49))
14 | - Finish work on "required" parsers [#39](https://github.com/catchorg/Clara/issues/39), [#16](https://github.com/catchorg/Clara/issues/16) & [PR #28](https://github.com/catchorg/Clara/pull/28)
15 | - Finish work on removing exceptions completely (or making them "optional")
16 | - (compiler conditional) Support for `std::optional` and maybe other optional types (e.g. boost) [#4](https://github.com/catchorg/Clara/issues/4) & [PR #45](https://github.com/catchorg/Clara/pull/45) - this is mostly, if not entirely, implemented. Should be checked, and #4 closed if so.
17 | - Arg\[0] support on Windows [#29](https://github.com/catchorg/Clara/issues/29)
18 | - config files and environment variables
19 | - "hidden" options [#29](https://github.com/catchorg/Clara/issues/29)
20 | - Capture general text for help output [#48]((https://github.com/catchorg/Clara/issues/48)
21 | - subcommands [#31](https://github.com/catchorg/Clara/issues/31) (I actually have a design mostly done for this) - but see also [PR #32](https://github.com/catchorg/Clara/pull/32)
22 |
23 | ---
24 |
25 |
--------------------------------------------------------------------------------
/scripts/embed.py:
--------------------------------------------------------------------------------
1 | import re
2 |
3 | preprocessorRe = re.compile( r'\s*#.*' )
4 |
5 | fdefineRe = re.compile( r'\s*#\s*define\s*(\S*)\s*\(' ) # #defines that take arguments
6 | defineRe = re.compile( r'\s*#\s*define\s*(\S*)\s+(\S*)' ) # all #defines
7 | undefRe = re.compile( r'\s*#\s*undef\s*(\S*)' ) # all #undefs
8 |
9 | ifdefCommonRe = re.compile( r'\s*#\s*if' ) # all #ifdefs
10 | ifdefRe = re.compile( r'\s*#\s*ifdef\s*(\S*)' )
11 | ifndefRe = re.compile( r'\s*#\s*ifndef\s*(\S*)' )
12 | endifRe = re.compile( r'\s*#\s*endif\s*//\s*(.*)' )
13 | elseRe = re.compile( r'\s*#\s*else' )
14 | ifRe = re.compile( r'\s*#\s*if\s+(.*)' )
15 |
16 | nsRe = re.compile( r'(\s*\s*namespace\s+)(.+)(\s*{?)' )
17 | nsCloseRe = re.compile( r'(\s*})(\s*\/\/\s*namespace\s+)(.+)(\s*{?)' )
18 |
19 |
20 | class LineMapper:
21 | def __init__( self, idMap, outerNamespace ):
22 | self.idMap = idMap
23 | self.outerNamespace = outerNamespace
24 |
25 | def replaceId( self, lineNo, id ):
26 | if not id in self.idMap:
27 | raise ValueError( "Unrecognised macro identifier: '{0}' on line: {1}".format( id, lineNo ) )
28 | subst = self.idMap[id]
29 | if subst == "":
30 | return id
31 | else:
32 | return subst
33 |
34 | # TBD:
35 | # #if, #ifdef, comments after #else
36 | def mapLine( self, lineNo, line ):
37 | m = ifndefRe.match( line )
38 | if m:
39 | return "#ifndef " + self.replaceId( lineNo, m.group(1)) + "\n"
40 | m = defineRe.match( line )
41 | if m:
42 | return "#define " + self.replaceId( lineNo, m.group(1)) + "\n"
43 | m = endifRe.match( line )
44 | if m:
45 | return "#endif // " + self.replaceId( lineNo, m.group(1)) + "\n"
46 | m = nsRe.match( line )
47 | if m:
48 | return "{0}{1} {{ namespace {2}{3}".format( m.group(1), self.outerNamespace, m.group(2), m.group(3))
49 | m = nsCloseRe.match( line )
50 | if m:
51 | return "{0}}}{1}{2}::{3}{4}".format( m.group(1), m.group(2), self.outerNamespace, m.group(3), m.group(4))
52 | return line
53 |
54 | def mapFile(self, filenameIn, filenameOut ):
55 | print( "Embedding:\n {0}\nas:\n {1}".format( filenameIn, filenameOut ) )
56 | with open( filenameIn, 'r' ) as f, open( filenameOut, 'w' ) as outf:
57 | lineNo = 1
58 | for line in f:
59 | outf.write( self.mapLine( lineNo, line ) )
60 | lineNo = lineNo + 1
61 | print( "Written {0} lines".format( lineNo ) )
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
6 |
7 | ## Our Standards
8 |
9 | Examples of behavior that contributes to creating a positive environment include:
10 |
11 | * Using welcoming and inclusive language
12 | * Being respectful of differing viewpoints and experiences
13 | * Gracefully accepting constructive criticism
14 | * Focusing on what is best for the community
15 | * Showing empathy towards other community members
16 |
17 | Examples of unacceptable behavior by participants include:
18 |
19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances
20 | * Trolling, insulting/derogatory comments, and personal or political attacks
21 | * Public or private harassment
22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission
23 | * Other conduct which could reasonably be considered inappropriate in a professional setting
24 |
25 | ## Our Responsibilities
26 |
27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
28 |
29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
30 |
31 | ## Scope
32 |
33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
34 |
35 | ## Enforcement
36 |
37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at clara@philnash.me. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
38 |
39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
40 |
41 | ## Attribution
42 |
43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
44 |
45 | [homepage]: http://contributor-covenant.org
46 | [version]: http://contributor-covenant.org/version/1/4/
47 |
--------------------------------------------------------------------------------
/misc/coverage-helper.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 |
13 |
14 | void create_empty_file(std::string const& path) {
15 | std::ofstream ofs(path);
16 | ofs << '\n';
17 | }
18 |
19 | const std::string separator = "--sep--";
20 | const std::string logfile_prefix = "--log-file=";
21 | const std::string project_name = "Clara";
22 |
23 | std::string to_lower(std::string in) {
24 | for (auto& c : in) {
25 | c = std::tolower(c);
26 | }
27 | return in;
28 | }
29 |
30 | bool starts_with(std::string const& str, std::string const& pref) {
31 | return str.find(pref) == 0;
32 | }
33 |
34 | int parse_log_file_arg(std::string const& arg) {
35 | assert(starts_with(arg, logfile_prefix) && "Attempting to parse incorrect arg!");
36 | auto fname = arg.substr(logfile_prefix.size());
37 | create_empty_file(fname);
38 | std::regex regex("MemoryChecker\\.(\\d+)\\.log", std::regex::icase);
39 | std::smatch match;
40 | if (std::regex_search(fname, match, regex)) {
41 | return std::stoi(match[1]);
42 | } else {
43 | throw std::domain_error("Couldn't find desired expression in string: " + fname);
44 | }
45 | }
46 |
47 | std::string project_path(std::string path) {
48 | auto start = path.find(project_name);
49 | if (start == std::string::npos) {
50 | start = path.find(to_lower(project_name));
51 | }
52 | if (start == std::string::npos) {
53 | throw std::domain_error("Couldn't find project's base path");
54 | }
55 | auto end = path.find_first_of("\\/", start);
56 | return path.substr(0, end);
57 | }
58 |
59 | std::string windowsify_path(std::string path) {
60 | for (auto& c : path) {
61 | if (c == '/') {
62 | c = '\\';
63 | }
64 | }
65 | return path;
66 | }
67 |
68 | void exec_cmd(std::string const& cmd, int log_num, std::string const& path) {
69 | std::array buffer;
70 | #if defined(_WIN32)
71 | auto real_cmd = "OpenCppCoverage --export_type binary:cov-report" + std::to_string(log_num)
72 | + ".bin --quiet " + "--sources " + path + " --cover_children -- " + cmd;
73 | std::cout << "=== Marker ===: Cmd: " << real_cmd << '\n';
74 | std::shared_ptr pipe(_popen(real_cmd.c_str(), "r"), _pclose);
75 | #else // Just for testing, in the real world we will always work under WIN32
76 | (void)log_num; (void)path;
77 | std::shared_ptr pipe(popen(cmd.c_str(), "r"), pclose);
78 | #endif
79 |
80 | if (!pipe) {
81 | throw std::runtime_error("popen() failed!");
82 | }
83 | while (!feof(pipe.get())) {
84 | if (fgets(buffer.data(), 128, pipe.get()) != nullptr) {
85 | std::cout << buffer.data();
86 | }
87 | }
88 | }
89 |
90 | // argv should be:
91 | // [0]: our path
92 | // [1]: "--log-file="
93 | // [2]: "--sep--"
94 | // [3]+: the actual command
95 |
96 | int main(int argc, char** argv) {
97 | std::vector args(argv, argv + argc);
98 | auto sep = std::find(begin(args), end(args), separator);
99 | assert(sep - begin(args) == 2 && "Structure differs from expected!");
100 |
101 | auto num = parse_log_file_arg(args[1]);
102 |
103 | auto cmdline = std::accumulate(++sep, end(args), std::string{}, [] (const std::string& lhs, const std::string& rhs) {
104 | return lhs + ' ' + rhs;
105 | });
106 |
107 | exec_cmd(cmdline, num, windowsify_path(project_path(args[0])));
108 | }
109 |
--------------------------------------------------------------------------------
/scripts/release.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | from __future__ import print_function
4 | import os
5 | import sys
6 | import re
7 |
8 | rootPath = os.path.dirname(os.path.realpath(os.path.dirname(sys.argv[0])))
9 | claraPath = os.path.join( rootPath, "include/clara.hpp" )
10 | readmePath = os.path.join( rootPath, "README.md" )
11 |
12 | versionParser = re.compile( r'\s*\/\/\s*Clara\s+v([0-9]+)\.([0-9]+)(\.([0-9]+)(\-(.*)\.([0-9]*))?)?' )
13 | readmeParser = re.compile( r'\s*#\s*Clara\s+v(.*)' )
14 |
15 | warnings = 0
16 |
17 | def precheck():
18 | global warnings
19 | f = open( claraPath, 'r' )
20 | lineNo = 0
21 | for line in f:
22 | lineNo = lineNo+1
23 | if "dynamic" in line:
24 | warnings = warnings + 1
25 | print( "** Warning: use of dynamic_cast on line {0}!".format(lineNo) )
26 | f.close()
27 |
28 | class Version:
29 | def __init__(self):
30 | f = open( claraPath, 'r' )
31 | for line in f:
32 | m = versionParser.match( line )
33 | if m:
34 | self.majorVersion = int(m.group(1))
35 | self.minorVersion = int(m.group(2))
36 | self.patchNumber = int(m.group(4))
37 | if m.group(6) == None:
38 | self.branchName = ""
39 | else:
40 | self.branchName = m.group(6)
41 | if m.group(7) == None:
42 | self.buildNumber = 0
43 | else:
44 | self.buildNumber = int(m.group(7))
45 | f.close()
46 |
47 | def nonDevelopRelease(self):
48 | if self.branchName != "":
49 | self.branchName = ""
50 | self.buildNumber = 0
51 | def developBuild(self):
52 | if self.branchName == "":
53 | self.branchName = "develop"
54 | self.buildNumber = 0
55 | else:
56 | self.buildNumber = self.buildNumber+1
57 |
58 | def incrementBuildNumber(self):
59 | self.developBuild()
60 | self.buildNumber = self.buildNumber+1
61 |
62 | def incrementPatchNumber(self):
63 | self.nonDevelopRelease()
64 | self.patchNumber = self.patchNumber+1
65 |
66 | def incrementMinorVersion(self):
67 | self.nonDevelopRelease()
68 | self.patchNumber = 0
69 | self.minorVersion = self.minorVersion+1
70 |
71 | def incrementMajorVersion(self):
72 | self.nonDevelopRelease()
73 | self.patchNumber = 0
74 | self.minorVersion = 0
75 | self.majorVersion = self.majorVersion+1
76 |
77 | def getVersionString(self):
78 | versionString = '{0}.{1}.{2}'.format( self.majorVersion, self.minorVersion, self.patchNumber )
79 | if self.branchName != "":
80 | versionString = versionString + '-{0}.{1}'.format( self.branchName, self.buildNumber )
81 | return versionString
82 |
83 | def updateHeader(self):
84 | f = open( claraPath, 'r' )
85 | lines = []
86 | for line in f:
87 | m = versionParser.match( line )
88 | if m:
89 | lines.append( '// Clara v{0}'.format( self.getVersionString() ) )
90 | else:
91 | lines.append( line.rstrip() )
92 | f.close()
93 | f = open( claraPath, 'w' )
94 | for line in lines:
95 | f.write( line + "\n" )
96 |
97 | def updateReadme(self):
98 | f = open( readmePath, 'r' )
99 | lines = []
100 | changed = False
101 | for line in f:
102 | m = readmeParser.match( line )
103 | if m:
104 | lines.append( '# Clara v{0}'.format( self.getVersionString() ) )
105 | changed = True
106 | else:
107 | lines.append( line.rstrip() )
108 | f.close()
109 | if changed:
110 | f = open( readmePath, 'w' )
111 | for line in lines:
112 | f.write( line + "\n" )
113 | else:
114 | print( "*** Did not update README" )
115 | def usage():
116 | print( "\n**** Run with patch|minor|major|dev|verify\n" )
117 | return 1
118 |
119 | if len( sys.argv) == 1:
120 | exit( usage() )
121 |
122 | precheck()
123 |
124 | v = Version()
125 | oldV = v.getVersionString()
126 |
127 | cmd = sys.argv[1].lower()
128 |
129 | if warnings > 0:
130 | print( "Found {0} issue(s)".format(warnings))
131 | exit(1)
132 |
133 | if cmd == "verify":
134 | print( "No issues found")
135 | exit(0)
136 | elif cmd == "patch":
137 | v.incrementPatchNumber()
138 | elif cmd == "minor":
139 | v.incrementMinorVersion()
140 | elif cmd == "major":
141 | v.incrementMajorVersion()
142 | elif cmd == "dev":
143 | v.developBuild()
144 | else:
145 | exit( usage() )
146 |
147 | v.updateHeader()
148 | v.updateReadme()
149 |
150 | print( "Updated clara.hpp and README from {0} to v{1}".format( oldV, v.getVersionString() ) )
151 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Clara v1.1.5
2 | [](https://travis-ci.org/catchorg/Clara)
3 | [](https://ci.appveyor.com/project/catchorg/clara)
4 | [](https://codecov.io/gh/catchorg/Clara)
5 |
6 | # !! This repository is unmaintained. Go [here](https://github.com/bfgroup/Lyra) for a fork that is somewhat maintained. !!
7 |
8 | -----------------------------
9 |
10 |
11 | A simple to use, composable, command line parser for C++ 11 and beyond.
12 |
13 | Clara is a single-header library.
14 |
15 | To use, just `#include "clara.hpp"`
16 |
17 | A parser for a single option can be created like this:
18 |
19 | ```c++
20 | int width = 0;
21 | // ...
22 | using namespace clara;
23 | auto cli
24 | = Opt( width, "width" )
25 | ["-w"]["--width"]
26 | ("How wide should it be?");
27 | ```
28 |
29 | You can use this parser directly like this:
30 |
31 | ```c++
32 | auto result = cli.parse( Args( argc, argv ) );
33 | if( !result ) {
34 | std::cerr << "Error in command line: " << result.errorMessage() << std::endl;
35 | exit(1);
36 | }
37 |
38 | // Everything was ok, width will have a value if supplied on command line
39 | ```
40 |
41 | Note that exceptions are not used for error handling.
42 |
43 | You can combine parsers by composing with `|`, like this:
44 |
45 | ```c++
46 | int width = 0;
47 | std::string name;
48 | bool doIt = false;
49 | std::string command;
50 | auto cli
51 | = Opt( width, "width" )
52 | ["-w"]["--width"]
53 | ("How wide should it be?")
54 | | Opt( name, "name" )
55 | ["-n"]["--name"]
56 | ("By what name should I be known")
57 | | Opt( doIt )
58 | ["-d"]["--doit"]
59 | ("Do the thing" )
60 | | Arg( command, "command" )
61 | ("which command to run");
62 | ```
63 |
64 | `Opt`s specify options that start with a short dash (`-`) or long dash (`--`).
65 | On Windows forward slashes are also accepted (and automatically interpretted as a short dash).
66 | Options can be argument taking (such as `-w 42`), in which case the `Opt` takes a second argument - a hint,
67 | or they are pure flags (such as `-d`), in which case the `Opt` has only one argument - which must be a boolean.
68 | The option names are provided in one or more sets of square brackets, and a description string can
69 | be provided in parentheses. The first argument to an `Opt` is any variable, local, global member, of any type
70 | that can be converted from a string using `std::ostream`.
71 |
72 | `Arg`s specify arguments that are not tied to options, and so have no square bracket names. They otherwise work just like `Opt`s.
73 |
74 | A, console optimised, usage string can be obtained by inserting the parser into a stream.
75 | The usage string is built from the information supplied and is formatted for the console width.
76 |
77 | As a convenience, the standard help options (`-h`, `--help` and `-?`) can be specified using the `Help` parser,
78 | which just takes a boolean to bind to.
79 |
80 | For more usage please see the unit tests or look at how it is used in the Catch code-base (catch-lib.net).
81 | Fuller documentation will be coming soon.
82 |
83 | Some of the key features:
84 |
85 | - A single header file with no external dependencies (except the std library).
86 | - Define your interface once to get parsing, type conversions and usage strings with no redundancy.
87 | - Composable. Each `Opt` or `Arg` is an independent parser. Combine these to produce a composite parser - this can be done in stages across multiple function calls - or even projects.
88 | - Bind parsers directly to variables that will receive the results of the parse - no intermediate dictionaries to worry about.
89 | - Or can also bind parsers to lambdas for more custom handling.
90 | - Deduces types from bound variables or lambdas and performs type conversions (via `ostream <<`), with error handling, behind the scenes.
91 | - Bind parsers to vectors for args that can have multiple values.
92 | - Uses Result types for error propagation, rather than exceptions (doesn't yet build with exceptions disabled, but that will be coming later)
93 | - Models POSIX standards for short and long opt behaviour.
94 |
95 | ## Roadmap
96 |
97 | To see which direction Clara is going in, please see [the roadmap](Roadmap.md)
98 |
99 | ## Old version
100 |
101 | If you used the earlier, v0.x, version of Clara please note that this is a complete rewrite which assumes C++11 and has
102 | a different interface (composability was a big step forward). Conversion between v0.x and v1.x is a fairly simple and mechanical task, but is a bit of manual
103 | work - so don't take this version until you're ready (and, of course, able to use C++11).
104 |
105 | I hope you'll find the new interface an improvement - and this will be built on to offer new features moving forwards.
106 | I don't expect to maintain v0.x any further, but it remains on a branch.
107 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: cpp
2 | sudo: false
3 |
4 | common_sources: &all_sources
5 | - ubuntu-toolchain-r-test
6 | - llvm-toolchain-trusty
7 | - llvm-toolchain-trusty-3.9
8 | - llvm-toolchain-trusty-4.0
9 | - llvm-toolchain-trusty-5.0
10 |
11 | matrix:
12 | include:
13 |
14 | # 1/ Linux Clang Builds
15 | - os: linux
16 | compiler: clang
17 | addons:
18 | apt:
19 | sources: *all_sources
20 | packages: ['valgrind', 'lcov', 'clang-3.5']
21 | env: COMPILER='clang++-3.5' VALGRIND=1
22 |
23 | - os: linux
24 | compiler: clang
25 | addons:
26 | apt:
27 | sources: *all_sources
28 | packages: ['valgrind', 'lcov', 'clang-3.6']
29 | env: COMPILER='clang++-3.6' VALGRIND=1
30 |
31 | # Travis's containers do not seem to have Clang 3.7 in apt, no matter what sources I add.
32 | # - os: linux
33 | # compiler: clang
34 | # addons:
35 | # apt:
36 | # sources: *all_sources
37 | # packages: ['valgrind', 'clang-3.7']
38 | # env: COMPILER='clang++-3.7' VALGRIND=1
39 |
40 | - os: linux
41 | compiler: clang
42 | addons:
43 | apt:
44 | sources: *all_sources
45 | packages: ['valgrind', 'lcov', 'clang-3.8']
46 | env: COMPILER='clang++-3.8' VALGRIND=1
47 |
48 | - os: linux
49 | compiler: clang
50 | addons:
51 | apt:
52 | sources: *all_sources
53 | packages: ['clang-3.9', 'valgrind', 'lcov']
54 | env: COMPILER='clang++-3.9' VALGRIND=1
55 |
56 | - os: linux
57 | compiler: clang
58 | addons:
59 | apt:
60 | sources: *all_sources
61 | packages: ['clang-4.0', 'valgrind', 'lcov']
62 | env: COMPILER='clang++-4.0' VALGRIND=1
63 |
64 | - os: linux
65 | compiler: clang
66 | addons:
67 | apt:
68 | sources: *all_sources
69 | packages: ['clang-5.0', 'valgrind', 'lcov']
70 | env: COMPILER='clang++-5.0' VALGRIND=1
71 |
72 | # 2/ Linux GCC Builds
73 | - os: linux
74 | compiler: gcc
75 | addons:
76 | apt:
77 | sources: ['ubuntu-toolchain-r-test']
78 | packages: ['valgrind', 'lcov', 'g++-4.8']
79 | env: COMPILER='g++-4.8' VALGRIND=1 COVERAGE=1
80 |
81 | - os: linux
82 | compiler: gcc
83 | addons:
84 | apt:
85 | sources: *all_sources
86 | packages: ['valgrind', 'lcov', 'g++-4.9']
87 | env: COMPILER='g++-4.9' VALGRIND=1
88 |
89 | - os: linux
90 | compiler: gcc
91 | addons:
92 | apt:
93 | sources: *all_sources
94 | packages: ['valgrind', 'lcov', 'g++-5']
95 | env: COMPILER='g++-5' VALGRIND=1
96 |
97 | - os: linux
98 | compiler: gcc
99 | addons: &gcc6
100 | apt:
101 | sources: *all_sources
102 | packages: ['valgrind', 'lcov', 'g++-6']
103 | env: COMPILER='g++-6' VALGRIND=1
104 |
105 | - os: linux
106 | compiler: gcc
107 | addons: &gcc7
108 | apt:
109 | sources: *all_sources
110 | packages: ['valgrind', 'lcov', 'g++-7']
111 | env: COMPILER='g++-7' VALGRIND=1
112 |
113 | - os: linux
114 | compiler: gcc
115 | addons: *gcc7
116 | env: COMPILER='g++-7' CPP17=1 COVERAGE=1
117 |
118 | # 5/ OSX Clang Builds
119 | - os: osx
120 | osx_image: xcode8.3
121 | compiler: clang
122 | env: COMPILER='clang++'
123 |
124 | - os: osx
125 | osx_image: xcode9
126 | compiler: clang
127 | env: COMPILER='clang++'
128 |
129 | - os: osx
130 | osx_image: xcode9.1
131 | compiler: clang
132 | env: COMPILER='clang++'
133 |
134 | - os: osx
135 | osx_image: xcode9.1
136 | compiler: clang
137 | env: COMPILER='clang++' CPP14=1
138 |
139 |
140 | install:
141 | - DEPS_DIR="${TRAVIS_BUILD_DIR}/deps"
142 | - mkdir -p ${DEPS_DIR} && cd ${DEPS_DIR}
143 | - |
144 | if [[ "${TRAVIS_OS_NAME}" == "linux" ]]; then
145 | CMAKE_URL="http://cmake.org/files/v3.8/cmake-3.8.2-Linux-x86_64.tar.gz"
146 | mkdir cmake && travis_retry wget --no-check-certificate --quiet -O - ${CMAKE_URL} | tar --strip-components=1 -xz -C cmake
147 | export PATH=${DEPS_DIR}/cmake/bin:${PATH}
148 | elif [[ "${TRAVIS_OS_NAME}" == "osx" ]]; then
149 | which cmake || brew install cmake;
150 | fi
151 |
152 | before_script:
153 | - export CXX=${COMPILER}
154 | - cd ${TRAVIS_BUILD_DIR}
155 | # Use Debug builds for collecting coverage
156 | - cmake -H. -BBuild-Debug -DCMAKE_BUILD_TYPE=Debug -DENABLE_COVERAGE=${COVERAGE} -DUSE_CPP14=${CPP14} -DUSE_CPP17=${CPP17}
157 | # Don't bother with release build for coverage build
158 | - cmake -H. -BBuild-Release -DCMAKE_BUILD_TYPE=Release
159 |
160 |
161 | script:
162 | - cd Build-Debug
163 | - make -j 2
164 | - CTEST_OUTPUT_ON_FAILURE=1 ctest -j 2
165 | - |
166 | # Coverage collection does not work for OS X atm
167 | if [[ "${COVERAGE}" == "1" ]]; then
168 | make gcov
169 | make lcov
170 | bash <(curl -s https://codecov.io/bash) -X gcov || echo "Codecov did not collect coverage reports"
171 | fi
172 | # Go to release build
173 | - cd ../Build-Release
174 | - make -j 2
175 | - CTEST_OUTPUT_ON_FAILURE=1 ctest -j 2
176 |
--------------------------------------------------------------------------------
/CMake/FindGcov.cmake:
--------------------------------------------------------------------------------
1 | # This file is part of CMake-codecov.
2 | #
3 | # Copyright (c)
4 | # 2015-2017 RWTH Aachen University, Federal Republic of Germany
5 | #
6 | # See the LICENSE file in the package base directory for details
7 | #
8 | # Written by Alexander Haase, alexander.haase@rwth-aachen.de
9 | #
10 |
11 |
12 | # include required Modules
13 | include(FindPackageHandleStandardArgs)
14 |
15 |
16 | # Search for gcov binary.
17 | set(CMAKE_REQUIRED_QUIET_SAVE ${CMAKE_REQUIRED_QUIET})
18 | set(CMAKE_REQUIRED_QUIET ${codecov_FIND_QUIETLY})
19 |
20 | get_property(ENABLED_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES)
21 | foreach (LANG ${ENABLED_LANGUAGES})
22 | # Gcov evaluation is dependend on the used compiler. Check gcov support for
23 | # each compiler that is used. If gcov binary was already found for this
24 | # compiler, do not try to find it again.
25 | if (NOT GCOV_${CMAKE_${LANG}_COMPILER_ID}_BIN)
26 | get_filename_component(COMPILER_PATH "${CMAKE_${LANG}_COMPILER}" PATH)
27 |
28 | if ("${CMAKE_${LANG}_COMPILER_ID}" STREQUAL "GNU")
29 | # Some distributions like OSX (homebrew) ship gcov with the compiler
30 | # version appended as gcov-x. To find this binary we'll build the
31 | # suggested binary name with the compiler version.
32 | string(REGEX MATCH "^[0-9]+" GCC_VERSION
33 | "${CMAKE_${LANG}_COMPILER_VERSION}")
34 |
35 | find_program(GCOV_BIN NAMES gcov-${GCC_VERSION} gcov
36 | HINTS ${COMPILER_PATH})
37 |
38 | elseif ("${CMAKE_${LANG}_COMPILER_ID}" STREQUAL "Clang")
39 | # Some distributions like Debian ship llvm-cov with the compiler
40 | # version appended as llvm-cov-x.y. To find this binary we'll build
41 | # the suggested binary name with the compiler version.
42 | string(REGEX MATCH "^[0-9]+.[0-9]+" LLVM_VERSION
43 | "${CMAKE_${LANG}_COMPILER_VERSION}")
44 |
45 | # llvm-cov prior version 3.5 seems to be not working with coverage
46 | # evaluation tools, but these versions are compatible with the gcc
47 | # gcov tool.
48 | if(LLVM_VERSION VERSION_GREATER 3.4)
49 | find_program(LLVM_COV_BIN NAMES "llvm-cov-${LLVM_VERSION}"
50 | "llvm-cov" HINTS ${COMPILER_PATH})
51 | mark_as_advanced(LLVM_COV_BIN)
52 |
53 | if (LLVM_COV_BIN)
54 | find_program(LLVM_COV_WRAPPER "llvm-cov-wrapper" PATHS
55 | ${CMAKE_MODULE_PATH})
56 | if (LLVM_COV_WRAPPER)
57 | set(GCOV_BIN "${LLVM_COV_WRAPPER}" CACHE FILEPATH "")
58 |
59 | # set additional parameters
60 | set(GCOV_${CMAKE_${LANG}_COMPILER_ID}_ENV
61 | "LLVM_COV_BIN=${LLVM_COV_BIN}" CACHE STRING
62 | "Environment variables for llvm-cov-wrapper.")
63 | mark_as_advanced(GCOV_${CMAKE_${LANG}_COMPILER_ID}_ENV)
64 | endif ()
65 | endif ()
66 | endif ()
67 |
68 | if (NOT GCOV_BIN)
69 | # Fall back to gcov binary if llvm-cov was not found or is
70 | # incompatible. This is the default on OSX, but may crash on
71 | # recent Linux versions.
72 | find_program(GCOV_BIN gcov HINTS ${COMPILER_PATH})
73 | endif ()
74 | endif ()
75 |
76 |
77 | if (GCOV_BIN)
78 | set(GCOV_${CMAKE_${LANG}_COMPILER_ID}_BIN "${GCOV_BIN}" CACHE STRING
79 | "${LANG} gcov binary.")
80 |
81 | if (NOT CMAKE_REQUIRED_QUIET)
82 | message("-- Found gcov evaluation for "
83 | "${CMAKE_${LANG}_COMPILER_ID}: ${GCOV_BIN}")
84 | endif()
85 |
86 | unset(GCOV_BIN CACHE)
87 | endif ()
88 | endif ()
89 | endforeach ()
90 |
91 |
92 |
93 |
94 | # Add a new global target for all gcov targets. This target could be used to
95 | # generate the gcov files for the whole project instead of calling -gcov
96 | # for each target.
97 | if (NOT TARGET gcov)
98 | add_custom_target(gcov)
99 | endif (NOT TARGET gcov)
100 |
101 |
102 |
103 | # This function will add gcov evaluation for target . Only sources of
104 | # this target will be evaluated and no dependencies will be added. It will call
105 | # Gcov on any source file of once and store the gcov file in the same
106 | # directory.
107 | function (add_gcov_target TNAME)
108 | set(TDIR ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${TNAME}.dir)
109 |
110 | # We don't have to check, if the target has support for coverage, thus this
111 | # will be checked by add_coverage_target in Findcoverage.cmake. Instead we
112 | # have to determine which gcov binary to use.
113 | get_target_property(TSOURCES ${TNAME} SOURCES)
114 | set(SOURCES "")
115 | set(TCOMPILER "")
116 | foreach (FILE ${TSOURCES})
117 | codecov_path_of_source(${FILE} FILE)
118 | if (NOT "${FILE}" STREQUAL "")
119 | codecov_lang_of_source(${FILE} LANG)
120 | if (NOT "${LANG}" STREQUAL "")
121 | list(APPEND SOURCES "${FILE}")
122 | set(TCOMPILER ${CMAKE_${LANG}_COMPILER_ID})
123 | endif ()
124 | endif ()
125 | endforeach ()
126 |
127 | # If no gcov binary was found, coverage data can't be evaluated.
128 | if (NOT GCOV_${TCOMPILER}_BIN)
129 | message(WARNING "No coverage evaluation binary found for ${TCOMPILER}.")
130 | return()
131 | endif ()
132 |
133 | set(GCOV_BIN "${GCOV_${TCOMPILER}_BIN}")
134 | set(GCOV_ENV "${GCOV_${TCOMPILER}_ENV}")
135 |
136 |
137 | set(BUFFER "")
138 | foreach(FILE ${SOURCES})
139 | get_filename_component(FILE_PATH "${TDIR}/${FILE}" PATH)
140 |
141 | # call gcov
142 | add_custom_command(OUTPUT ${TDIR}/${FILE}.gcov
143 | COMMAND ${GCOV_ENV} ${GCOV_BIN} ${TDIR}/${FILE}.gcno > /dev/null
144 | DEPENDS ${TNAME} ${TDIR}/${FILE}.gcno
145 | WORKING_DIRECTORY ${FILE_PATH}
146 | )
147 |
148 | list(APPEND BUFFER ${TDIR}/${FILE}.gcov)
149 | endforeach()
150 |
151 |
152 | # add target for gcov evaluation of
153 | add_custom_target(${TNAME}-gcov DEPENDS ${BUFFER})
154 |
155 | # add evaluation target to the global gcov target.
156 | add_dependencies(gcov ${TNAME}-gcov)
157 | endfunction (add_gcov_target)
158 |
--------------------------------------------------------------------------------
/scripts/stitch.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | # This is an initial cut of a general header stitching script.
4 | # It is currently hard-coded to work with Clara, but that is purely
5 | # a path thing. The next step is the genericise this further and
6 | # apply it to Catch. There will undoubtedly be issues to fix there.
7 | # After that there are further tweaks to make such as suppressing initial
8 | # comments in stitched headers and adding a single new header block to
9 | # the output file, suppressing conditional blocks where the identifiers
10 | # are known and moving all headers not in conditional blocks to the top
11 | # of the file
12 |
13 | import os
14 | import sys
15 | import re
16 | import datetime
17 | import string
18 |
19 | preprocessorRe = re.compile( r'\s*#.*' )
20 |
21 | includesRe = re.compile( r'\s*#\s*include.*' ) # all #includes
22 | sysIncludesRe = re.compile( r'\s*#\s*include\s*<(.*)>' ) # .e.g #include
23 | prjIncludesRe = re.compile( r'\s*#\s*include\s*"(.*)"' ) # e.g. #include "myheader.h"
24 |
25 | fdefineRe = re.compile( r'\s*#\s*define\s*(\S*)\s*\(' ) # #defines that take arguments
26 | defineRe = re.compile( r'\s*#\s*define\s*(\S*)\s+(\S*)' ) # all #defines
27 | undefRe = re.compile( r'\s*#\s*undef\s*(\S*)' ) # all #undefs
28 |
29 | ifdefCommonRe = re.compile( r'\s*#\s*if' ) # all #ifdefs
30 | ifdefRe = re.compile( r'\s*#\s*ifdef\s*(\S*)' )
31 | ifndefRe = re.compile( r'\s*#\s*ifndef\s*(\S*)' )
32 | endifRe = re.compile( r'\s*#\s*endif(.*)' )
33 | elseRe = re.compile( r'\s*#\s*else' )
34 | ifRe = re.compile( r'\s*#\s*if\s+(.*)' )
35 |
36 | rootPath = os.path.dirname(os.path.realpath( os.path.dirname(sys.argv[0])))
37 | srcsPath = os.path.join( rootPath, 'src' )
38 | includePath = os.path.join( rootPath, 'include' )
39 | singleIncludePath = os.path.join( rootPath, 'single_include' )
40 | extIncludePath = os.path.join( includePath, "external" )
41 | thirdPartyPath = os.path.join( rootPath, 'third_party' )
42 | firstHeaderPath = os.path.join( includePath, 'clara.hpp' )
43 | outPath = os.path.join( singleIncludePath, 'clara.hpp' )
44 |
45 | o = open( outPath, 'w' )
46 |
47 | systemHeaders = set([])
48 | projectHeaders = set([])
49 | defines = set([])
50 | LevelMax = 9999
51 | suppressUntilLevel = LevelMax
52 | level = 0
53 |
54 | class FileParser:
55 | filename = ""
56 |
57 | def __init__( self, filename ):
58 | self.filename = filename
59 |
60 | def findHeader( self, headerFile ):
61 |
62 | # !TBD: make this array based
63 |
64 | # First check next to current file
65 | dir, _ = os.path.split( self.filename )
66 | fullPath = os.path.join( dir, headerFile )
67 | if os.path.isfile(fullPath):
68 | return fullPath
69 |
70 | # Next check include folder
71 | fullPath = os.path.join( includePath, headerFile )
72 | if os.path.isfile(fullPath):
73 | return fullPath
74 |
75 | # Then ext folder
76 | fullPath = os.path.join( extIncludePath, headerFile )
77 | if os.path.isfile(fullPath):
78 | return fullPath
79 |
80 | # Finally check ThirdParty folder
81 | fullPath = os.path.join( thirdPartyPath, headerFile )
82 | if os.path.isfile(fullPath):
83 | return fullPath
84 |
85 | raise FileNotFoundError( "Cannot locate include file: '" + filename + "'" )
86 |
87 |
88 | def parse( self ):
89 | with open( self.filename, 'r' ) as f:
90 | for line in f:
91 | if preprocessorRe.match( line ):
92 | self.handlePreprocessor( line )
93 | else:
94 | self.handleNonPP( line )
95 |
96 | def handleNonPP( self, line ):
97 | if suppressUntilLevel > level:
98 | self.writeLine( line )
99 |
100 | def handlePreprocessor( self, line ):
101 | if includesRe.match( line ):
102 | if suppressUntilLevel > level:
103 | self.handleInclude( line )
104 | else:
105 | self.handleNonIncludePP( line )
106 |
107 | def handleInclude( self, line ):
108 | global systemHeaders
109 | global projectHeaders
110 | m = sysIncludesRe.match( line )
111 | if m:
112 | headerFile = m.group(1)
113 | if not headerFile in systemHeaders:
114 | self.writeLine( "#include <{0}>".format( headerFile ) )
115 | systemHeaders.add( headerFile )
116 |
117 | m = prjIncludesRe.match( line )
118 | if m:
119 | headerFile = m.group(1)
120 | if not headerFile in projectHeaders:
121 | self.writeLine( '\n// ----------- #included from {0} -----------'.format( headerFile ) )
122 | self.writeLine( "" )
123 | projectHeaders.add( headerFile )
124 |
125 | _, filename = os.path.split( self.filename )
126 | headerPath = self.findHeader( headerFile )
127 | p = FileParser( headerPath )
128 | p.parse()
129 | self.writeLine( '\n// ----------- end of #include from {0} -----------'.format( headerFile ) )
130 | self.writeLine( '// ........... back in {0}'.format( filename ) )
131 | self.writeLine( "" )
132 |
133 | def handleNonIncludePP( self, line ):
134 | m = endifRe.match( line )
135 | if m:
136 | self.handleEndif( m.group(1) )
137 | m = elseRe.match( line )
138 | if m:
139 | self.handleElse()
140 |
141 | if ifdefCommonRe.match( line ):
142 | self.handleIfdefCommon( line )
143 |
144 | if suppressUntilLevel > level:
145 | m = defineRe.match( line )
146 | if m:
147 | if fdefineRe.match( line ):
148 | self.writeLine( line )
149 | else:
150 | self.handleDefine( m.group(1), m.group(2) )
151 | m = undefRe.match( line )
152 | if m:
153 | self.handleUndef( m.group(1) )
154 |
155 | def handleDefine( self, define, value ):
156 | self.writeLine( "#define {0} {1}".format( define, value ) )
157 | defines.add( define )
158 |
159 | def handleUndef( self, define ):
160 | self.writeLine( "#undef {0}".format( define ) )
161 | defines.remove( define )
162 |
163 | def handleIfdefCommon( self, line ):
164 | global level
165 | level = level + 1
166 | m = ifndefRe.match( line )
167 | if m:
168 | self.handleIfndef( m.group(1) )
169 | else:
170 | m = ifdefRe.match( line )
171 | if m:
172 | self.handleIfdef( m.group(1) )
173 | else:
174 | m = ifRe.match( line )
175 | if m:
176 | self.handleIf( m.group(1) )
177 | else:
178 | print "****** error ***** " + line
179 |
180 | def handleEndif( self, trailing ):
181 | global level
182 | global suppressUntilLevel
183 | self.writeLine( "#endif{0}".format( trailing ) )
184 | level = level - 1
185 | if level == suppressUntilLevel:
186 | suppressUntilLevel = LevelMax
187 |
188 | def handleElse( self ):
189 | global suppressUntilLevel
190 | self.writeLine( "#else" )
191 | if level == suppressUntilLevel+1:
192 | suppressUntilLevel = LevelMax
193 |
194 | def handleIfndef( self, define ):
195 | self.writeLine( "#ifndef {0}".format( define ) )
196 | if define not in defines:
197 | suppressUntilLevel = level
198 |
199 | def handleIfdef( self, define ):
200 | self.writeLine( "#ifdef {0}".format( define ) )
201 | if define in defines:
202 | suppressUntilLevel = level
203 |
204 | def handleIf( self, trailing ):
205 | global level
206 | global suppressUntilLevel
207 | self.writeLine( "#if {0}".format( trailing ) )
208 | level = level + 1
209 | # if level == suppressUntilLevel:
210 | # suppressUntilLevel = LevelMax
211 |
212 |
213 | def writeLine( self, line ):
214 | o.write( line.rstrip() + "\n" )
215 |
216 | print( "from: " + firstHeaderPath )
217 | print( "to: " + outPath )
218 | p = FileParser( firstHeaderPath )
219 | p.parse()
220 |
221 | o.close()
222 |
223 | print "-------------"
224 | print level
225 | #for h in systemHeaders:
226 | # print "#include <" + h + ">"
227 | #print
228 | #
229 | #for h in projectHeaders:
230 | # print '#include "' + h + '"'
231 | #print
232 | #for d in defines:
233 | # print d
234 | #print
235 |
--------------------------------------------------------------------------------
/CMake/Findcodecov.cmake:
--------------------------------------------------------------------------------
1 | # This file is part of CMake-codecov.
2 | #
3 | # Copyright (c)
4 | # 2015-2017 RWTH Aachen University, Federal Republic of Germany
5 | #
6 | # See the LICENSE file in the package base directory for details
7 | #
8 | # Written by Alexander Haase, alexander.haase@rwth-aachen.de
9 | #
10 |
11 |
12 | # Add an option to choose, if coverage should be enabled or not. If enabled
13 | # marked targets will be build with coverage support and appropriate targets
14 | # will be added. If disabled coverage will be ignored for *ALL* targets.
15 | option(ENABLE_COVERAGE "Enable coverage build." OFF)
16 |
17 | set(COVERAGE_FLAG_CANDIDATES
18 | # gcc and clang
19 | "-O0 -g -fprofile-arcs -ftest-coverage"
20 |
21 | # gcc and clang fallback
22 | "-O0 -g --coverage"
23 | )
24 |
25 |
26 | # Add coverage support for target ${TNAME} and register target for coverage
27 | # evaluation. If coverage is disabled or not supported, this function will
28 | # simply do nothing.
29 | #
30 | # Note: This function is only a wrapper to define this function always, even if
31 | # coverage is not supported by the compiler or disabled. This function must
32 | # be defined here, because the module will be exited, if there is no coverage
33 | # support by the compiler or it is disabled by the user.
34 | function (add_coverage TNAME)
35 | # only add coverage for target, if coverage is support and enabled.
36 | if (ENABLE_COVERAGE)
37 | foreach (TNAME ${ARGV})
38 | add_coverage_target(${TNAME})
39 | endforeach ()
40 | endif ()
41 | endfunction (add_coverage)
42 |
43 |
44 | # Add global target to gather coverage information after all targets have been
45 | # added. Other evaluation functions could be added here, after checks for the
46 | # specific module have been passed.
47 | #
48 | # Note: This function is only a wrapper to define this function always, even if
49 | # coverage is not supported by the compiler or disabled. This function must
50 | # be defined here, because the module will be exited, if there is no coverage
51 | # support by the compiler or it is disabled by the user.
52 | function (coverage_evaluate)
53 | # add lcov evaluation
54 | if (LCOV_FOUND)
55 | lcov_capture_initial()
56 | lcov_capture()
57 | endif (LCOV_FOUND)
58 | endfunction ()
59 |
60 |
61 | # Exit this module, if coverage is disabled. add_coverage is defined before this
62 | # return, so this module can be exited now safely without breaking any build-
63 | # scripts.
64 | if (NOT ENABLE_COVERAGE)
65 | return()
66 | endif ()
67 |
68 |
69 |
70 |
71 | # Find the reuired flags foreach language.
72 | set(CMAKE_REQUIRED_QUIET_SAVE ${CMAKE_REQUIRED_QUIET})
73 | set(CMAKE_REQUIRED_QUIET ${codecov_FIND_QUIETLY})
74 |
75 | get_property(ENABLED_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES)
76 | foreach (LANG ${ENABLED_LANGUAGES})
77 | # Coverage flags are not dependend on language, but the used compiler. So
78 | # instead of searching flags foreach language, search flags foreach compiler
79 | # used.
80 | set(COMPILER ${CMAKE_${LANG}_COMPILER_ID})
81 | if (NOT COVERAGE_${COMPILER}_FLAGS)
82 | foreach (FLAG ${COVERAGE_FLAG_CANDIDATES})
83 | if(NOT CMAKE_REQUIRED_QUIET)
84 | message(STATUS "Try ${COMPILER} code coverage flag = [${FLAG}]")
85 | endif()
86 |
87 | set(CMAKE_REQUIRED_FLAGS "${FLAG}")
88 | unset(COVERAGE_FLAG_DETECTED CACHE)
89 |
90 | if (${LANG} STREQUAL "C")
91 | include(CheckCCompilerFlag)
92 | check_c_compiler_flag("${FLAG}" COVERAGE_FLAG_DETECTED)
93 |
94 | elseif (${LANG} STREQUAL "CXX")
95 | include(CheckCXXCompilerFlag)
96 | check_cxx_compiler_flag("${FLAG}" COVERAGE_FLAG_DETECTED)
97 |
98 | elseif (${LANG} STREQUAL "Fortran")
99 | # CheckFortranCompilerFlag was introduced in CMake 3.x. To be
100 | # compatible with older Cmake versions, we will check if this
101 | # module is present before we use it. Otherwise we will define
102 | # Fortran coverage support as not available.
103 | include(CheckFortranCompilerFlag OPTIONAL
104 | RESULT_VARIABLE INCLUDED)
105 | if (INCLUDED)
106 | check_fortran_compiler_flag("${FLAG}"
107 | COVERAGE_FLAG_DETECTED)
108 | elseif (NOT CMAKE_REQUIRED_QUIET)
109 | message("-- Performing Test COVERAGE_FLAG_DETECTED")
110 | message("-- Performing Test COVERAGE_FLAG_DETECTED - Failed"
111 | " (Check not supported)")
112 | endif ()
113 | endif()
114 |
115 | if (COVERAGE_FLAG_DETECTED)
116 | set(COVERAGE_${COMPILER}_FLAGS "${FLAG}"
117 | CACHE STRING "${COMPILER} flags for code coverage.")
118 | mark_as_advanced(COVERAGE_${COMPILER}_FLAGS)
119 | break()
120 | else ()
121 | message(WARNING "Code coverage is not available for ${COMPILER}"
122 | " compiler. Targets using this compiler will be "
123 | "compiled without it.")
124 | endif ()
125 | endforeach ()
126 | endif ()
127 | endforeach ()
128 |
129 | set(CMAKE_REQUIRED_QUIET ${CMAKE_REQUIRED_QUIET_SAVE})
130 |
131 |
132 |
133 |
134 | # Helper function to get the language of a source file.
135 | function (codecov_lang_of_source FILE RETURN_VAR)
136 | get_filename_component(FILE_EXT "${FILE}" EXT)
137 | string(TOLOWER "${FILE_EXT}" FILE_EXT)
138 | string(SUBSTRING "${FILE_EXT}" 1 -1 FILE_EXT)
139 |
140 | get_property(ENABLED_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES)
141 | foreach (LANG ${ENABLED_LANGUAGES})
142 | list(FIND CMAKE_${LANG}_SOURCE_FILE_EXTENSIONS "${FILE_EXT}" TEMP)
143 | if (NOT ${TEMP} EQUAL -1)
144 | set(${RETURN_VAR} "${LANG}" PARENT_SCOPE)
145 | return()
146 | endif ()
147 | endforeach()
148 |
149 | set(${RETURN_VAR} "" PARENT_SCOPE)
150 | endfunction ()
151 |
152 |
153 | # Helper function to get the relative path of the source file destination path.
154 | # This path is needed by FindGcov and FindLcov cmake files to locate the
155 | # captured data.
156 | function (codecov_path_of_source FILE RETURN_VAR)
157 | string(REGEX MATCH "TARGET_OBJECTS:([^ >]+)" _source ${FILE})
158 |
159 | # If expression was found, SOURCEFILE is a generator-expression for an
160 | # object library. Currently we found no way to call this function automatic
161 | # for the referenced target, so it must be called in the directoryso of the
162 | # object library definition.
163 | if (NOT "${_source}" STREQUAL "")
164 | set(${RETURN_VAR} "" PARENT_SCOPE)
165 | return()
166 | endif ()
167 |
168 |
169 | string(REPLACE "${CMAKE_CURRENT_BINARY_DIR}/" "" FILE "${FILE}")
170 | if(IS_ABSOLUTE ${FILE})
171 | file(RELATIVE_PATH FILE ${CMAKE_CURRENT_SOURCE_DIR} ${FILE})
172 | endif()
173 |
174 | # get the right path for file
175 | string(REPLACE ".." "__" PATH "${FILE}")
176 |
177 | set(${RETURN_VAR} "${PATH}" PARENT_SCOPE)
178 | endfunction()
179 |
180 |
181 |
182 |
183 | # Add coverage support for target ${TNAME} and register target for coverage
184 | # evaluation.
185 | function(add_coverage_target TNAME)
186 | # Check if all sources for target use the same compiler. If a target uses
187 | # e.g. C and Fortran mixed and uses different compilers (e.g. clang and
188 | # gfortran) this can trigger huge problems, because different compilers may
189 | # use different implementations for code coverage.
190 | get_target_property(TSOURCES ${TNAME} SOURCES)
191 | set(TARGET_COMPILER "")
192 | set(ADDITIONAL_FILES "")
193 | foreach (FILE ${TSOURCES})
194 | # If expression was found, FILE is a generator-expression for an object
195 | # library. Object libraries will be ignored.
196 | string(REGEX MATCH "TARGET_OBJECTS:([^ >]+)" _file ${FILE})
197 | if ("${_file}" STREQUAL "")
198 | codecov_lang_of_source(${FILE} LANG)
199 | if (LANG)
200 | list(APPEND TARGET_COMPILER ${CMAKE_${LANG}_COMPILER_ID})
201 |
202 | list(APPEND ADDITIONAL_FILES "${FILE}.gcno")
203 | list(APPEND ADDITIONAL_FILES "${FILE}.gcda")
204 | endif ()
205 | endif ()
206 | endforeach ()
207 |
208 | list(REMOVE_DUPLICATES TARGET_COMPILER)
209 | list(LENGTH TARGET_COMPILER NUM_COMPILERS)
210 |
211 | if (NUM_COMPILERS GREATER 1)
212 | message(WARNING "Can't use code coverage for target ${TNAME}, because "
213 | "it will be compiled by incompatible compilers. Target will be "
214 | "compiled without code coverage.")
215 | return()
216 |
217 | elseif (NUM_COMPILERS EQUAL 0)
218 | message(WARNING "Can't use code coverage for target ${TNAME}, because "
219 | "it uses an unknown compiler. Target will be compiled without "
220 | "code coverage.")
221 | return()
222 |
223 | elseif (NOT DEFINED "COVERAGE_${TARGET_COMPILER}_FLAGS")
224 | # A warning has been printed before, so just return if flags for this
225 | # compiler aren't available.
226 | return()
227 | endif()
228 |
229 |
230 | # enable coverage for target
231 | set_property(TARGET ${TNAME} APPEND_STRING
232 | PROPERTY COMPILE_FLAGS " ${COVERAGE_${TARGET_COMPILER}_FLAGS}")
233 | set_property(TARGET ${TNAME} APPEND_STRING
234 | PROPERTY LINK_FLAGS " ${COVERAGE_${TARGET_COMPILER}_FLAGS}")
235 |
236 |
237 | # Add gcov files generated by compiler to clean target.
238 | set(CLEAN_FILES "")
239 | foreach (FILE ${ADDITIONAL_FILES})
240 | codecov_path_of_source(${FILE} FILE)
241 | list(APPEND CLEAN_FILES "CMakeFiles/${TNAME}.dir/${FILE}")
242 | endforeach()
243 |
244 | set_directory_properties(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES
245 | "${CLEAN_FILES}")
246 |
247 |
248 | add_gcov_target(${TNAME})
249 | add_lcov_target(${TNAME})
250 | endfunction(add_coverage_target)
251 |
252 |
253 |
254 |
255 | # Include modules for parsing the collected data and output it in a readable
256 | # format (like gcov and lcov).
257 | find_package(Gcov)
258 | find_package(Lcov)
259 |
--------------------------------------------------------------------------------
/third_party/TextFlow.hpp:
--------------------------------------------------------------------------------
1 | // TextFlowCpp
2 | //
3 | // A single-header library for wrapping and laying out basic text, by Phil Nash
4 | //
5 | // Distributed under the Boost Software License, Version 1.0. (See accompanying
6 | // file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7 | //
8 | // This project is hosted at https://github.com/philsquared/textflowcpp
9 |
10 | #ifndef TEXTFLOW_HPP_INCLUDED
11 | #define TEXTFLOW_HPP_INCLUDED
12 |
13 | #include
14 | #include
15 | #include
16 | #include
17 |
18 | #ifndef TEXTFLOW_CONFIG_CONSOLE_WIDTH
19 | #define TEXTFLOW_CONFIG_CONSOLE_WIDTH 80
20 | #endif
21 |
22 |
23 | namespace TextFlow {
24 |
25 | inline auto isWhitespace( char c ) -> bool {
26 | static std::string chars = " \t\n\r";
27 | return chars.find( c ) != std::string::npos;
28 | }
29 | inline auto isBreakableBefore( char c ) -> bool {
30 | static std::string chars = "[({<|";
31 | return chars.find( c ) != std::string::npos;
32 | }
33 | inline auto isBreakableAfter( char c ) -> bool {
34 | static std::string chars = "])}>.,:;*+-=&/\\";
35 | return chars.find( c ) != std::string::npos;
36 | }
37 |
38 | class Columns;
39 |
40 | class Column {
41 | std::vector m_strings;
42 | size_t m_width = TEXTFLOW_CONFIG_CONSOLE_WIDTH;
43 | size_t m_indent = 0;
44 | size_t m_initialIndent = std::string::npos;
45 |
46 | public:
47 | class iterator {
48 | friend Column;
49 |
50 | Column const& m_column;
51 | size_t m_stringIndex = 0;
52 | size_t m_pos = 0;
53 |
54 | size_t m_len = 0;
55 | size_t m_end = 0;
56 | bool m_suffix = false;
57 |
58 | iterator( Column const& column, size_t stringIndex )
59 | : m_column( column ),
60 | m_stringIndex( stringIndex )
61 | {}
62 |
63 | auto line() const -> std::string const& { return m_column.m_strings[m_stringIndex]; }
64 |
65 | auto isBoundary( size_t at ) const -> bool {
66 | assert( at > 0 );
67 | assert( at <= line().size() );
68 |
69 | return at == line().size() ||
70 | ( isWhitespace( line()[at] ) && !isWhitespace( line()[at-1] ) ) ||
71 | isBreakableBefore( line()[at] ) ||
72 | isBreakableAfter( line()[at-1] );
73 | }
74 |
75 | void calcLength() {
76 | assert( m_stringIndex < m_column.m_strings.size() );
77 |
78 | m_suffix = false;
79 | auto width = m_column.m_width-indent();
80 | m_end = m_pos;
81 | while( m_end < line().size() && line()[m_end] != '\n' )
82 | ++m_end;
83 |
84 | if( m_end < m_pos + width ) {
85 | m_len = m_end - m_pos;
86 | }
87 | else {
88 | size_t len = width;
89 | while (len > 0 && !isBoundary(m_pos + len))
90 | --len;
91 | while (len > 0 && isWhitespace( line()[m_pos + len - 1] ))
92 | --len;
93 |
94 | if (len > 0) {
95 | m_len = len;
96 | } else {
97 | m_suffix = true;
98 | m_len = width - 1;
99 | }
100 | }
101 | }
102 |
103 | auto indent() const -> size_t {
104 | auto initial = m_pos == 0 && m_stringIndex == 0 ? m_column.m_initialIndent : std::string::npos;
105 | return initial == std::string::npos ? m_column.m_indent : initial;
106 | }
107 |
108 | auto addIndentAndSuffix(std::string const &plain) const -> std::string {
109 | return std::string( indent(), ' ' ) + (m_suffix ? plain + "-" : plain);
110 | }
111 |
112 | public:
113 | using difference_type = std::ptrdiff_t;
114 | using value_type = std::string;
115 | using pointer = value_type*;
116 | using reference = value_type&;
117 | using iterator_category = std::forward_iterator_tag;
118 |
119 | explicit iterator( Column const& column ) : m_column( column ) {
120 | assert( m_column.m_width > m_column.m_indent );
121 | assert( m_column.m_initialIndent == std::string::npos || m_column.m_width > m_column.m_initialIndent );
122 | calcLength();
123 | if( m_len == 0 )
124 | m_stringIndex++; // Empty string
125 | }
126 |
127 | auto operator *() const -> std::string {
128 | assert( m_stringIndex < m_column.m_strings.size() );
129 | assert( m_pos <= m_end );
130 | return addIndentAndSuffix(line().substr(m_pos, m_len));
131 | }
132 |
133 | auto operator ++() -> iterator& {
134 | m_pos += m_len;
135 | if( m_pos < line().size() && line()[m_pos] == '\n' )
136 | m_pos += 1;
137 | else
138 | while( m_pos < line().size() && isWhitespace( line()[m_pos] ) )
139 | ++m_pos;
140 |
141 | if( m_pos == line().size() ) {
142 | m_pos = 0;
143 | ++m_stringIndex;
144 | }
145 | if( m_stringIndex < m_column.m_strings.size() )
146 | calcLength();
147 | return *this;
148 | }
149 | auto operator ++(int) -> iterator {
150 | iterator prev( *this );
151 | operator++();
152 | return prev;
153 | }
154 |
155 | auto operator ==( iterator const& other ) const -> bool {
156 | return
157 | m_pos == other.m_pos &&
158 | m_stringIndex == other.m_stringIndex &&
159 | &m_column == &other.m_column;
160 | }
161 | auto operator !=( iterator const& other ) const -> bool {
162 | return !operator==( other );
163 | }
164 | };
165 | using const_iterator = iterator;
166 |
167 | explicit Column( std::string const& text ) { m_strings.push_back( text ); }
168 |
169 | auto width( size_t newWidth ) -> Column& {
170 | assert( newWidth > 0 );
171 | m_width = newWidth;
172 | return *this;
173 | }
174 | auto indent( size_t newIndent ) -> Column& {
175 | m_indent = newIndent;
176 | return *this;
177 | }
178 | auto initialIndent( size_t newIndent ) -> Column& {
179 | m_initialIndent = newIndent;
180 | return *this;
181 | }
182 |
183 | auto width() const -> size_t { return m_width; }
184 | auto begin() const -> iterator { return iterator( *this ); }
185 | auto end() const -> iterator { return { *this, m_strings.size() }; }
186 |
187 | inline friend std::ostream& operator << ( std::ostream& os, Column const& col ) {
188 | bool first = true;
189 | for( auto line : col ) {
190 | if( first )
191 | first = false;
192 | else
193 | os << "\n";
194 | os << line;
195 | }
196 | return os;
197 | }
198 |
199 | auto operator + ( Column const& other ) -> Columns;
200 |
201 | auto toString() const -> std::string {
202 | std::ostringstream oss;
203 | oss << *this;
204 | return oss.str();
205 | }
206 | };
207 |
208 | class Spacer : public Column {
209 |
210 | public:
211 | explicit Spacer( size_t spaceWidth ) : Column( "" ) {
212 | width( spaceWidth );
213 | }
214 | };
215 |
216 | class Columns {
217 | std::vector m_columns;
218 |
219 | public:
220 |
221 | class iterator {
222 | friend Columns;
223 | struct EndTag {};
224 |
225 | std::vector const& m_columns;
226 | std::vector m_iterators;
227 | size_t m_activeIterators;
228 |
229 | iterator( Columns const& columns, EndTag )
230 | : m_columns( columns.m_columns ),
231 | m_activeIterators( 0 )
232 | {
233 | m_iterators.reserve( m_columns.size() );
234 |
235 | for( auto const& col : m_columns )
236 | m_iterators.push_back( col.end() );
237 | }
238 |
239 | public:
240 | using difference_type = std::ptrdiff_t;
241 | using value_type = std::string;
242 | using pointer = value_type*;
243 | using reference = value_type&;
244 | using iterator_category = std::forward_iterator_tag;
245 |
246 | explicit iterator( Columns const& columns )
247 | : m_columns( columns.m_columns ),
248 | m_activeIterators( m_columns.size() )
249 | {
250 | m_iterators.reserve( m_columns.size() );
251 |
252 | for( auto const& col : m_columns )
253 | m_iterators.push_back( col.begin() );
254 | }
255 |
256 | auto operator ==( iterator const& other ) const -> bool {
257 | return m_iterators == other.m_iterators;
258 | }
259 | auto operator !=( iterator const& other ) const -> bool {
260 | return m_iterators != other.m_iterators;
261 | }
262 | auto operator *() const -> std::string {
263 | std::string row, padding;
264 |
265 | for( size_t i = 0; i < m_columns.size(); ++i ) {
266 | auto width = m_columns[i].width();
267 | if( m_iterators[i] != m_columns[i].end() ) {
268 | std::string col = *m_iterators[i];
269 | row += padding + col;
270 | if( col.size() < width )
271 | padding = std::string( width - col.size(), ' ' );
272 | else
273 | padding = "";
274 | }
275 | else {
276 | padding += std::string( width, ' ' );
277 | }
278 | }
279 | return row;
280 | }
281 | auto operator ++() -> iterator& {
282 | for( size_t i = 0; i < m_columns.size(); ++i ) {
283 | if (m_iterators[i] != m_columns[i].end())
284 | ++m_iterators[i];
285 | }
286 | return *this;
287 | }
288 | auto operator ++(int) -> iterator {
289 | iterator prev( *this );
290 | operator++();
291 | return prev;
292 | }
293 | };
294 | using const_iterator = iterator;
295 |
296 | auto begin() const -> iterator { return iterator( *this ); }
297 | auto end() const -> iterator { return { *this, iterator::EndTag() }; }
298 |
299 | auto operator += ( Column const& col ) -> Columns& {
300 | m_columns.push_back( col );
301 | return *this;
302 | }
303 | auto operator + ( Column const& col ) -> Columns {
304 | Columns combined = *this;
305 | combined += col;
306 | return combined;
307 | }
308 |
309 | inline friend std::ostream& operator << ( std::ostream& os, Columns const& cols ) {
310 |
311 | bool first = true;
312 | for( auto line : cols ) {
313 | if( first )
314 | first = false;
315 | else
316 | os << "\n";
317 | os << line;
318 | }
319 | return os;
320 | }
321 |
322 | auto toString() const -> std::string {
323 | std::ostringstream oss;
324 | oss << *this;
325 | return oss.str();
326 | }
327 | };
328 |
329 | inline auto Column::operator + ( Column const& other ) -> Columns {
330 | Columns cols;
331 | cols += *this;
332 | cols += other;
333 | return cols;
334 | }
335 | }
336 |
337 | #endif // TEXTFLOW_HPP_INCLUDED
338 |
--------------------------------------------------------------------------------
/include/clara_textflow.hpp:
--------------------------------------------------------------------------------
1 | // TextFlowCpp
2 | //
3 | // A single-header library for wrapping and laying out basic text, by Phil Nash
4 | //
5 | // Distributed under the Boost Software License, Version 1.0. (See accompanying
6 | // file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7 | //
8 | // This project is hosted at https://github.com/philsquared/textflowcpp
9 |
10 | #ifndef CLARA_TEXTFLOW_HPP_INCLUDED
11 | #define CLARA_TEXTFLOW_HPP_INCLUDED
12 |
13 | #include
14 | #include
15 | #include
16 | #include
17 |
18 | #ifndef CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH
19 | #define CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH 80
20 | #endif
21 |
22 |
23 | namespace clara { namespace TextFlow {
24 |
25 | inline auto isWhitespace( char c ) -> bool {
26 | static std::string chars = " \t\n\r";
27 | return chars.find( c ) != std::string::npos;
28 | }
29 | inline auto isBreakableBefore( char c ) -> bool {
30 | static std::string chars = "[({<|";
31 | return chars.find( c ) != std::string::npos;
32 | }
33 | inline auto isBreakableAfter( char c ) -> bool {
34 | static std::string chars = "])}>.,:;*+-=&/\\";
35 | return chars.find( c ) != std::string::npos;
36 | }
37 |
38 | class Columns;
39 |
40 | class Column {
41 | std::vector m_strings;
42 | size_t m_width = CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH;
43 | size_t m_indent = 0;
44 | size_t m_initialIndent = std::string::npos;
45 |
46 | public:
47 | class iterator {
48 | friend Column;
49 |
50 | Column const& m_column;
51 | size_t m_stringIndex = 0;
52 | size_t m_pos = 0;
53 |
54 | size_t m_len = 0;
55 | size_t m_end = 0;
56 | bool m_suffix = false;
57 |
58 | iterator( Column const& column, size_t stringIndex )
59 | : m_column( column ),
60 | m_stringIndex( stringIndex )
61 | {}
62 |
63 | auto line() const -> std::string const& { return m_column.m_strings[m_stringIndex]; }
64 |
65 | auto isBoundary( size_t at ) const -> bool {
66 | assert( at > 0 );
67 | assert( at <= line().size() );
68 |
69 | return at == line().size() ||
70 | ( isWhitespace( line()[at] ) && !isWhitespace( line()[at-1] ) ) ||
71 | isBreakableBefore( line()[at] ) ||
72 | isBreakableAfter( line()[at-1] );
73 | }
74 |
75 | void calcLength() {
76 | assert( m_stringIndex < m_column.m_strings.size() );
77 |
78 | m_suffix = false;
79 | auto width = m_column.m_width-indent();
80 | m_end = m_pos;
81 | while( m_end < line().size() && line()[m_end] != '\n' )
82 | ++m_end;
83 |
84 | if( m_end < m_pos + width ) {
85 | m_len = m_end - m_pos;
86 | }
87 | else {
88 | size_t len = width;
89 | while (len > 0 && !isBoundary(m_pos + len))
90 | --len;
91 | while (len > 0 && isWhitespace( line()[m_pos + len - 1] ))
92 | --len;
93 |
94 | if (len > 0) {
95 | m_len = len;
96 | } else {
97 | m_suffix = true;
98 | m_len = width - 1;
99 | }
100 | }
101 | }
102 |
103 | auto indent() const -> size_t {
104 | auto initial = m_pos == 0 && m_stringIndex == 0 ? m_column.m_initialIndent : std::string::npos;
105 | return initial == std::string::npos ? m_column.m_indent : initial;
106 | }
107 |
108 | auto addIndentAndSuffix(std::string const &plain) const -> std::string {
109 | return std::string( indent(), ' ' ) + (m_suffix ? plain + "-" : plain);
110 | }
111 |
112 | public:
113 | using difference_type = std::ptrdiff_t;
114 | using value_type = std::string;
115 | using pointer = value_type*;
116 | using reference = value_type&;
117 | using iterator_category = std::forward_iterator_tag;
118 |
119 | explicit iterator( Column const& column ) : m_column( column ) {
120 | assert( m_column.m_width > m_column.m_indent );
121 | assert( m_column.m_initialIndent == std::string::npos || m_column.m_width > m_column.m_initialIndent );
122 | calcLength();
123 | if( m_len == 0 )
124 | m_stringIndex++; // Empty string
125 | }
126 |
127 | auto operator *() const -> std::string {
128 | assert( m_stringIndex < m_column.m_strings.size() );
129 | assert( m_pos <= m_end );
130 | return addIndentAndSuffix(line().substr(m_pos, m_len));
131 | }
132 |
133 | auto operator ++() -> iterator& {
134 | m_pos += m_len;
135 | if( m_pos < line().size() && line()[m_pos] == '\n' )
136 | m_pos += 1;
137 | else
138 | while( m_pos < line().size() && isWhitespace( line()[m_pos] ) )
139 | ++m_pos;
140 |
141 | if( m_pos == line().size() ) {
142 | m_pos = 0;
143 | ++m_stringIndex;
144 | }
145 | if( m_stringIndex < m_column.m_strings.size() )
146 | calcLength();
147 | return *this;
148 | }
149 | auto operator ++(int) -> iterator {
150 | iterator prev( *this );
151 | operator++();
152 | return prev;
153 | }
154 |
155 | auto operator ==( iterator const& other ) const -> bool {
156 | return
157 | m_pos == other.m_pos &&
158 | m_stringIndex == other.m_stringIndex &&
159 | &m_column == &other.m_column;
160 | }
161 | auto operator !=( iterator const& other ) const -> bool {
162 | return !operator==( other );
163 | }
164 | };
165 | using const_iterator = iterator;
166 |
167 | explicit Column( std::string const& text ) { m_strings.push_back( text ); }
168 |
169 | auto width( size_t newWidth ) -> Column& {
170 | assert( newWidth > 0 );
171 | m_width = newWidth;
172 | return *this;
173 | }
174 | auto indent( size_t newIndent ) -> Column& {
175 | m_indent = newIndent;
176 | return *this;
177 | }
178 | auto initialIndent( size_t newIndent ) -> Column& {
179 | m_initialIndent = newIndent;
180 | return *this;
181 | }
182 |
183 | auto width() const -> size_t { return m_width; }
184 | auto begin() const -> iterator { return iterator( *this ); }
185 | auto end() const -> iterator { return { *this, m_strings.size() }; }
186 |
187 | inline friend std::ostream& operator << ( std::ostream& os, Column const& col ) {
188 | bool first = true;
189 | for( auto line : col ) {
190 | if( first )
191 | first = false;
192 | else
193 | os << "\n";
194 | os << line;
195 | }
196 | return os;
197 | }
198 |
199 | auto operator + ( Column const& other ) -> Columns;
200 |
201 | auto toString() const -> std::string {
202 | std::ostringstream oss;
203 | oss << *this;
204 | return oss.str();
205 | }
206 | };
207 |
208 | class Spacer : public Column {
209 |
210 | public:
211 | explicit Spacer( size_t spaceWidth ) : Column( "" ) {
212 | width( spaceWidth );
213 | }
214 | };
215 |
216 | class Columns {
217 | std::vector m_columns;
218 |
219 | public:
220 |
221 | class iterator {
222 | friend Columns;
223 | struct EndTag {};
224 |
225 | std::vector const& m_columns;
226 | std::vector m_iterators;
227 | size_t m_activeIterators;
228 |
229 | iterator( Columns const& columns, EndTag )
230 | : m_columns( columns.m_columns ),
231 | m_activeIterators( 0 )
232 | {
233 | m_iterators.reserve( m_columns.size() );
234 |
235 | for( auto const& col : m_columns )
236 | m_iterators.push_back( col.end() );
237 | }
238 |
239 | public:
240 | using difference_type = std::ptrdiff_t;
241 | using value_type = std::string;
242 | using pointer = value_type*;
243 | using reference = value_type&;
244 | using iterator_category = std::forward_iterator_tag;
245 |
246 | explicit iterator( Columns const& columns )
247 | : m_columns( columns.m_columns ),
248 | m_activeIterators( m_columns.size() )
249 | {
250 | m_iterators.reserve( m_columns.size() );
251 |
252 | for( auto const& col : m_columns )
253 | m_iterators.push_back( col.begin() );
254 | }
255 |
256 | auto operator ==( iterator const& other ) const -> bool {
257 | return m_iterators == other.m_iterators;
258 | }
259 | auto operator !=( iterator const& other ) const -> bool {
260 | return m_iterators != other.m_iterators;
261 | }
262 | auto operator *() const -> std::string {
263 | std::string row, padding;
264 |
265 | for( size_t i = 0; i < m_columns.size(); ++i ) {
266 | auto width = m_columns[i].width();
267 | if( m_iterators[i] != m_columns[i].end() ) {
268 | std::string col = *m_iterators[i];
269 | row += padding + col;
270 | if( col.size() < width )
271 | padding = std::string( width - col.size(), ' ' );
272 | else
273 | padding = "";
274 | }
275 | else {
276 | padding += std::string( width, ' ' );
277 | }
278 | }
279 | return row;
280 | }
281 | auto operator ++() -> iterator& {
282 | for( size_t i = 0; i < m_columns.size(); ++i ) {
283 | if (m_iterators[i] != m_columns[i].end())
284 | ++m_iterators[i];
285 | }
286 | return *this;
287 | }
288 | auto operator ++(int) -> iterator {
289 | iterator prev( *this );
290 | operator++();
291 | return prev;
292 | }
293 | };
294 | using const_iterator = iterator;
295 |
296 | auto begin() const -> iterator { return iterator( *this ); }
297 | auto end() const -> iterator { return { *this, iterator::EndTag() }; }
298 |
299 | auto operator += ( Column const& col ) -> Columns& {
300 | m_columns.push_back( col );
301 | return *this;
302 | }
303 | auto operator + ( Column const& col ) -> Columns {
304 | Columns combined = *this;
305 | combined += col;
306 | return combined;
307 | }
308 |
309 | inline friend std::ostream& operator << ( std::ostream& os, Columns const& cols ) {
310 |
311 | bool first = true;
312 | for( auto line : cols ) {
313 | if( first )
314 | first = false;
315 | else
316 | os << "\n";
317 | os << line;
318 | }
319 | return os;
320 | }
321 |
322 | auto toString() const -> std::string {
323 | std::ostringstream oss;
324 | oss << *this;
325 | return oss.str();
326 | }
327 | };
328 |
329 | inline auto Column::operator + ( Column const& other ) -> Columns {
330 | Columns cols;
331 | cols += *this;
332 | cols += other;
333 | return cols;
334 | }
335 | }}
336 |
337 | #endif // CLARA_TEXTFLOW_HPP_INCLUDED
338 |
--------------------------------------------------------------------------------
/CMake/FindLcov.cmake:
--------------------------------------------------------------------------------
1 | # This file is part of CMake-codecov.
2 | #
3 | # Copyright (c)
4 | # 2015-2017 RWTH Aachen University, Federal Republic of Germany
5 | #
6 | # See the LICENSE file in the package base directory for details
7 | #
8 | # Written by Alexander Haase, alexander.haase@rwth-aachen.de
9 | #
10 |
11 |
12 | # configuration
13 | set(LCOV_DATA_PATH "${CMAKE_BINARY_DIR}/lcov/data")
14 | set(LCOV_DATA_PATH_INIT "${LCOV_DATA_PATH}/init")
15 | set(LCOV_DATA_PATH_CAPTURE "${LCOV_DATA_PATH}/capture")
16 | set(LCOV_HTML_PATH "${CMAKE_BINARY_DIR}/lcov/html")
17 |
18 |
19 |
20 |
21 | # Search for Gcov which is used by Lcov.
22 | find_package(Gcov)
23 |
24 |
25 |
26 |
27 | # This function will add lcov evaluation for target . Only sources of
28 | # this target will be evaluated and no dependencies will be added. It will call
29 | # geninfo on any source file of once and store the info file in the same
30 | # directory.
31 | #
32 | # Note: This function is only a wrapper to define this function always, even if
33 | # coverage is not supported by the compiler or disabled. This function must
34 | # be defined here, because the module will be exited, if there is no coverage
35 | # support by the compiler or it is disabled by the user.
36 | function (add_lcov_target TNAME)
37 | if (LCOV_FOUND)
38 | # capture initial coverage data
39 | lcov_capture_initial_tgt(${TNAME})
40 |
41 | # capture coverage data after execution
42 | lcov_capture_tgt(${TNAME})
43 | endif ()
44 | endfunction (add_lcov_target)
45 |
46 |
47 |
48 |
49 | # include required Modules
50 | include(FindPackageHandleStandardArgs)
51 |
52 | # Search for required lcov binaries.
53 | find_program(LCOV_BIN lcov)
54 | find_program(GENINFO_BIN geninfo)
55 | find_program(GENHTML_BIN genhtml)
56 | find_package_handle_standard_args(lcov
57 | REQUIRED_VARS LCOV_BIN GENINFO_BIN GENHTML_BIN
58 | )
59 |
60 | # enable genhtml C++ demangeling, if c++filt is found.
61 | set(GENHTML_CPPFILT_FLAG "")
62 | find_program(CPPFILT_BIN c++filt)
63 | if (NOT CPPFILT_BIN STREQUAL "")
64 | set(GENHTML_CPPFILT_FLAG "--demangle-cpp")
65 | endif (NOT CPPFILT_BIN STREQUAL "")
66 |
67 | # enable no-external flag for lcov, if available.
68 | if (GENINFO_BIN AND NOT DEFINED GENINFO_EXTERN_FLAG)
69 | set(FLAG "")
70 | execute_process(COMMAND ${GENINFO_BIN} --help OUTPUT_VARIABLE GENINFO_HELP)
71 | string(REGEX MATCH "external" GENINFO_RES "${GENINFO_HELP}")
72 | if (GENINFO_RES)
73 | set(FLAG "--no-external")
74 | endif ()
75 |
76 | set(GENINFO_EXTERN_FLAG "${FLAG}"
77 | CACHE STRING "Geninfo flag to exclude system sources.")
78 | endif ()
79 |
80 | # If Lcov was not found, exit module now.
81 | if (NOT LCOV_FOUND)
82 | return()
83 | endif (NOT LCOV_FOUND)
84 |
85 |
86 |
87 |
88 | # Create directories to be used.
89 | file(MAKE_DIRECTORY ${LCOV_DATA_PATH_INIT})
90 | file(MAKE_DIRECTORY ${LCOV_DATA_PATH_CAPTURE})
91 |
92 | set(LCOV_REMOVE_PATTERNS "")
93 |
94 | # This function will merge lcov files to a single target file. Additional lcov
95 | # flags may be set with setting LCOV_EXTRA_FLAGS before calling this function.
96 | function (lcov_merge_files OUTFILE ...)
97 | # Remove ${OUTFILE} from ${ARGV} and generate lcov parameters with files.
98 | list(REMOVE_AT ARGV 0)
99 |
100 | # Generate merged file.
101 | string(REPLACE "${CMAKE_BINARY_DIR}/" "" FILE_REL "${OUTFILE}")
102 | add_custom_command(OUTPUT "${OUTFILE}.raw"
103 | COMMAND cat ${ARGV} > ${OUTFILE}.raw
104 | DEPENDS ${ARGV}
105 | COMMENT "Generating ${FILE_REL}"
106 | )
107 |
108 | add_custom_command(OUTPUT "${OUTFILE}"
109 | COMMAND ${LCOV_BIN} --quiet -a ${OUTFILE}.raw --output-file ${OUTFILE}
110 | --base-directory ${PROJECT_SOURCE_DIR} ${LCOV_EXTRA_FLAGS}
111 | COMMAND ${LCOV_BIN} --quiet -r ${OUTFILE} ${LCOV_REMOVE_PATTERNS}
112 | --output-file ${OUTFILE} ${LCOV_EXTRA_FLAGS}
113 | DEPENDS ${OUTFILE}.raw
114 | COMMENT "Post-processing ${FILE_REL}"
115 | )
116 | endfunction ()
117 |
118 |
119 |
120 |
121 | # Add a new global target to generate initial coverage reports for all targets.
122 | # This target will be used to generate the global initial info file, which is
123 | # used to gather even empty report data.
124 | if (NOT TARGET lcov-capture-init)
125 | add_custom_target(lcov-capture-init)
126 | set(LCOV_CAPTURE_INIT_FILES "" CACHE INTERNAL "")
127 | endif (NOT TARGET lcov-capture-init)
128 |
129 |
130 | # This function will add initial capture of coverage data for target ,
131 | # which is needed to get also data for objects, which were not loaded at
132 | # execution time. It will call geninfo for every source file of once and
133 | # store the info file in the same directory.
134 | function (lcov_capture_initial_tgt TNAME)
135 | # We don't have to check, if the target has support for coverage, thus this
136 | # will be checked by add_coverage_target in Findcoverage.cmake. Instead we
137 | # have to determine which gcov binary to use.
138 | get_target_property(TSOURCES ${TNAME} SOURCES)
139 | set(SOURCES "")
140 | set(TCOMPILER "")
141 | foreach (FILE ${TSOURCES})
142 | codecov_path_of_source(${FILE} FILE)
143 | if (NOT "${FILE}" STREQUAL "")
144 | codecov_lang_of_source(${FILE} LANG)
145 | if (NOT "${LANG}" STREQUAL "")
146 | list(APPEND SOURCES "${FILE}")
147 | set(TCOMPILER ${CMAKE_${LANG}_COMPILER_ID})
148 | endif ()
149 | endif ()
150 | endforeach ()
151 |
152 | # If no gcov binary was found, coverage data can't be evaluated.
153 | if (NOT GCOV_${TCOMPILER}_BIN)
154 | message(WARNING "No coverage evaluation binary found for ${TCOMPILER}.")
155 | return()
156 | endif ()
157 |
158 | set(GCOV_BIN "${GCOV_${TCOMPILER}_BIN}")
159 | set(GCOV_ENV "${GCOV_${TCOMPILER}_ENV}")
160 |
161 |
162 | set(TDIR ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${TNAME}.dir)
163 | set(GENINFO_FILES "")
164 | foreach(FILE ${SOURCES})
165 | # generate empty coverage files
166 | set(OUTFILE "${TDIR}/${FILE}.info.init")
167 | list(APPEND GENINFO_FILES ${OUTFILE})
168 |
169 | add_custom_command(OUTPUT ${OUTFILE} COMMAND ${GCOV_ENV} ${GENINFO_BIN}
170 | --quiet --base-directory ${PROJECT_SOURCE_DIR} --initial
171 | --gcov-tool ${GCOV_BIN} --output-filename ${OUTFILE}
172 | ${GENINFO_EXTERN_FLAG} ${TDIR}/${FILE}.gcno
173 | DEPENDS ${TNAME}
174 | COMMENT "Capturing initial coverage data for ${FILE}"
175 | )
176 | endforeach()
177 |
178 | # Concatenate all files generated by geninfo to a single file per target.
179 | set(OUTFILE "${LCOV_DATA_PATH_INIT}/${TNAME}.info")
180 | set(LCOV_EXTRA_FLAGS "--initial")
181 | lcov_merge_files("${OUTFILE}" ${GENINFO_FILES})
182 | add_custom_target(${TNAME}-capture-init ALL DEPENDS ${OUTFILE})
183 |
184 | # add geninfo file generation to global lcov-geninfo target
185 | add_dependencies(lcov-capture-init ${TNAME}-capture-init)
186 | set(LCOV_CAPTURE_INIT_FILES "${LCOV_CAPTURE_INIT_FILES}"
187 | "${OUTFILE}" CACHE INTERNAL ""
188 | )
189 | endfunction (lcov_capture_initial_tgt)
190 |
191 |
192 | # This function will generate the global info file for all targets. It has to be
193 | # called after all other CMake functions in the root CMakeLists.txt file, to get
194 | # a full list of all targets that generate coverage data.
195 | function (lcov_capture_initial)
196 | # Skip this function (and do not create the following targets), if there are
197 | # no input files.
198 | if ("${LCOV_CAPTURE_INIT_FILES}" STREQUAL "")
199 | return()
200 | endif ()
201 |
202 | # Add a new target to merge the files of all targets.
203 | set(OUTFILE "${LCOV_DATA_PATH_INIT}/all_targets.info")
204 | lcov_merge_files("${OUTFILE}" ${LCOV_CAPTURE_INIT_FILES})
205 | add_custom_target(lcov-geninfo-init ALL DEPENDS ${OUTFILE}
206 | lcov-capture-init
207 | )
208 | endfunction (lcov_capture_initial)
209 |
210 |
211 |
212 |
213 | # Add a new global target to generate coverage reports for all targets. This
214 | # target will be used to generate the global info file.
215 | if (NOT TARGET lcov-capture)
216 | add_custom_target(lcov-capture)
217 | set(LCOV_CAPTURE_FILES "" CACHE INTERNAL "")
218 | endif (NOT TARGET lcov-capture)
219 |
220 |
221 | # This function will add capture of coverage data for target , which is
222 | # needed to get also data for objects, which were not loaded at execution time.
223 | # It will call geninfo for every source file of once and store the info
224 | # file in the same directory.
225 | function (lcov_capture_tgt TNAME)
226 | # We don't have to check, if the target has support for coverage, thus this
227 | # will be checked by add_coverage_target in Findcoverage.cmake. Instead we
228 | # have to determine which gcov binary to use.
229 | get_target_property(TSOURCES ${TNAME} SOURCES)
230 | set(SOURCES "")
231 | set(TCOMPILER "")
232 | foreach (FILE ${TSOURCES})
233 | codecov_path_of_source(${FILE} FILE)
234 | if (NOT "${FILE}" STREQUAL "")
235 | codecov_lang_of_source(${FILE} LANG)
236 | if (NOT "${LANG}" STREQUAL "")
237 | list(APPEND SOURCES "${FILE}")
238 | set(TCOMPILER ${CMAKE_${LANG}_COMPILER_ID})
239 | endif ()
240 | endif ()
241 | endforeach ()
242 |
243 | # If no gcov binary was found, coverage data can't be evaluated.
244 | if (NOT GCOV_${TCOMPILER}_BIN)
245 | message(WARNING "No coverage evaluation binary found for ${TCOMPILER}.")
246 | return()
247 | endif ()
248 |
249 | set(GCOV_BIN "${GCOV_${TCOMPILER}_BIN}")
250 | set(GCOV_ENV "${GCOV_${TCOMPILER}_ENV}")
251 |
252 |
253 | set(TDIR ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${TNAME}.dir)
254 | set(GENINFO_FILES "")
255 | foreach(FILE ${SOURCES})
256 | # Generate coverage files. If no .gcda file was generated during
257 | # execution, the empty coverage file will be used instead.
258 | set(OUTFILE "${TDIR}/${FILE}.info")
259 | list(APPEND GENINFO_FILES ${OUTFILE})
260 |
261 | add_custom_command(OUTPUT ${OUTFILE}
262 | COMMAND test -f "${TDIR}/${FILE}.gcda"
263 | && ${GCOV_ENV} ${GENINFO_BIN} --quiet --base-directory
264 | ${PROJECT_SOURCE_DIR} --gcov-tool ${GCOV_BIN}
265 | --output-filename ${OUTFILE} ${GENINFO_EXTERN_FLAG}
266 | ${TDIR}/${FILE}.gcda
267 | || cp ${OUTFILE}.init ${OUTFILE}
268 | DEPENDS ${TNAME} ${TNAME}-capture-init
269 | COMMENT "Capturing coverage data for ${FILE}"
270 | )
271 | endforeach()
272 |
273 | # Concatenate all files generated by geninfo to a single file per target.
274 | set(OUTFILE "${LCOV_DATA_PATH_CAPTURE}/${TNAME}.info")
275 | lcov_merge_files("${OUTFILE}" ${GENINFO_FILES})
276 | add_custom_target(${TNAME}-geninfo DEPENDS ${OUTFILE})
277 |
278 | # add geninfo file generation to global lcov-capture target
279 | add_dependencies(lcov-capture ${TNAME}-geninfo)
280 | set(LCOV_CAPTURE_FILES "${LCOV_CAPTURE_FILES}" "${OUTFILE}" CACHE INTERNAL
281 | ""
282 | )
283 |
284 | # Add target for generating html output for this target only.
285 | file(MAKE_DIRECTORY ${LCOV_HTML_PATH}/${TNAME})
286 | add_custom_target(${TNAME}-genhtml
287 | COMMAND ${GENHTML_BIN} --quiet --sort --prefix ${PROJECT_SOURCE_DIR}
288 | --baseline-file ${LCOV_DATA_PATH_INIT}/${TNAME}.info
289 | --output-directory ${LCOV_HTML_PATH}/${TNAME}
290 | --title "${CMAKE_PROJECT_NAME} - target ${TNAME}"
291 | ${GENHTML_CPPFILT_FLAG} ${OUTFILE}
292 | DEPENDS ${TNAME}-geninfo ${TNAME}-capture-init
293 | )
294 | endfunction (lcov_capture_tgt)
295 |
296 |
297 | # This function will generate the global info file for all targets. It has to be
298 | # called after all other CMake functions in the root CMakeLists.txt file, to get
299 | # a full list of all targets that generate coverage data.
300 | function (lcov_capture)
301 | # Skip this function (and do not create the following targets), if there are
302 | # no input files.
303 | if ("${LCOV_CAPTURE_FILES}" STREQUAL "")
304 | return()
305 | endif ()
306 |
307 | # Add a new target to merge the files of all targets.
308 | set(OUTFILE "${LCOV_DATA_PATH_CAPTURE}/all_targets.info")
309 | lcov_merge_files("${OUTFILE}" ${LCOV_CAPTURE_FILES})
310 | add_custom_target(lcov-geninfo DEPENDS ${OUTFILE} lcov-capture)
311 |
312 | # Add a new global target for all lcov targets. This target could be used to
313 | # generate the lcov html output for the whole project instead of calling
314 | # -geninfo and -genhtml for each target. It will also be
315 | # used to generate a html site for all project data together instead of one
316 | # for each target.
317 | if (NOT TARGET lcov)
318 | file(MAKE_DIRECTORY ${LCOV_HTML_PATH}/all_targets)
319 | add_custom_target(lcov
320 | COMMAND ${GENHTML_BIN} --quiet --sort
321 | --baseline-file ${LCOV_DATA_PATH_INIT}/all_targets.info
322 | --output-directory ${LCOV_HTML_PATH}/all_targets
323 | --title "${CMAKE_PROJECT_NAME}" --prefix "${PROJECT_SOURCE_DIR}"
324 | ${GENHTML_CPPFILT_FLAG} ${OUTFILE}
325 | DEPENDS lcov-geninfo-init lcov-geninfo
326 | )
327 | endif ()
328 | endfunction (lcov_capture)
329 |
330 |
331 |
332 |
333 | # Add a new global target to generate the lcov html report for the whole project
334 | # instead of calling -genhtml for each target (to create an own report
335 | # for each target). Instead of the lcov target it does not require geninfo for
336 | # all targets, so you have to call -geninfo to generate the info files
337 | # the targets you'd like to have in your report or lcov-geninfo for generating
338 | # info files for all targets before calling lcov-genhtml.
339 | file(MAKE_DIRECTORY ${LCOV_HTML_PATH}/selected_targets)
340 | if (NOT TARGET lcov-genhtml)
341 | add_custom_target(lcov-genhtml
342 | COMMAND ${GENHTML_BIN}
343 | --quiet
344 | --output-directory ${LCOV_HTML_PATH}/selected_targets
345 | --title \"${CMAKE_PROJECT_NAME} - targets `find
346 | ${LCOV_DATA_PATH_CAPTURE} -name \"*.info\" ! -name
347 | \"all_targets.info\" -exec basename {} .info \\\;`\"
348 | --prefix ${PROJECT_SOURCE_DIR}
349 | --sort
350 | ${GENHTML_CPPFILT_FLAG}
351 | `find ${LCOV_DATA_PATH_CAPTURE} -name \"*.info\" ! -name
352 | \"all_targets.info\"`
353 | )
354 | endif (NOT TARGET lcov-genhtml)
355 |
--------------------------------------------------------------------------------
/src/ClaraTests.cpp:
--------------------------------------------------------------------------------
1 | #include "clara.hpp"
2 |
3 | #include "catch.hpp"
4 |
5 | #include
6 |
7 | using namespace clara;
8 |
9 | namespace Catch {
10 | template<>
11 | struct StringMaker {
12 | static std::string convert( clara::detail::InternalParseResult const& result ) {
13 | switch( result.type() ) {
14 | case clara::detail::ResultBase::Ok:
15 | return "Ok";
16 | case clara::detail::ResultBase::LogicError:
17 | return "LogicError '" + result.errorMessage() + "'";
18 | case clara::detail::ResultBase::RuntimeError:
19 | return "RuntimeError: '" + result.errorMessage() + "'";
20 | default:
21 | return "Unknow type: " + std::to_string( static_cast( result.type() ) );
22 | }
23 | }
24 | };
25 | }
26 |
27 | std::string toString( Opt const& opt ) {
28 | std::ostringstream oss;
29 | oss << (Parser() | opt);
30 | return oss.str();
31 | }
32 | std::string toString( Parser const& p ) {
33 | std::ostringstream oss;
34 | oss << p;
35 | return oss.str();
36 | }
37 |
38 | // !TBD
39 | // for Catch:
40 | // error on unrecognised?
41 |
42 | // Beyond Catch:
43 | // exceptions or not
44 | // error on unmet requireds
45 | // enum mapping
46 | // sets of values (in addition to vectors)
47 | // arg literals
48 | // --help for option names/ args/ arg literals
49 | // other dependencies/ hierarchical parsers
50 | // Exclusive() parser for choices
51 |
52 | TEST_CASE( "single parsers" ) {
53 |
54 | std::string name;
55 | auto p = Opt(name, "name")
56 | ["-n"]["--name"]
57 | ("the name to use");
58 |
59 | REQUIRE( name == "" );
60 |
61 | SECTION( "-n" ) {
62 | p.parse( Args{ "TestApp", "-n", "Vader" } );
63 | REQUIRE( name == "Vader");
64 | }
65 | SECTION( "--name" ) {
66 | p.parse( Args{ "TestApp", "--name", "Vader" } );
67 | REQUIRE( name == "Vader");
68 | }
69 | SECTION( "-n:" ) {
70 | p.parse( Args{ "TestApp", "-n:Vader" } );
71 | REQUIRE( name == "Vader");
72 | }
73 | SECTION( "-n=" ) {
74 | p.parse( Args{ "TestApp", "-n=Vader" } );
75 | REQUIRE( name == "Vader");
76 | }
77 | SECTION( "no args" ) {
78 | p.parse( Args{ "TestApp" } );
79 | REQUIRE( name == "");
80 | }
81 | SECTION( "different args" ) {
82 | p.parse( Args{ "TestApp", "-f" } );
83 | REQUIRE( name == "");
84 | }
85 | }
86 |
87 | struct Config {
88 | int m_rngSeed;
89 | std::string m_name;
90 | std::vector m_tests;
91 | bool m_flag = false;
92 | double m_value = 0;
93 | };
94 |
95 | TEST_CASE( "Combined parser" ) {
96 | Config config;
97 |
98 | bool showHelp = false;
99 | auto parser
100 | = Help( showHelp )
101 | | Opt( config.m_rngSeed, "time|value" )
102 | ["--rng-seed"]["-r"]
103 | ("set a specific seed for random numbers" )
104 | .required()
105 | | Opt( config.m_name, "name" )
106 | ["-n"]["--name"]
107 | ( "the name to use" )
108 | | Opt( config.m_flag )
109 | ["-f"]["--flag"]
110 | ( "a flag to set" )
111 | | Opt( [&]( double value ){ config.m_value = value; }, "number" )
112 | ["-d"]["--double"]
113 | ( "just some number" )
114 | | Arg( config.m_tests, "test name|tags|pattern" )
115 | ( "which test or tests to use" );
116 |
117 | SECTION( "usage" ) {
118 | REQUIRE(toString(parser) ==
119 | "usage:\n"
120 | " [ ... ] options\n"
121 | "\n"
122 | "where options are:\n"
123 | " -?, -h, --help display usage information\n"
124 | " --rng-seed, -r