From 6ea1e36c57ebc8d6ce0ff0996f20e5382fde0158 Mon Sep 17 00:00:00 2001 From: Abdessattar Sassi <457645+abdes@users.noreply.github.com> Date: Thu, 17 Feb 2022 22:10:22 +0400 Subject: [PATCH] Initial commit --- .clang-format | 137 + .clang-tidy | 18 + .clangd.in | 8 + .cmake-format.yaml | 63 + .commitlintrc.json | 5 + .devcontainer/.p10k.zsh | 1641 ++++++++++ .devcontainer/.zshrc | 120 + .devcontainer/Dockerfile | 65 + .devcontainer/devcontainer.json | 59 + .editorconfig | 11 + .gitattributes | 12 + .github/workflows/gh-pages.yml | 64 + .github/workflows/linux-build.yml | 50 + .github/workflows/windows-build.yml | 47 + .gitignore | 239 ++ .gitmodules | 9 + .husky/commit-msg | 4 + .versionrc.json | 46 + .vscode/extensions.json | 23 + .vscode/settings.json | 291 ++ AUTHORS | 11 + CHANGELOG.md | 168 ++ CMakeLists.txt | 363 +++ CMakePresets.json | 473 +++ LICENSE | 29 + README.md | 69 + cmake/AsapTargets.cmake | 158 + cmake/BuildHelpers.cmake | 49 + cmake/ClangFormat.cmake | 27 + cmake/ClangTidy.cmake | 32 + cmake/CodeCoverage.cmake | 35 + cmake/CompileDefinitions.cmake | 55 + cmake/CompileOptions.cmake | 234 ++ cmake/ComponentInstall.cmake | 11 + cmake/DoxGeneration.cmake | 158 + cmake/FindSphinx.cmake | 22 + cmake/FindValgrind.cmake | 7 + cmake/GenerateTemplateExportHeader.cmake | 25 + cmake/GetGitRevisionDescription.cmake | 128 + cmake/GetGitRevisionDescription.cmake.in | 46 + cmake/GoogleSanitizers.cmake | 194 ++ cmake/LanguageStandards.cmake | 11 + cmake/ListTargets.cmake | 19 + cmake/SphinxGeneration.cmake | 82 + cmake/TestTargets.cmake | 25 + cmake/Valgrind.cmake | 22 + cmake/common | 1 + cmake/scripts/standard-version-updater.js | 96 + common/.gitignore | 2 + common/CMakeLists.txt | 146 + common/README.md | 30 + common/config.cmake.in | 7 + common/config.pc.in | 11 + common/doc/_static/favicon.ico | Bin 0 -> 178091 bytes common/doc/_static/logo.png | Bin 0 -> 34593 bytes common/doc/api.rst | 22 + common/doc/api/compilers.rst | 115 + common/doc/api/contract.rst | 110 + common/doc/api/flags.rst | 36 + common/doc/api/mixin.rst | 53 + common/doc/api/overload.rst | 26 + common/doc/api/platform.rst | 97 + common/doc/conf.py.in | 131 + common/doc/index.rst | 25 + common/doc/license.rst | 14 + common/doc/version.rst | 14 + common/include/common/compilers.h | 647 ++++ common/include/common/config.h.in | 20 + common/include/common/flag_ops.h | 78 + common/include/common/mixin.h | 254 ++ common/include/common/overload.h | 34 + common/include/common/platform.h | 84 + common/test/CMakeLists.txt | 39 + common/test/flag_ops_test.cpp | 148 + common/test/main.cpp | 13 + common/test/mixin/CMakeLists.txt | 36 + common/test/mixin/mixin_chained_init_test.cpp | 74 + common/test/mixin/mixin_curry_test.cpp | 61 + common/test/mixin/mixin_interfaces_test.cpp | 85 + common/test/mixin/mixin_top_test.cpp | 81 + common/test/overload_test.cpp | 66 + contract/.gitignore | 2 + contract/CMakeLists.txt | 149 + contract/README.md | 30 + contract/config.cmake.in | 7 + contract/config.pc.in | 11 + contract/doc/_static/favicon.ico | Bin 0 -> 178091 bytes contract/doc/_static/logo.png | Bin 0 -> 34593 bytes contract/doc/api.rst | 17 + contract/doc/api/contract.rst | 151 + contract/doc/conf.py.in | 131 + contract/doc/index.rst | 25 + contract/doc/license.rst | 14 + contract/doc/version.rst | 14 + contract/include/contract/contract.h | 334 +++ contract/include/contract/ut/framework.h | 128 + contract/include/contract/ut/gtest.h | 95 + contract/src/contract.cpp | 79 + contract/src/contract_ut.cpp | 82 + contract/test/CMakeLists.txt | 126 + contract/test/contract_handlers_test.cpp | 91 + contract/test/contracts_audit_test.cpp | 98 + contract/test/contracts_default_test.cpp | 98 + contract/test/contracts_honored_test.cpp | 79 + contract/test/contracts_off_test.cpp | 98 + contract/test/main.cpp | 13 + contract/test/test_helper.cpp | 43 + contract/test/test_helper.h | 26 + contract/test/ut/CMakeLists.txt | 51 + contract/test/ut/contract_ut_gtest_test.cpp | 117 + data/REPLACE_WITH_REAL_DATA | 0 doc/01-getting-started/customizing.rst | 122 + doc/01-getting-started/devenv.rst | 554 ++++ doc/01-getting-started/get-the-code.rst | 141 + doc/01-getting-started/get-updates.rst | 60 + doc/01-getting-started/index.rst | 95 + doc/01-getting-started/structure.rst | 312 ++ doc/01-getting-started/useful-commands.rst | 87 + doc/02-project-development/build.rst | 202 ++ doc/02-project-development/devflow.rst | 199 ++ doc/02-project-development/index.rst | 39 + doc/02-project-development/modules.rst | 253 ++ doc/02-project-development/third_party.rst | 272 ++ doc/03-documentation/doxygen-doc.rst | 63 + doc/03-documentation/index.rst | 45 + doc/03-documentation/sphinx-doc.rst | 70 + doc/04-library-modules/index.rst | 14 + doc/_static/favicon.ico | Bin 0 -> 178091 bytes doc/_static/logo.png | Bin 0 -> 34593 bytes doc/_static/windows_terminal_posh.png | Bin 0 -> 75225 bytes doc/changelog.md | 2 + doc/conf.py.in | 134 + doc/index.html | 44 + doc/index.rst | 75 + doc/license.rst | 14 + doc/version.rst | 13 + doxygen/Doxyfile.in | 2658 +++++++++++++++++ doxygen/doxygen-awesome-css | 1 + doxygen/footer.html | 32 + doxygen/header.html | 130 + requirements.txt | 14 + templates/template_api.h.in | 29 + templates/template_msvc_api.h.in | 29 + templates/version.h.in | 22 + third_party/CMakeLists.txt | 12 + third_party/gsl | 1 + 146 files changed, 15868 insertions(+) create mode 100644 .clang-format create mode 100644 .clang-tidy create mode 100644 .clangd.in create mode 100644 .cmake-format.yaml create mode 100644 .commitlintrc.json create mode 100644 .devcontainer/.p10k.zsh create mode 100644 .devcontainer/.zshrc create mode 100644 .devcontainer/Dockerfile create mode 100644 .devcontainer/devcontainer.json create mode 100644 .editorconfig create mode 100644 .gitattributes create mode 100644 .github/workflows/gh-pages.yml create mode 100644 .github/workflows/linux-build.yml create mode 100644 .github/workflows/windows-build.yml create mode 100644 .gitignore create mode 100644 .gitmodules create mode 100755 .husky/commit-msg create mode 100644 .versionrc.json create mode 100644 .vscode/extensions.json create mode 100644 .vscode/settings.json create mode 100644 AUTHORS create mode 100644 CHANGELOG.md create mode 100644 CMakeLists.txt create mode 100644 CMakePresets.json create mode 100644 LICENSE create mode 100644 README.md create mode 100644 cmake/AsapTargets.cmake create mode 100644 cmake/BuildHelpers.cmake create mode 100644 cmake/ClangFormat.cmake create mode 100644 cmake/ClangTidy.cmake create mode 100644 cmake/CodeCoverage.cmake create mode 100644 cmake/CompileDefinitions.cmake create mode 100644 cmake/CompileOptions.cmake create mode 100644 cmake/ComponentInstall.cmake create mode 100644 cmake/DoxGeneration.cmake create mode 100644 cmake/FindSphinx.cmake create mode 100644 cmake/FindValgrind.cmake create mode 100644 cmake/GenerateTemplateExportHeader.cmake create mode 100644 cmake/GetGitRevisionDescription.cmake create mode 100644 cmake/GetGitRevisionDescription.cmake.in create mode 100644 cmake/GoogleSanitizers.cmake create mode 100644 cmake/LanguageStandards.cmake create mode 100644 cmake/ListTargets.cmake create mode 100644 cmake/SphinxGeneration.cmake create mode 100644 cmake/TestTargets.cmake create mode 100644 cmake/Valgrind.cmake create mode 160000 cmake/common create mode 100644 cmake/scripts/standard-version-updater.js create mode 100644 common/.gitignore create mode 100644 common/CMakeLists.txt create mode 100644 common/README.md create mode 100644 common/config.cmake.in create mode 100644 common/config.pc.in create mode 100644 common/doc/_static/favicon.ico create mode 100644 common/doc/_static/logo.png create mode 100644 common/doc/api.rst create mode 100644 common/doc/api/compilers.rst create mode 100644 common/doc/api/contract.rst create mode 100644 common/doc/api/flags.rst create mode 100644 common/doc/api/mixin.rst create mode 100644 common/doc/api/overload.rst create mode 100644 common/doc/api/platform.rst create mode 100644 common/doc/conf.py.in create mode 100644 common/doc/index.rst create mode 100644 common/doc/license.rst create mode 100644 common/doc/version.rst create mode 100644 common/include/common/compilers.h create mode 100644 common/include/common/config.h.in create mode 100644 common/include/common/flag_ops.h create mode 100644 common/include/common/mixin.h create mode 100644 common/include/common/overload.h create mode 100644 common/include/common/platform.h create mode 100644 common/test/CMakeLists.txt create mode 100644 common/test/flag_ops_test.cpp create mode 100644 common/test/main.cpp create mode 100644 common/test/mixin/CMakeLists.txt create mode 100644 common/test/mixin/mixin_chained_init_test.cpp create mode 100644 common/test/mixin/mixin_curry_test.cpp create mode 100644 common/test/mixin/mixin_interfaces_test.cpp create mode 100644 common/test/mixin/mixin_top_test.cpp create mode 100644 common/test/overload_test.cpp create mode 100644 contract/.gitignore create mode 100644 contract/CMakeLists.txt create mode 100644 contract/README.md create mode 100644 contract/config.cmake.in create mode 100644 contract/config.pc.in create mode 100644 contract/doc/_static/favicon.ico create mode 100644 contract/doc/_static/logo.png create mode 100644 contract/doc/api.rst create mode 100644 contract/doc/api/contract.rst create mode 100644 contract/doc/conf.py.in create mode 100644 contract/doc/index.rst create mode 100644 contract/doc/license.rst create mode 100644 contract/doc/version.rst create mode 100644 contract/include/contract/contract.h create mode 100644 contract/include/contract/ut/framework.h create mode 100644 contract/include/contract/ut/gtest.h create mode 100644 contract/src/contract.cpp create mode 100644 contract/src/contract_ut.cpp create mode 100644 contract/test/CMakeLists.txt create mode 100644 contract/test/contract_handlers_test.cpp create mode 100644 contract/test/contracts_audit_test.cpp create mode 100644 contract/test/contracts_default_test.cpp create mode 100644 contract/test/contracts_honored_test.cpp create mode 100644 contract/test/contracts_off_test.cpp create mode 100644 contract/test/main.cpp create mode 100644 contract/test/test_helper.cpp create mode 100644 contract/test/test_helper.h create mode 100644 contract/test/ut/CMakeLists.txt create mode 100644 contract/test/ut/contract_ut_gtest_test.cpp create mode 100644 data/REPLACE_WITH_REAL_DATA create mode 100644 doc/01-getting-started/customizing.rst create mode 100644 doc/01-getting-started/devenv.rst create mode 100644 doc/01-getting-started/get-the-code.rst create mode 100644 doc/01-getting-started/get-updates.rst create mode 100644 doc/01-getting-started/index.rst create mode 100644 doc/01-getting-started/structure.rst create mode 100644 doc/01-getting-started/useful-commands.rst create mode 100644 doc/02-project-development/build.rst create mode 100644 doc/02-project-development/devflow.rst create mode 100644 doc/02-project-development/index.rst create mode 100644 doc/02-project-development/modules.rst create mode 100644 doc/02-project-development/third_party.rst create mode 100644 doc/03-documentation/doxygen-doc.rst create mode 100644 doc/03-documentation/index.rst create mode 100644 doc/03-documentation/sphinx-doc.rst create mode 100644 doc/04-library-modules/index.rst create mode 100644 doc/_static/favicon.ico create mode 100644 doc/_static/logo.png create mode 100644 doc/_static/windows_terminal_posh.png create mode 100644 doc/changelog.md create mode 100644 doc/conf.py.in create mode 100644 doc/index.html create mode 100644 doc/index.rst create mode 100644 doc/license.rst create mode 100644 doc/version.rst create mode 100644 doxygen/Doxyfile.in create mode 160000 doxygen/doxygen-awesome-css create mode 100644 doxygen/footer.html create mode 100644 doxygen/header.html create mode 100644 requirements.txt create mode 100644 templates/template_api.h.in create mode 100644 templates/template_msvc_api.h.in create mode 100644 templates/version.h.in create mode 100644 third_party/CMakeLists.txt create mode 160000 third_party/gsl diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..2034d82 --- /dev/null +++ b/.clang-format @@ -0,0 +1,137 @@ +--- +Language: Cpp +# BasedOnStyle: LLVM +AccessModifierOffset: -2 +AlignAfterOpenBracket: DontAlign +AlignConsecutiveMacros: false +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlines: Right +AlignOperands: true +AlignTrailingComments: true +AllowAllArgumentsOnNextLine: true +AllowAllConstructorInitializersOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: Never +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: false +AllowShortLambdasOnASingleLine: All +AllowShortIfStatementsOnASingleLine: Never +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: MultiLine +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: false + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Attach +BreakBeforeInheritanceComma: false +BreakInheritanceList: BeforeColon +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: BeforeColon +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: true +ColumnLimit: 80 +CommentPragmas: "^ IWYU pragma:" +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DeriveLineEnding: true +DerivePointerAlignment: false +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '^"(llvm|llvm-c|clang|clang-c)/' + Priority: 2 + SortPriority: 0 + - Regex: '^(<|"(gtest|gmock|isl|json)/)' + Priority: 3 + SortPriority: 0 + - Regex: ".*" + Priority: 1 + SortPriority: 0 +IncludeIsMainRegex: "(Test)?$" +IncludeIsMainSourceRegex: "" +IndentCaseLabels: false +IndentGotoLabels: true +IndentPPDirectives: None +IndentWidth: 2 +IndentWrappedFunctionNames: false +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: true +MacroBlockBegin: "" +MacroBlockEnd: "" +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBinPackProtocolList: Auto +ObjCBlockIndentWidth: 2 +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 60 +PointerAlignment: Right +ReflowComments: true +SortIncludes: true +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyBlock: false +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInConditionalStatement: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +SpaceBeforeSquareBrackets: false +Standard: Latest +StatementMacros: + - Q_UNUSED + - QT_REQUIRE_VERSION +TabWidth: 8 +UseCRLF: false +UseTab: Never +--- + diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 0000000..6c5b018 --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,18 @@ +UseColor: true + +Checks: > + bugprone-*, + clang-analyzer-*, + cppcoreguidelines-*, + google-*, + hicpp-*, + modernize-*, + performance-*, + portability-*, + readability-*, + -cppcoreguidelines-macro-usage, + -hicpp-signed-bitwise, + -hicpp-exception-baseclass, + -readability-function-cognitive-complexity, + -google-runtime-references +HeaderFilterRegex: ".*/[common|logging]/.*" diff --git a/.clangd.in b/.clangd.in new file mode 100644 index 0000000..e9c4963 --- /dev/null +++ b/.clangd.in @@ -0,0 +1,8 @@ +# We need to configure the location of the compilation database in this file +# and not in vscode `.settings` until we have a way to get the cmake build +# directory or preset name as a subsititution variable. +# +# See https://github.com/clangd/vscode-clangd/issues/48 + +CompileFlags: + CompilationDatabase: "@CMAKE_BINARY_DIR@" diff --git a/.cmake-format.yaml b/.cmake-format.yaml new file mode 100644 index 0000000..e67a973 --- /dev/null +++ b/.cmake-format.yaml @@ -0,0 +1,63 @@ +parse: {} +format: + line_width: 80 + tab_size: 2 + use_tabchars: false + fractional_tab_policy: use-space + max_subgroups_hwrap: 2 + max_pargs_hwrap: 6 + max_rows_cmdline: 2 + separate_ctrl_name_with_space: false + separate_fn_name_with_space: false + dangle_parens: false + dangle_align: prefix + min_prefix_chars: 4 + max_prefix_chars: 10 + max_lines_hwrap: 2 + line_ending: unix + command_case: canonical + keyword_case: unchanged + always_wrap: [] + enable_sort: true + autosort: false + require_valid_layout: false + layout_passes: {} +markup: + bullet_char: "*" + enum_char: . + first_comment_is_literal: false + literal_comment_pattern: null + fence_pattern: ^\s*([`~]{3}[`~]*)(.*)$ + ruler_pattern: ^\s*[^\w\s]{3}.*[^\w\s]{3}$ + explicit_trailing_pattern: "#<" + hashruler_min_length: 10 + canonicalize_hashrulers: true + enable_markup: true +lint: + disabled_codes: + - C0103 # Invalid argument name + - C0113 # Missing COMMENT in statement which allows it + - C0111 # Missing docstring on function or macro declaration + function_pattern: "[0-9a-z_]+" + macro_pattern: "[0-9A-Z_]+" + global_var_pattern: "[A-Z][0-9A-Z_]+" + internal_var_pattern: _[A-Z][0-9A-Z_]+ + local_var_pattern: "[a-z][a-z0-9_]+" + private_var_pattern: _[0-9a-z_]+ + public_var_pattern: "[A-Z][0-9A-Z_]+" + argument_var_pattern: "[a-z][a-z0-9_]+" + keyword_pattern: "[A-Z][0-9A-Z_]+" + max_conditionals_custom_parser: 2 + min_statement_spacing: 1 + max_statement_spacing: 2 + max_returns: 6 + max_branches: 30 # Default target: 12 + max_arguments: 6 # Default target: 5 + max_localvars: 15 + max_statements: 110 # Default target: 50 +encode: + emit_byteorder_mark: false + input_encoding: utf-8 + output_encoding: utf-8 +misc: + per_command: {} diff --git a/.commitlintrc.json b/.commitlintrc.json new file mode 100644 index 0000000..b34b410 --- /dev/null +++ b/.commitlintrc.json @@ -0,0 +1,5 @@ +{ + "extends": [ + "@commitlint/config-conventional" + ] +} \ No newline at end of file diff --git a/.devcontainer/.p10k.zsh b/.devcontainer/.p10k.zsh new file mode 100644 index 0000000..8da04bf --- /dev/null +++ b/.devcontainer/.p10k.zsh @@ -0,0 +1,1641 @@ +# Generated by Powerlevel10k configuration wizard on 2021-12-04 at 18:31 UTC. +# Based on romkatv/powerlevel10k/config/p10k-classic.zsh, checksum 26821. +# Wizard options: compatible, classic, unicode, light, 24h time, flat heads, flat tails, +# 2 lines, disconnected, left frame, compact, concise, transient_prompt, +# instant_prompt=verbose. +# Type `p10k configure` to generate another config. +# +# Config for Powerlevel10k with classic powerline prompt style. Type `p10k configure` to generate +# your own config based on it. +# +# Tip: Looking for a nice color? Here's a one-liner to print colormap. +# +# for i in {0..255}; do print -Pn "%K{$i} %k%F{$i}${(l:3::0:)i}%f " ${${(M)$((i%6)):#3}:+$'\n'}; done + +# Temporarily change options. +'builtin' 'local' '-a' 'p10k_config_opts' +[[ ! -o 'aliases' ]] || p10k_config_opts+=('aliases') +[[ ! -o 'sh_glob' ]] || p10k_config_opts+=('sh_glob') +[[ ! -o 'no_brace_expand' ]] || p10k_config_opts+=('no_brace_expand') +'builtin' 'setopt' 'no_aliases' 'no_sh_glob' 'brace_expand' + +() { + emulate -L zsh -o extended_glob + + # Unset all configuration options. This allows you to apply configuration changes without + # restarting zsh. Edit ~/.p10k.zsh and type `source ~/.p10k.zsh`. + unset -m '(POWERLEVEL9K_*|DEFAULT_USER)~POWERLEVEL9K_GITSTATUS_DIR' + + # Zsh >= 5.1 is required. + autoload -Uz is-at-least && is-at-least 5.1 || return + + # The list of segments shown on the left. Fill it with the most important segments. + typeset -g POWERLEVEL9K_LEFT_PROMPT_ELEMENTS=( + # =========================[ Line #1 ]========================= + # os_icon # os identifier + dir # current directory + vcs # git status + # =========================[ Line #2 ]========================= + newline # \n + # prompt_char # prompt symbol + ) + + # The list of segments shown on the right. Fill it with less important segments. + # Right prompt on the last prompt line (where you are typing your commands) gets + # automatically hidden when the input line reaches it. Right prompt above the + # last prompt line gets hidden if it would overlap with left prompt. + typeset -g POWERLEVEL9K_RIGHT_PROMPT_ELEMENTS=( + # =========================[ Line #1 ]========================= + status # exit code of the last command + command_execution_time # duration of the last command + background_jobs # presence of background jobs + direnv # direnv status (https://direnv.net/) + asdf # asdf version manager (https://github.com/asdf-vm/asdf) + virtualenv # python virtual environment (https://docs.python.org/3/library/venv.html) + anaconda # conda environment (https://conda.io/) + pyenv # python environment (https://github.com/pyenv/pyenv) + goenv # go environment (https://github.com/syndbg/goenv) + nodenv # node.js version from nodenv (https://github.com/nodenv/nodenv) + nvm # node.js version from nvm (https://github.com/nvm-sh/nvm) + nodeenv # node.js environment (https://github.com/ekalinin/nodeenv) + # node_version # node.js version + # go_version # go version (https://golang.org) + # rust_version # rustc version (https://www.rust-lang.org) + # dotnet_version # .NET version (https://dotnet.microsoft.com) + # php_version # php version (https://www.php.net/) + # laravel_version # laravel php framework version (https://laravel.com/) + # java_version # java version (https://www.java.com/) + # package # name@version from package.json (https://docs.npmjs.com/files/package.json) + rbenv # ruby version from rbenv (https://github.com/rbenv/rbenv) + rvm # ruby version from rvm (https://rvm.io) + fvm # flutter version management (https://github.com/leoafarias/fvm) + luaenv # lua version from luaenv (https://github.com/cehoffman/luaenv) + jenv # java version from jenv (https://github.com/jenv/jenv) + plenv # perl version from plenv (https://github.com/tokuhirom/plenv) + phpenv # php version from phpenv (https://github.com/phpenv/phpenv) + scalaenv # scala version from scalaenv (https://github.com/scalaenv/scalaenv) + haskell_stack # haskell version from stack (https://haskellstack.org/) + kubecontext # current kubernetes context (https://kubernetes.io/) + terraform # terraform workspace (https://www.terraform.io) + # terraform_version # terraform version (https://www.terraform.io) + aws # aws profile (https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-profiles.html) + aws_eb_env # aws elastic beanstalk environment (https://aws.amazon.com/elasticbeanstalk/) + azure # azure account name (https://docs.microsoft.com/en-us/cli/azure) + gcloud # google cloud cli account and project (https://cloud.google.com/) + google_app_cred # google application credentials (https://cloud.google.com/docs/authentication/production) + toolbox # toolbox name (https://github.com/containers/toolbox) + context # user@hostname + nordvpn # nordvpn connection status, linux only (https://nordvpn.com/) + ranger # ranger shell (https://github.com/ranger/ranger) + nnn # nnn shell (https://github.com/jarun/nnn) + xplr # xplr shell (https://github.com/sayanarijit/xplr) + vim_shell # vim shell indicator (:sh) + midnight_commander # midnight commander shell (https://midnight-commander.org/) + nix_shell # nix shell (https://nixos.org/nixos/nix-pills/developing-with-nix-shell.html) + vi_mode # vi mode (you don't need this if you've enabled prompt_char) + # vpn_ip # virtual private network indicator + # load # CPU load + # disk_usage # disk usage + # ram # free RAM + # swap # used swap + todo # todo items (https://github.com/todotxt/todo.txt-cli) + timewarrior # timewarrior tracking status (https://timewarrior.net/) + taskwarrior # taskwarrior task count (https://taskwarrior.org/) + time # current time + # =========================[ Line #2 ]========================= + newline # \n + # ip # ip address and bandwidth usage for a specified network interface + # public_ip # public IP address + # proxy # system-wide http/https/ftp proxy + # battery # internal battery + # wifi # wifi speed + # example # example user-defined segment (see prompt_example function below) + ) + + # Defines character set used by powerlevel10k. It's best to let `p10k configure` set it for you. + typeset -g POWERLEVEL9K_MODE=compatible + # When set to `moderate`, some icons will have an extra space after them. This is meant to avoid + # icon overlap when using non-monospace fonts. When set to `none`, spaces are not added. + typeset -g POWERLEVEL9K_ICON_PADDING=none + + # When set to true, icons appear before content on both sides of the prompt. When set + # to false, icons go after content. If empty or not set, icons go before content in the left + # prompt and after content in the right prompt. + # + # You can also override it for a specific segment: + # + # POWERLEVEL9K_STATUS_ICON_BEFORE_CONTENT=false + # + # Or for a specific segment in specific state: + # + # POWERLEVEL9K_DIR_NOT_WRITABLE_ICON_BEFORE_CONTENT=false + typeset -g POWERLEVEL9K_ICON_BEFORE_CONTENT= + + # Add an empty line before each prompt. + typeset -g POWERLEVEL9K_PROMPT_ADD_NEWLINE=false + + # Connect left prompt lines with these symbols. You'll probably want to use the same color + # as POWERLEVEL9K_MULTILINE_FIRST_PROMPT_GAP_FOREGROUND below. + typeset -g POWERLEVEL9K_MULTILINE_FIRST_PROMPT_PREFIX='%242F╭─' + typeset -g POWERLEVEL9K_MULTILINE_NEWLINE_PROMPT_PREFIX='%242F├─' + typeset -g POWERLEVEL9K_MULTILINE_LAST_PROMPT_PREFIX='%242F╰─' + # Connect right prompt lines with these symbols. + typeset -g POWERLEVEL9K_MULTILINE_FIRST_PROMPT_SUFFIX= + typeset -g POWERLEVEL9K_MULTILINE_NEWLINE_PROMPT_SUFFIX= + typeset -g POWERLEVEL9K_MULTILINE_LAST_PROMPT_SUFFIX= + + # Filler between left and right prompt on the first prompt line. You can set it to ' ', '·' or + # '─'. The last two make it easier to see the alignment between left and right prompt and to + # separate prompt from command output. You might want to set POWERLEVEL9K_PROMPT_ADD_NEWLINE=false + # for more compact prompt if using this option. + typeset -g POWERLEVEL9K_MULTILINE_FIRST_PROMPT_GAP_CHAR=' ' + typeset -g POWERLEVEL9K_MULTILINE_FIRST_PROMPT_GAP_BACKGROUND= + typeset -g POWERLEVEL9K_MULTILINE_NEWLINE_PROMPT_GAP_BACKGROUND= + if [[ $POWERLEVEL9K_MULTILINE_FIRST_PROMPT_GAP_CHAR != ' ' ]]; then + # The color of the filler. You'll probably want to match the color of POWERLEVEL9K_MULTILINE + # ornaments defined above. + typeset -g POWERLEVEL9K_MULTILINE_FIRST_PROMPT_GAP_FOREGROUND=242 + # Start filler from the edge of the screen if there are no left segments on the first line. + typeset -g POWERLEVEL9K_EMPTY_LINE_LEFT_PROMPT_FIRST_SEGMENT_END_SYMBOL='%{%}' + # End filler on the edge of the screen if there are no right segments on the first line. + typeset -g POWERLEVEL9K_EMPTY_LINE_RIGHT_PROMPT_FIRST_SEGMENT_START_SYMBOL='%{%}' + fi + + # Default background color. + typeset -g POWERLEVEL9K_BACKGROUND=238 + + # Separator between same-color segments on the left. + typeset -g POWERLEVEL9K_LEFT_SUBSEGMENT_SEPARATOR='%246F\u2502' + # Separator between same-color segments on the right. + typeset -g POWERLEVEL9K_RIGHT_SUBSEGMENT_SEPARATOR='%246F\u2502' + # Separator between different-color segments on the left. + typeset -g POWERLEVEL9K_LEFT_SEGMENT_SEPARATOR='' + # Separator between different-color segments on the right. + typeset -g POWERLEVEL9K_RIGHT_SEGMENT_SEPARATOR='' + # The right end of left prompt. + typeset -g POWERLEVEL9K_LEFT_PROMPT_LAST_SEGMENT_END_SYMBOL='' + # The left end of right prompt. + typeset -g POWERLEVEL9K_RIGHT_PROMPT_FIRST_SEGMENT_START_SYMBOL='' + # The left end of left prompt. + typeset -g POWERLEVEL9K_LEFT_PROMPT_FIRST_SEGMENT_START_SYMBOL='' + # The right end of right prompt. + typeset -g POWERLEVEL9K_RIGHT_PROMPT_LAST_SEGMENT_END_SYMBOL='' + # Left prompt terminator for lines without any segments. + typeset -g POWERLEVEL9K_EMPTY_LINE_LEFT_PROMPT_LAST_SEGMENT_END_SYMBOL= + + #################################[ os_icon: os identifier ]################################## + # OS identifier color. + typeset -g POWERLEVEL9K_OS_ICON_FOREGROUND=255 + # Custom icon. + # typeset -g POWERLEVEL9K_OS_ICON_CONTENT_EXPANSION='⭐' + + ################################[ prompt_char: prompt symbol ]################################ + # Transparent background. + typeset -g POWERLEVEL9K_PROMPT_CHAR_BACKGROUND= + # Green prompt symbol if the last command succeeded. + typeset -g POWERLEVEL9K_PROMPT_CHAR_OK_{VIINS,VICMD,VIVIS,VIOWR}_FOREGROUND=76 + # Red prompt symbol if the last command failed. + typeset -g POWERLEVEL9K_PROMPT_CHAR_ERROR_{VIINS,VICMD,VIVIS,VIOWR}_FOREGROUND=196 + # Default prompt symbol. + typeset -g POWERLEVEL9K_PROMPT_CHAR_{OK,ERROR}_VIINS_CONTENT_EXPANSION='❯' + # Prompt symbol in command vi mode. + typeset -g POWERLEVEL9K_PROMPT_CHAR_{OK,ERROR}_VICMD_CONTENT_EXPANSION='❮' + # Prompt symbol in visual vi mode. + typeset -g POWERLEVEL9K_PROMPT_CHAR_{OK,ERROR}_VIVIS_CONTENT_EXPANSION='V' + # Prompt symbol in overwrite vi mode. + typeset -g POWERLEVEL9K_PROMPT_CHAR_{OK,ERROR}_VIOWR_CONTENT_EXPANSION='▶' + typeset -g POWERLEVEL9K_PROMPT_CHAR_OVERWRITE_STATE=true + # No line terminator if prompt_char is the last segment. + typeset -g POWERLEVEL9K_PROMPT_CHAR_LEFT_PROMPT_LAST_SEGMENT_END_SYMBOL= + # No line introducer if prompt_char is the first segment. + typeset -g POWERLEVEL9K_PROMPT_CHAR_LEFT_PROMPT_FIRST_SEGMENT_START_SYMBOL= + # No surrounding whitespace. + typeset -g POWERLEVEL9K_PROMPT_CHAR_LEFT_{LEFT,RIGHT}_WHITESPACE= + + ##################################[ dir: current directory ]################################## + # Default current directory color. + typeset -g POWERLEVEL9K_DIR_FOREGROUND=31 + # If directory is too long, shorten some of its segments to the shortest possible unique + # prefix. The shortened directory can be tab-completed to the original. + typeset -g POWERLEVEL9K_SHORTEN_STRATEGY=truncate_to_unique + # Replace removed segment suffixes with this symbol. + typeset -g POWERLEVEL9K_SHORTEN_DELIMITER= + # Color of the shortened directory segments. + typeset -g POWERLEVEL9K_DIR_SHORTENED_FOREGROUND=103 + # Color of the anchor directory segments. Anchor segments are never shortened. The first + # segment is always an anchor. + typeset -g POWERLEVEL9K_DIR_ANCHOR_FOREGROUND=39 + # Display anchor directory segments in bold. + typeset -g POWERLEVEL9K_DIR_ANCHOR_BOLD=true + # Don't shorten directories that contain any of these files. They are anchors. + local anchor_files=( + .bzr + .citc + .git + .hg + .node-version + .python-version + .go-version + .ruby-version + .lua-version + .java-version + .perl-version + .php-version + .tool-version + .shorten_folder_marker + .svn + .terraform + CVS + Cargo.toml + composer.json + go.mod + package.json + stack.yaml + ) + typeset -g POWERLEVEL9K_SHORTEN_FOLDER_MARKER="(${(j:|:)anchor_files})" + # If set to "first" ("last"), remove everything before the first (last) subdirectory that contains + # files matching $POWERLEVEL9K_SHORTEN_FOLDER_MARKER. For example, when the current directory is + # /foo/bar/git_repo/nested_git_repo/baz, prompt will display git_repo/nested_git_repo/baz (first) + # or nested_git_repo/baz (last). This assumes that git_repo and nested_git_repo contain markers + # and other directories don't. + # + # Optionally, "first" and "last" can be followed by ":" where is an integer. + # This moves the truncation point to the right (positive offset) or to the left (negative offset) + # relative to the marker. Plain "first" and "last" are equivalent to "first:0" and "last:0" + # respectively. + typeset -g POWERLEVEL9K_DIR_TRUNCATE_BEFORE_MARKER=false + # Don't shorten this many last directory segments. They are anchors. + typeset -g POWERLEVEL9K_SHORTEN_DIR_LENGTH=1 + # Shorten directory if it's longer than this even if there is space for it. The value can + # be either absolute (e.g., '80') or a percentage of terminal width (e.g, '50%'). If empty, + # directory will be shortened only when prompt doesn't fit or when other parameters demand it + # (see POWERLEVEL9K_DIR_MIN_COMMAND_COLUMNS and POWERLEVEL9K_DIR_MIN_COMMAND_COLUMNS_PCT below). + # If set to `0`, directory will always be shortened to its minimum length. + typeset -g POWERLEVEL9K_DIR_MAX_LENGTH=80 + # When `dir` segment is on the last prompt line, try to shorten it enough to leave at least this + # many columns for typing commands. + typeset -g POWERLEVEL9K_DIR_MIN_COMMAND_COLUMNS=40 + # When `dir` segment is on the last prompt line, try to shorten it enough to leave at least + # COLUMNS * POWERLEVEL9K_DIR_MIN_COMMAND_COLUMNS_PCT * 0.01 columns for typing commands. + typeset -g POWERLEVEL9K_DIR_MIN_COMMAND_COLUMNS_PCT=50 + # If set to true, embed a hyperlink into the directory. Useful for quickly + # opening a directory in the file manager simply by clicking the link. + # Can also be handy when the directory is shortened, as it allows you to see + # the full directory that was used in previous commands. + typeset -g POWERLEVEL9K_DIR_HYPERLINK=false + + # Enable special styling for non-writable and non-existent directories. See POWERLEVEL9K_LOCK_ICON + # and POWERLEVEL9K_DIR_CLASSES below. + typeset -g POWERLEVEL9K_DIR_SHOW_WRITABLE=v3 + + # The default icon shown next to non-writable and non-existent directories when + # POWERLEVEL9K_DIR_SHOW_WRITABLE is set to v3. + typeset -g POWERLEVEL9K_LOCK_ICON='∅' + + # POWERLEVEL9K_DIR_CLASSES allows you to specify custom icons and colors for different + # directories. It must be an array with 3 * N elements. Each triplet consists of: + # + # 1. A pattern against which the current directory ($PWD) is matched. Matching is done with + # extended_glob option enabled. + # 2. Directory class for the purpose of styling. + # 3. An empty string. + # + # Triplets are tried in order. The first triplet whose pattern matches $PWD wins. + # + # If POWERLEVEL9K_DIR_SHOW_WRITABLE is set to v3, non-writable and non-existent directories + # acquire class suffix _NOT_WRITABLE and NON_EXISTENT respectively. + # + # For example, given these settings: + # + # typeset -g POWERLEVEL9K_DIR_CLASSES=( + # '~/work(|/*)' WORK '' + # '~(|/*)' HOME '' + # '*' DEFAULT '') + # + # Whenever the current directory is ~/work or a subdirectory of ~/work, it gets styled with one + # of the following classes depending on its writability and existence: WORK, WORK_NOT_WRITABLE or + # WORK_NON_EXISTENT. + # + # Simply assigning classes to directories doesn't have any visible effects. It merely gives you an + # option to define custom colors and icons for different directory classes. + # + # # Styling for WORK. + # typeset -g POWERLEVEL9K_DIR_WORK_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_DIR_WORK_FOREGROUND=31 + # typeset -g POWERLEVEL9K_DIR_WORK_SHORTENED_FOREGROUND=103 + # typeset -g POWERLEVEL9K_DIR_WORK_ANCHOR_FOREGROUND=39 + # + # # Styling for WORK_NOT_WRITABLE. + # typeset -g POWERLEVEL9K_DIR_WORK_NOT_WRITABLE_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_DIR_WORK_NOT_WRITABLE_FOREGROUND=31 + # typeset -g POWERLEVEL9K_DIR_WORK_NOT_WRITABLE_SHORTENED_FOREGROUND=103 + # typeset -g POWERLEVEL9K_DIR_WORK_NOT_WRITABLE_ANCHOR_FOREGROUND=39 + # + # # Styling for WORK_NON_EXISTENT. + # typeset -g POWERLEVEL9K_DIR_WORK_NON_EXISTENT_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_DIR_WORK_NON_EXISTENT_FOREGROUND=31 + # typeset -g POWERLEVEL9K_DIR_WORK_NON_EXISTENT_SHORTENED_FOREGROUND=103 + # typeset -g POWERLEVEL9K_DIR_WORK_NON_EXISTENT_ANCHOR_FOREGROUND=39 + # + # If a styling parameter isn't explicitly defined for some class, it falls back to the classless + # parameter. For example, if POWERLEVEL9K_DIR_WORK_NOT_WRITABLE_FOREGROUND is not set, it falls + # back to POWERLEVEL9K_DIR_FOREGROUND. + # + typeset -g POWERLEVEL9K_DIR_CLASSES=() + + # Custom prefix. + # typeset -g POWERLEVEL9K_DIR_PREFIX='%248Fin ' + + #####################################[ vcs: git status ]###################################### + # Branch icon. Set this parameter to '\uF126 ' for the popular Powerline branch icon. + typeset -g POWERLEVEL9K_VCS_BRANCH_ICON= + + # Untracked files icon. It's really a question mark, your font isn't broken. + # Change the value of this parameter to show a different icon. + typeset -g POWERLEVEL9K_VCS_UNTRACKED_ICON='?' + + # Formatter for Git status. + # + # Example output: master wip ⇣42⇡42 *42 merge ~42 +42 !42 ?42. + # + # You can edit the function to customize how Git status looks. + # + # VCS_STATUS_* parameters are set by gitstatus plugin. See reference: + # https://github.com/romkatv/gitstatus/blob/master/gitstatus.plugin.zsh. + function my_git_formatter() { + emulate -L zsh + + if [[ -n $P9K_CONTENT ]]; then + # If P9K_CONTENT is not empty, use it. It's either "loading" or from vcs_info (not from + # gitstatus plugin). VCS_STATUS_* parameters are not available in this case. + typeset -g my_git_format=$P9K_CONTENT + return + fi + + if (( $1 )); then + # Styling for up-to-date Git status. + local meta='%248F' # grey foreground + local clean='%76F' # green foreground + local modified='%178F' # yellow foreground + local untracked='%39F' # blue foreground + local conflicted='%196F' # red foreground + else + # Styling for incomplete and stale Git status. + local meta='%244F' # grey foreground + local clean='%244F' # grey foreground + local modified='%244F' # grey foreground + local untracked='%244F' # grey foreground + local conflicted='%244F' # grey foreground + fi + + local res + + if [[ -n $VCS_STATUS_LOCAL_BRANCH ]]; then + local branch=${(V)VCS_STATUS_LOCAL_BRANCH} + # If local branch name is at most 32 characters long, show it in full. + # Otherwise show the first 12 … the last 12. + # Tip: To always show local branch name in full without truncation, delete the next line. + (( $#branch > 32 )) && branch[13,-13]="…" # <-- this line + res+="${clean}${(g::)POWERLEVEL9K_VCS_BRANCH_ICON}${branch//\%/%%}" + fi + + if [[ -n $VCS_STATUS_TAG + # Show tag only if not on a branch. + # Tip: To always show tag, delete the next line. + && -z $VCS_STATUS_LOCAL_BRANCH # <-- this line + ]]; then + local tag=${(V)VCS_STATUS_TAG} + # If tag name is at most 32 characters long, show it in full. + # Otherwise show the first 12 … the last 12. + # Tip: To always show tag name in full without truncation, delete the next line. + (( $#tag > 32 )) && tag[13,-13]="…" # <-- this line + res+="${meta}#${clean}${tag//\%/%%}" + fi + + # Display the current Git commit if there is no branch and no tag. + # Tip: To always display the current Git commit, delete the next line. + [[ -z $VCS_STATUS_LOCAL_BRANCH && -z $VCS_STATUS_TAG ]] && # <-- this line + res+="${meta}@${clean}${VCS_STATUS_COMMIT[1,8]}" + + # Show tracking branch name if it differs from local branch. + if [[ -n ${VCS_STATUS_REMOTE_BRANCH:#$VCS_STATUS_LOCAL_BRANCH} ]]; then + res+="${meta}:${clean}${(V)VCS_STATUS_REMOTE_BRANCH//\%/%%}" + fi + + # Display "wip" if the latest commit's summary contains "wip" or "WIP". + if [[ $VCS_STATUS_COMMIT_SUMMARY == (|*[^[:alnum:]])(wip|WIP)(|[^[:alnum:]]*) ]]; then + res+=" ${modified}wip" + fi + + # ⇣42 if behind the remote. + (( VCS_STATUS_COMMITS_BEHIND )) && res+=" ${clean}⇣${VCS_STATUS_COMMITS_BEHIND}" + # ⇡42 if ahead of the remote; no leading space if also behind the remote: ⇣42⇡42. + (( VCS_STATUS_COMMITS_AHEAD && !VCS_STATUS_COMMITS_BEHIND )) && res+=" " + (( VCS_STATUS_COMMITS_AHEAD )) && res+="${clean}⇡${VCS_STATUS_COMMITS_AHEAD}" + # ⇠42 if behind the push remote. + (( VCS_STATUS_PUSH_COMMITS_BEHIND )) && res+=" ${clean}⇠${VCS_STATUS_PUSH_COMMITS_BEHIND}" + (( VCS_STATUS_PUSH_COMMITS_AHEAD && !VCS_STATUS_PUSH_COMMITS_BEHIND )) && res+=" " + # ⇢42 if ahead of the push remote; no leading space if also behind: ⇠42⇢42. + (( VCS_STATUS_PUSH_COMMITS_AHEAD )) && res+="${clean}⇢${VCS_STATUS_PUSH_COMMITS_AHEAD}" + # *42 if have stashes. + (( VCS_STATUS_STASHES )) && res+=" ${clean}*${VCS_STATUS_STASHES}" + # 'merge' if the repo is in an unusual state. + [[ -n $VCS_STATUS_ACTION ]] && res+=" ${conflicted}${VCS_STATUS_ACTION}" + # ~42 if have merge conflicts. + (( VCS_STATUS_NUM_CONFLICTED )) && res+=" ${conflicted}~${VCS_STATUS_NUM_CONFLICTED}" + # +42 if have staged changes. + (( VCS_STATUS_NUM_STAGED )) && res+=" ${modified}+${VCS_STATUS_NUM_STAGED}" + # !42 if have unstaged changes. + (( VCS_STATUS_NUM_UNSTAGED )) && res+=" ${modified}!${VCS_STATUS_NUM_UNSTAGED}" + # ?42 if have untracked files. It's really a question mark, your font isn't broken. + # See POWERLEVEL9K_VCS_UNTRACKED_ICON above if you want to use a different icon. + # Remove the next line if you don't want to see untracked files at all. + (( VCS_STATUS_NUM_UNTRACKED )) && res+=" ${untracked}${(g::)POWERLEVEL9K_VCS_UNTRACKED_ICON}${VCS_STATUS_NUM_UNTRACKED}" + # "─" if the number of unstaged files is unknown. This can happen due to + # POWERLEVEL9K_VCS_MAX_INDEX_SIZE_DIRTY (see below) being set to a non-negative number lower + # than the number of files in the Git index, or due to bash.showDirtyState being set to false + # in the repository config. The number of staged and untracked files may also be unknown + # in this case. + (( VCS_STATUS_HAS_UNSTAGED == -1 )) && res+=" ${modified}─" + + typeset -g my_git_format=$res + } + functions -M my_git_formatter 2>/dev/null + + # Don't count the number of unstaged, untracked and conflicted files in Git repositories with + # more than this many files in the index. Negative value means infinity. + # + # If you are working in Git repositories with tens of millions of files and seeing performance + # sagging, try setting POWERLEVEL9K_VCS_MAX_INDEX_SIZE_DIRTY to a number lower than the output + # of `git ls-files | wc -l`. Alternatively, add `bash.showDirtyState = false` to the repository's + # config: `git config bash.showDirtyState false`. + typeset -g POWERLEVEL9K_VCS_MAX_INDEX_SIZE_DIRTY=-1 + + # Don't show Git status in prompt for repositories whose workdir matches this pattern. + # For example, if set to '~', the Git repository at $HOME/.git will be ignored. + # Multiple patterns can be combined with '|': '~(|/foo)|/bar/baz/*'. + typeset -g POWERLEVEL9K_VCS_DISABLED_WORKDIR_PATTERN='~' + + # Disable the default Git status formatting. + typeset -g POWERLEVEL9K_VCS_DISABLE_GITSTATUS_FORMATTING=true + # Install our own Git status formatter. + typeset -g POWERLEVEL9K_VCS_CONTENT_EXPANSION='${$((my_git_formatter(1)))+${my_git_format}}' + typeset -g POWERLEVEL9K_VCS_LOADING_CONTENT_EXPANSION='${$((my_git_formatter(0)))+${my_git_format}}' + # Enable counters for staged, unstaged, etc. + typeset -g POWERLEVEL9K_VCS_{STAGED,UNSTAGED,UNTRACKED,CONFLICTED,COMMITS_AHEAD,COMMITS_BEHIND}_MAX_NUM=-1 + + # Icon color. + typeset -g POWERLEVEL9K_VCS_VISUAL_IDENTIFIER_COLOR=76 + typeset -g POWERLEVEL9K_VCS_LOADING_VISUAL_IDENTIFIER_COLOR=244 + # Custom icon. + typeset -g POWERLEVEL9K_VCS_VISUAL_IDENTIFIER_EXPANSION= + # Custom prefix. + # typeset -g POWERLEVEL9K_VCS_PREFIX='%248Fon ' + + # Show status of repositories of these types. You can add svn and/or hg if you are + # using them. If you do, your prompt may become slow even when your current directory + # isn't in an svn or hg reposotiry. + typeset -g POWERLEVEL9K_VCS_BACKENDS=(git) + + # These settings are used for repositories other than Git or when gitstatusd fails and + # Powerlevel10k has to fall back to using vcs_info. + typeset -g POWERLEVEL9K_VCS_CLEAN_FOREGROUND=76 + typeset -g POWERLEVEL9K_VCS_UNTRACKED_FOREGROUND=76 + typeset -g POWERLEVEL9K_VCS_MODIFIED_FOREGROUND=178 + + ##########################[ status: exit code of the last command ]########################### + # Enable OK_PIPE, ERROR_PIPE and ERROR_SIGNAL status states to allow us to enable, disable and + # style them independently from the regular OK and ERROR state. + typeset -g POWERLEVEL9K_STATUS_EXTENDED_STATES=true + + # Status on success. No content, just an icon. No need to show it if prompt_char is enabled as + # it will signify success by turning green. + typeset -g POWERLEVEL9K_STATUS_OK=true + typeset -g POWERLEVEL9K_STATUS_OK_FOREGROUND=70 + typeset -g POWERLEVEL9K_STATUS_OK_VISUAL_IDENTIFIER_EXPANSION='✔' + + # Status when some part of a pipe command fails but the overall exit status is zero. It may look + # like this: 1|0. + typeset -g POWERLEVEL9K_STATUS_OK_PIPE=true + typeset -g POWERLEVEL9K_STATUS_OK_PIPE_FOREGROUND=70 + typeset -g POWERLEVEL9K_STATUS_OK_PIPE_VISUAL_IDENTIFIER_EXPANSION='✔' + + # Status when it's just an error code (e.g., '1'). No need to show it if prompt_char is enabled as + # it will signify error by turning red. + typeset -g POWERLEVEL9K_STATUS_ERROR=true + typeset -g POWERLEVEL9K_STATUS_ERROR_FOREGROUND=160 + typeset -g POWERLEVEL9K_STATUS_ERROR_VISUAL_IDENTIFIER_EXPANSION='х' + + # Status when the last command was terminated by a signal. + typeset -g POWERLEVEL9K_STATUS_ERROR_SIGNAL=true + typeset -g POWERLEVEL9K_STATUS_ERROR_SIGNAL_FOREGROUND=160 + # Use terse signal names: "INT" instead of "SIGINT(2)". + typeset -g POWERLEVEL9K_STATUS_VERBOSE_SIGNAME=false + typeset -g POWERLEVEL9K_STATUS_ERROR_SIGNAL_VISUAL_IDENTIFIER_EXPANSION='х' + + # Status when some part of a pipe command fails and the overall exit status is also non-zero. + # It may look like this: 1|0. + typeset -g POWERLEVEL9K_STATUS_ERROR_PIPE=true + typeset -g POWERLEVEL9K_STATUS_ERROR_PIPE_FOREGROUND=160 + typeset -g POWERLEVEL9K_STATUS_ERROR_PIPE_VISUAL_IDENTIFIER_EXPANSION='х' + + ###################[ command_execution_time: duration of the last command ]################### + # Show duration of the last command if takes at least this many seconds. + typeset -g POWERLEVEL9K_COMMAND_EXECUTION_TIME_THRESHOLD=3 + # Show this many fractional digits. Zero means round to seconds. + typeset -g POWERLEVEL9K_COMMAND_EXECUTION_TIME_PRECISION=0 + # Execution time color. + typeset -g POWERLEVEL9K_COMMAND_EXECUTION_TIME_FOREGROUND=248 + # Duration format: 1d 2h 3m 4s. + typeset -g POWERLEVEL9K_COMMAND_EXECUTION_TIME_FORMAT='d h m s' + # Custom icon. + typeset -g POWERLEVEL9K_COMMAND_EXECUTION_TIME_VISUAL_IDENTIFIER_EXPANSION= + # Custom prefix. + # typeset -g POWERLEVEL9K_COMMAND_EXECUTION_TIME_PREFIX='%248Ftook ' + + #######################[ background_jobs: presence of background jobs ]####################### + # Don't show the number of background jobs. + typeset -g POWERLEVEL9K_BACKGROUND_JOBS_VERBOSE=false + # Background jobs color. + typeset -g POWERLEVEL9K_BACKGROUND_JOBS_FOREGROUND=37 + # Custom icon. + typeset -g POWERLEVEL9K_BACKGROUND_JOBS_VISUAL_IDENTIFIER_EXPANSION='≡' + + #######################[ direnv: direnv status (https://direnv.net/) ]######################## + # Direnv color. + typeset -g POWERLEVEL9K_DIRENV_FOREGROUND=178 + # Custom icon. + # typeset -g POWERLEVEL9K_DIRENV_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ###############[ asdf: asdf version manager (https://github.com/asdf-vm/asdf) ]############### + # Default asdf color. Only used to display tools for which there is no color override (see below). + # Tip: Override this parameter for ${TOOL} with POWERLEVEL9K_ASDF_${TOOL}_FOREGROUND. + typeset -g POWERLEVEL9K_ASDF_FOREGROUND=66 + + # There are four parameters that can be used to hide asdf tools. Each parameter describes + # conditions under which a tool gets hidden. Parameters can hide tools but not unhide them. If at + # least one parameter decides to hide a tool, that tool gets hidden. If no parameter decides to + # hide a tool, it gets shown. + # + # Special note on the difference between POWERLEVEL9K_ASDF_SOURCES and + # POWERLEVEL9K_ASDF_PROMPT_ALWAYS_SHOW. Consider the effect of the following commands: + # + # asdf local python 3.8.1 + # asdf global python 3.8.1 + # + # After running both commands the current python version is 3.8.1 and its source is "local" as + # it takes precedence over "global". If POWERLEVEL9K_ASDF_PROMPT_ALWAYS_SHOW is set to false, + # it'll hide python version in this case because 3.8.1 is the same as the global version. + # POWERLEVEL9K_ASDF_SOURCES will hide python version only if the value of this parameter doesn't + # contain "local". + + # Hide tool versions that don't come from one of these sources. + # + # Available sources: + # + # - shell `asdf current` says "set by ASDF_${TOOL}_VERSION environment variable" + # - local `asdf current` says "set by /some/not/home/directory/file" + # - global `asdf current` says "set by /home/username/file" + # + # Note: If this parameter is set to (shell local global), it won't hide tools. + # Tip: Override this parameter for ${TOOL} with POWERLEVEL9K_ASDF_${TOOL}_SOURCES. + typeset -g POWERLEVEL9K_ASDF_SOURCES=(shell local global) + + # If set to false, hide tool versions that are the same as global. + # + # Note: The name of this parameter doesn't reflect its meaning at all. + # Note: If this parameter is set to true, it won't hide tools. + # Tip: Override this parameter for ${TOOL} with POWERLEVEL9K_ASDF_${TOOL}_PROMPT_ALWAYS_SHOW. + typeset -g POWERLEVEL9K_ASDF_PROMPT_ALWAYS_SHOW=false + + # If set to false, hide tool versions that are equal to "system". + # + # Note: If this parameter is set to true, it won't hide tools. + # Tip: Override this parameter for ${TOOL} with POWERLEVEL9K_ASDF_${TOOL}_SHOW_SYSTEM. + typeset -g POWERLEVEL9K_ASDF_SHOW_SYSTEM=true + + # If set to non-empty value, hide tools unless there is a file matching the specified file pattern + # in the current directory, or its parent directory, or its grandparent directory, and so on. + # + # Note: If this parameter is set to empty value, it won't hide tools. + # Note: SHOW_ON_UPGLOB isn't specific to asdf. It works with all prompt segments. + # Tip: Override this parameter for ${TOOL} with POWERLEVEL9K_ASDF_${TOOL}_SHOW_ON_UPGLOB. + # + # Example: Hide nodejs version when there is no package.json and no *.js files in the current + # directory, in `..`, in `../..` and so on. + # + # typeset -g POWERLEVEL9K_ASDF_NODEJS_SHOW_ON_UPGLOB='*.js|package.json' + typeset -g POWERLEVEL9K_ASDF_SHOW_ON_UPGLOB= + + # Ruby version from asdf. + typeset -g POWERLEVEL9K_ASDF_RUBY_FOREGROUND=168 + # typeset -g POWERLEVEL9K_ASDF_RUBY_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_ASDF_RUBY_SHOW_ON_UPGLOB='*.foo|*.bar' + + # Python version from asdf. + typeset -g POWERLEVEL9K_ASDF_PYTHON_FOREGROUND=37 + # typeset -g POWERLEVEL9K_ASDF_PYTHON_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_ASDF_PYTHON_SHOW_ON_UPGLOB='*.foo|*.bar' + + # Go version from asdf. + typeset -g POWERLEVEL9K_ASDF_GOLANG_FOREGROUND=37 + # typeset -g POWERLEVEL9K_ASDF_GOLANG_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_ASDF_GOLANG_SHOW_ON_UPGLOB='*.foo|*.bar' + + # Node.js version from asdf. + typeset -g POWERLEVEL9K_ASDF_NODEJS_FOREGROUND=70 + # typeset -g POWERLEVEL9K_ASDF_NODEJS_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_ASDF_NODEJS_SHOW_ON_UPGLOB='*.foo|*.bar' + + # Rust version from asdf. + typeset -g POWERLEVEL9K_ASDF_RUST_FOREGROUND=37 + # typeset -g POWERLEVEL9K_ASDF_RUST_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_ASDF_RUST_SHOW_ON_UPGLOB='*.foo|*.bar' + + # .NET Core version from asdf. + typeset -g POWERLEVEL9K_ASDF_DOTNET_CORE_FOREGROUND=134 + # typeset -g POWERLEVEL9K_ASDF_DOTNET_CORE_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_ASDF_DOTNET_CORE_SHOW_ON_UPGLOB='*.foo|*.bar' + + # Flutter version from asdf. + typeset -g POWERLEVEL9K_ASDF_FLUTTER_FOREGROUND=38 + # typeset -g POWERLEVEL9K_ASDF_FLUTTER_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_ASDF_FLUTTER_SHOW_ON_UPGLOB='*.foo|*.bar' + + # Lua version from asdf. + typeset -g POWERLEVEL9K_ASDF_LUA_FOREGROUND=32 + # typeset -g POWERLEVEL9K_ASDF_LUA_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_ASDF_LUA_SHOW_ON_UPGLOB='*.foo|*.bar' + + # Java version from asdf. + typeset -g POWERLEVEL9K_ASDF_JAVA_FOREGROUND=32 + # typeset -g POWERLEVEL9K_ASDF_JAVA_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_ASDF_JAVA_SHOW_ON_UPGLOB='*.foo|*.bar' + + # Perl version from asdf. + typeset -g POWERLEVEL9K_ASDF_PERL_FOREGROUND=67 + # typeset -g POWERLEVEL9K_ASDF_PERL_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_ASDF_PERL_SHOW_ON_UPGLOB='*.foo|*.bar' + + # Erlang version from asdf. + typeset -g POWERLEVEL9K_ASDF_ERLANG_FOREGROUND=125 + # typeset -g POWERLEVEL9K_ASDF_ERLANG_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_ASDF_ERLANG_SHOW_ON_UPGLOB='*.foo|*.bar' + + # Elixir version from asdf. + typeset -g POWERLEVEL9K_ASDF_ELIXIR_FOREGROUND=129 + # typeset -g POWERLEVEL9K_ASDF_ELIXIR_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_ASDF_ELIXIR_SHOW_ON_UPGLOB='*.foo|*.bar' + + # Postgres version from asdf. + typeset -g POWERLEVEL9K_ASDF_POSTGRES_FOREGROUND=31 + # typeset -g POWERLEVEL9K_ASDF_POSTGRES_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_ASDF_POSTGRES_SHOW_ON_UPGLOB='*.foo|*.bar' + + # PHP version from asdf. + typeset -g POWERLEVEL9K_ASDF_PHP_FOREGROUND=99 + # typeset -g POWERLEVEL9K_ASDF_PHP_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_ASDF_PHP_SHOW_ON_UPGLOB='*.foo|*.bar' + + # Haskell version from asdf. + typeset -g POWERLEVEL9K_ASDF_HASKELL_FOREGROUND=172 + # typeset -g POWERLEVEL9K_ASDF_HASKELL_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_ASDF_HASKELL_SHOW_ON_UPGLOB='*.foo|*.bar' + + # Julia version from asdf. + typeset -g POWERLEVEL9K_ASDF_JULIA_FOREGROUND=70 + # typeset -g POWERLEVEL9K_ASDF_JULIA_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_ASDF_JULIA_SHOW_ON_UPGLOB='*.foo|*.bar' + + ##########[ nordvpn: nordvpn connection status, linux only (https://nordvpn.com/) ]########### + # NordVPN connection indicator color. + typeset -g POWERLEVEL9K_NORDVPN_FOREGROUND=39 + # Hide NordVPN connection indicator when not connected. + typeset -g POWERLEVEL9K_NORDVPN_{DISCONNECTED,CONNECTING,DISCONNECTING}_CONTENT_EXPANSION= + typeset -g POWERLEVEL9K_NORDVPN_{DISCONNECTED,CONNECTING,DISCONNECTING}_VISUAL_IDENTIFIER_EXPANSION= + # Custom icon. + typeset -g POWERLEVEL9K_NORDVPN_VISUAL_IDENTIFIER_EXPANSION='nord' + + #################[ ranger: ranger shell (https://github.com/ranger/ranger) ]################## + # Ranger shell color. + typeset -g POWERLEVEL9K_RANGER_FOREGROUND=178 + # Custom icon. + typeset -g POWERLEVEL9K_RANGER_VISUAL_IDENTIFIER_EXPANSION='▲' + + ######################[ nnn: nnn shell (https://github.com/jarun/nnn) ]####################### + # Nnn shell color. + typeset -g POWERLEVEL9K_NNN_FOREGROUND=72 + # Custom icon. + # typeset -g POWERLEVEL9K_NNN_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ##################[ xplr: xplr shell (https://github.com/sayanarijit/xplr) ]################## + # xplr shell color. + typeset -g POWERLEVEL9K_XPLR_FOREGROUND=72 + # Custom icon. + # typeset -g POWERLEVEL9K_XPLR_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ###########################[ vim_shell: vim shell indicator (:sh) ]########################### + # Vim shell indicator color. + typeset -g POWERLEVEL9K_VIM_SHELL_FOREGROUND=34 + # Custom icon. + # typeset -g POWERLEVEL9K_VIM_SHELL_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ######[ midnight_commander: midnight commander shell (https://midnight-commander.org/) ]###### + # Midnight Commander shell color. + typeset -g POWERLEVEL9K_MIDNIGHT_COMMANDER_FOREGROUND=178 + # Custom icon. + # typeset -g POWERLEVEL9K_MIDNIGHT_COMMANDER_VISUAL_IDENTIFIER_EXPANSION='⭐' + + #[ nix_shell: nix shell (https://nixos.org/nixos/nix-pills/developing-with-nix-shell.html) ]## + # Nix shell color. + typeset -g POWERLEVEL9K_NIX_SHELL_FOREGROUND=74 + + # Tip: If you want to see just the icon without "pure" and "impure", uncomment the next line. + # typeset -g POWERLEVEL9K_NIX_SHELL_CONTENT_EXPANSION= + + # Custom icon. + # typeset -g POWERLEVEL9K_NIX_SHELL_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ##################################[ disk_usage: disk usage ]################################## + # Colors for different levels of disk usage. + typeset -g POWERLEVEL9K_DISK_USAGE_NORMAL_FOREGROUND=35 + typeset -g POWERLEVEL9K_DISK_USAGE_WARNING_FOREGROUND=220 + typeset -g POWERLEVEL9K_DISK_USAGE_CRITICAL_FOREGROUND=160 + # Thresholds for different levels of disk usage (percentage points). + typeset -g POWERLEVEL9K_DISK_USAGE_WARNING_LEVEL=90 + typeset -g POWERLEVEL9K_DISK_USAGE_CRITICAL_LEVEL=95 + # If set to true, hide disk usage when below $POWERLEVEL9K_DISK_USAGE_WARNING_LEVEL percent. + typeset -g POWERLEVEL9K_DISK_USAGE_ONLY_WARNING=false + # Custom icon. + # typeset -g POWERLEVEL9K_DISK_USAGE_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ###########[ vi_mode: vi mode (you don't need this if you've enabled prompt_char) ]########### + # Text and color for normal (a.k.a. command) vi mode. + typeset -g POWERLEVEL9K_VI_COMMAND_MODE_STRING=NORMAL + typeset -g POWERLEVEL9K_VI_MODE_NORMAL_FOREGROUND=106 + # Text and color for visual vi mode. + typeset -g POWERLEVEL9K_VI_VISUAL_MODE_STRING=VISUAL + typeset -g POWERLEVEL9K_VI_MODE_VISUAL_FOREGROUND=68 + # Text and color for overtype (a.k.a. overwrite and replace) vi mode. + typeset -g POWERLEVEL9K_VI_OVERWRITE_MODE_STRING=OVERTYPE + typeset -g POWERLEVEL9K_VI_MODE_OVERWRITE_FOREGROUND=172 + # Text and color for insert vi mode. + typeset -g POWERLEVEL9K_VI_INSERT_MODE_STRING= + typeset -g POWERLEVEL9K_VI_MODE_INSERT_FOREGROUND=66 + + # Custom icon. + typeset -g POWERLEVEL9K_RANGER_VISUAL_IDENTIFIER_EXPANSION='▲' + + ######################################[ ram: free RAM ]####################################### + # RAM color. + typeset -g POWERLEVEL9K_RAM_FOREGROUND=66 + # Custom icon. + # typeset -g POWERLEVEL9K_RAM_VISUAL_IDENTIFIER_EXPANSION='⭐' + + #####################################[ swap: used swap ]###################################### + # Swap color. + typeset -g POWERLEVEL9K_SWAP_FOREGROUND=96 + # Custom icon. + # typeset -g POWERLEVEL9K_SWAP_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ######################################[ load: CPU load ]###################################### + # Show average CPU load over this many last minutes. Valid values are 1, 5 and 15. + typeset -g POWERLEVEL9K_LOAD_WHICH=5 + # Load color when load is under 50%. + typeset -g POWERLEVEL9K_LOAD_NORMAL_FOREGROUND=66 + # Load color when load is between 50% and 70%. + typeset -g POWERLEVEL9K_LOAD_WARNING_FOREGROUND=178 + # Load color when load is over 70%. + typeset -g POWERLEVEL9K_LOAD_CRITICAL_FOREGROUND=166 + # Custom icon. + # typeset -g POWERLEVEL9K_LOAD_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ################[ todo: todo items (https://github.com/todotxt/todo.txt-cli) ]################ + # Todo color. + typeset -g POWERLEVEL9K_TODO_FOREGROUND=110 + # Hide todo when the total number of tasks is zero. + typeset -g POWERLEVEL9K_TODO_HIDE_ZERO_TOTAL=true + # Hide todo when the number of tasks after filtering is zero. + typeset -g POWERLEVEL9K_TODO_HIDE_ZERO_FILTERED=false + + # Todo format. The following parameters are available within the expansion. + # + # - P9K_TODO_TOTAL_TASK_COUNT The total number of tasks. + # - P9K_TODO_FILTERED_TASK_COUNT The number of tasks after filtering. + # + # These variables correspond to the last line of the output of `todo.sh -p ls`: + # + # TODO: 24 of 42 tasks shown + # + # Here 24 is P9K_TODO_FILTERED_TASK_COUNT and 42 is P9K_TODO_TOTAL_TASK_COUNT. + # + # typeset -g POWERLEVEL9K_TODO_CONTENT_EXPANSION='$P9K_TODO_FILTERED_TASK_COUNT' + + # Custom icon. + # typeset -g POWERLEVEL9K_TODO_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ###########[ timewarrior: timewarrior tracking status (https://timewarrior.net/) ]############ + # Timewarrior color. + typeset -g POWERLEVEL9K_TIMEWARRIOR_FOREGROUND=110 + # If the tracked task is longer than 24 characters, truncate and append "…". + # Tip: To always display tasks without truncation, delete the following parameter. + # Tip: To hide task names and display just the icon when time tracking is enabled, set the + # value of the following parameter to "". + typeset -g POWERLEVEL9K_TIMEWARRIOR_CONTENT_EXPANSION='${P9K_CONTENT:0:24}${${P9K_CONTENT:24}:+…}' + + # Custom icon. + # typeset -g POWERLEVEL9K_TIMEWARRIOR_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ##############[ taskwarrior: taskwarrior task count (https://taskwarrior.org/) ]############## + # Taskwarrior color. + typeset -g POWERLEVEL9K_TASKWARRIOR_FOREGROUND=74 + + # Taskwarrior segment format. The following parameters are available within the expansion. + # + # - P9K_TASKWARRIOR_PENDING_COUNT The number of pending tasks: `task +PENDING count`. + # - P9K_TASKWARRIOR_OVERDUE_COUNT The number of overdue tasks: `task +OVERDUE count`. + # + # Zero values are represented as empty parameters. + # + # The default format: + # + # '${P9K_TASKWARRIOR_OVERDUE_COUNT:+"!$P9K_TASKWARRIOR_OVERDUE_COUNT/"}$P9K_TASKWARRIOR_PENDING_COUNT' + # + # typeset -g POWERLEVEL9K_TASKWARRIOR_CONTENT_EXPANSION='$P9K_TASKWARRIOR_PENDING_COUNT' + + # Custom icon. + # typeset -g POWERLEVEL9K_TASKWARRIOR_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ##################################[ context: user@hostname ]################################## + # Context color when running with privileges. + typeset -g POWERLEVEL9K_CONTEXT_ROOT_FOREGROUND=178 + # Context color in SSH without privileges. + typeset -g POWERLEVEL9K_CONTEXT_{REMOTE,REMOTE_SUDO}_FOREGROUND=180 + # Default context color (no privileges, no SSH). + typeset -g POWERLEVEL9K_CONTEXT_FOREGROUND=180 + + # Context format when running with privileges: bold user@hostname. + typeset -g POWERLEVEL9K_CONTEXT_ROOT_TEMPLATE='%B%n@%m' + # Context format when in SSH without privileges: user@hostname. + typeset -g POWERLEVEL9K_CONTEXT_{REMOTE,REMOTE_SUDO}_TEMPLATE='%n@%m' + # Default context format (no privileges, no SSH): user@hostname. + typeset -g POWERLEVEL9K_CONTEXT_TEMPLATE='%n@%m' + + # Don't show context unless running with privileges or in SSH. + # Tip: Remove the next line to always show context. + typeset -g POWERLEVEL9K_CONTEXT_{DEFAULT,SUDO}_{CONTENT,VISUAL_IDENTIFIER}_EXPANSION= + + # Custom icon. + # typeset -g POWERLEVEL9K_CONTEXT_VISUAL_IDENTIFIER_EXPANSION='⭐' + # Custom prefix. + # typeset -g POWERLEVEL9K_CONTEXT_PREFIX='%248Fwith ' + + ###[ virtualenv: python virtual environment (https://docs.python.org/3/library/venv.html) ]### + # Python virtual environment color. + typeset -g POWERLEVEL9K_VIRTUALENV_FOREGROUND=37 + # Don't show Python version next to the virtual environment name. + typeset -g POWERLEVEL9K_VIRTUALENV_SHOW_PYTHON_VERSION=false + # If set to "false", won't show virtualenv if pyenv is already shown. + # If set to "if-different", won't show virtualenv if it's the same as pyenv. + typeset -g POWERLEVEL9K_VIRTUALENV_SHOW_WITH_PYENV=false + # Separate environment name from Python version only with a space. + typeset -g POWERLEVEL9K_VIRTUALENV_{LEFT,RIGHT}_DELIMITER= + # Custom icon. + # typeset -g POWERLEVEL9K_VIRTUALENV_VISUAL_IDENTIFIER_EXPANSION='⭐' + + #####################[ anaconda: conda environment (https://conda.io/) ]###################### + # Anaconda environment color. + typeset -g POWERLEVEL9K_ANACONDA_FOREGROUND=37 + + # Anaconda segment format. The following parameters are available within the expansion. + # + # - CONDA_PREFIX Absolute path to the active Anaconda/Miniconda environment. + # - CONDA_DEFAULT_ENV Name of the active Anaconda/Miniconda environment. + # - CONDA_PROMPT_MODIFIER Configurable prompt modifier (see below). + # - P9K_ANACONDA_PYTHON_VERSION Current python version (python --version). + # + # CONDA_PROMPT_MODIFIER can be configured with the following command: + # + # conda config --set env_prompt '({default_env}) ' + # + # The last argument is a Python format string that can use the following variables: + # + # - prefix The same as CONDA_PREFIX. + # - default_env The same as CONDA_DEFAULT_ENV. + # - name The last segment of CONDA_PREFIX. + # - stacked_env Comma-separated list of names in the environment stack. The first element is + # always the same as default_env. + # + # Note: '({default_env}) ' is the default value of env_prompt. + # + # The default value of POWERLEVEL9K_ANACONDA_CONTENT_EXPANSION expands to $CONDA_PROMPT_MODIFIER + # without the surrounding parentheses, or to the last path component of CONDA_PREFIX if the former + # is empty. + typeset -g POWERLEVEL9K_ANACONDA_CONTENT_EXPANSION='${${${${CONDA_PROMPT_MODIFIER#\(}% }%\)}:-${CONDA_PREFIX:t}}' + + # Custom icon. + # typeset -g POWERLEVEL9K_ANACONDA_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ################[ pyenv: python environment (https://github.com/pyenv/pyenv) ]################ + # Pyenv color. + typeset -g POWERLEVEL9K_PYENV_FOREGROUND=37 + # Hide python version if it doesn't come from one of these sources. + typeset -g POWERLEVEL9K_PYENV_SOURCES=(shell local global) + # If set to false, hide python version if it's the same as global: + # $(pyenv version-name) == $(pyenv global). + typeset -g POWERLEVEL9K_PYENV_PROMPT_ALWAYS_SHOW=false + # If set to false, hide python version if it's equal to "system". + typeset -g POWERLEVEL9K_PYENV_SHOW_SYSTEM=true + + # Pyenv segment format. The following parameters are available within the expansion. + # + # - P9K_CONTENT Current pyenv environment (pyenv version-name). + # - P9K_PYENV_PYTHON_VERSION Current python version (python --version). + # + # The default format has the following logic: + # + # 1. Display just "$P9K_CONTENT" if it's equal to "$P9K_PYENV_PYTHON_VERSION" or + # starts with "$P9K_PYENV_PYTHON_VERSION/". + # 2. Otherwise display "$P9K_CONTENT $P9K_PYENV_PYTHON_VERSION". + typeset -g POWERLEVEL9K_PYENV_CONTENT_EXPANSION='${P9K_CONTENT}${${P9K_CONTENT:#$P9K_PYENV_PYTHON_VERSION(|/*)}:+ $P9K_PYENV_PYTHON_VERSION}' + + # Custom icon. + # typeset -g POWERLEVEL9K_PYENV_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ################[ goenv: go environment (https://github.com/syndbg/goenv) ]################ + # Goenv color. + typeset -g POWERLEVEL9K_GOENV_FOREGROUND=37 + # Hide go version if it doesn't come from one of these sources. + typeset -g POWERLEVEL9K_GOENV_SOURCES=(shell local global) + # If set to false, hide go version if it's the same as global: + # $(goenv version-name) == $(goenv global). + typeset -g POWERLEVEL9K_GOENV_PROMPT_ALWAYS_SHOW=false + # If set to false, hide go version if it's equal to "system". + typeset -g POWERLEVEL9K_GOENV_SHOW_SYSTEM=true + # Custom icon. + # typeset -g POWERLEVEL9K_GOENV_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ##########[ nodenv: node.js version from nodenv (https://github.com/nodenv/nodenv) ]########## + # Nodenv color. + typeset -g POWERLEVEL9K_NODENV_FOREGROUND=70 + # Hide node version if it doesn't come from one of these sources. + typeset -g POWERLEVEL9K_NODENV_SOURCES=(shell local global) + # If set to false, hide node version if it's the same as global: + # $(nodenv version-name) == $(nodenv global). + typeset -g POWERLEVEL9K_NODENV_PROMPT_ALWAYS_SHOW=false + # If set to false, hide node version if it's equal to "system". + typeset -g POWERLEVEL9K_NODENV_SHOW_SYSTEM=true + # Custom icon. + # typeset -g POWERLEVEL9K_NODENV_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ##############[ nvm: node.js version from nvm (https://github.com/nvm-sh/nvm) ]############### + # Nvm color. + typeset -g POWERLEVEL9K_NVM_FOREGROUND=70 + # Custom icon. + # typeset -g POWERLEVEL9K_NVM_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ############[ nodeenv: node.js environment (https://github.com/ekalinin/nodeenv) ]############ + # Nodeenv color. + typeset -g POWERLEVEL9K_NODEENV_FOREGROUND=70 + # Don't show Node version next to the environment name. + typeset -g POWERLEVEL9K_NODEENV_SHOW_NODE_VERSION=false + # Separate environment name from Node version only with a space. + typeset -g POWERLEVEL9K_NODEENV_{LEFT,RIGHT}_DELIMITER= + # Custom icon. + # typeset -g POWERLEVEL9K_NODEENV_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ##############################[ node_version: node.js version ]############################### + # Node version color. + typeset -g POWERLEVEL9K_NODE_VERSION_FOREGROUND=70 + # Show node version only when in a directory tree containing package.json. + typeset -g POWERLEVEL9K_NODE_VERSION_PROJECT_ONLY=true + # Custom icon. + # typeset -g POWERLEVEL9K_NODE_VERSION_VISUAL_IDENTIFIER_EXPANSION='⭐' + + #######################[ go_version: go version (https://golang.org) ]######################## + # Go version color. + typeset -g POWERLEVEL9K_GO_VERSION_FOREGROUND=37 + # Show go version only when in a go project subdirectory. + typeset -g POWERLEVEL9K_GO_VERSION_PROJECT_ONLY=true + # Custom icon. + # typeset -g POWERLEVEL9K_GO_VERSION_VISUAL_IDENTIFIER_EXPANSION='⭐' + + #################[ rust_version: rustc version (https://www.rust-lang.org) ]################## + # Rust version color. + typeset -g POWERLEVEL9K_RUST_VERSION_FOREGROUND=37 + # Show rust version only when in a rust project subdirectory. + typeset -g POWERLEVEL9K_RUST_VERSION_PROJECT_ONLY=true + # Custom icon. + # typeset -g POWERLEVEL9K_RUST_VERSION_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ###############[ dotnet_version: .NET version (https://dotnet.microsoft.com) ]################ + # .NET version color. + typeset -g POWERLEVEL9K_DOTNET_VERSION_FOREGROUND=134 + # Show .NET version only when in a .NET project subdirectory. + typeset -g POWERLEVEL9K_DOTNET_VERSION_PROJECT_ONLY=true + # Custom icon. + # typeset -g POWERLEVEL9K_DOTNET_VERSION_VISUAL_IDENTIFIER_EXPANSION='⭐' + + #####################[ php_version: php version (https://www.php.net/) ]###################### + # PHP version color. + typeset -g POWERLEVEL9K_PHP_VERSION_FOREGROUND=99 + # Show PHP version only when in a PHP project subdirectory. + typeset -g POWERLEVEL9K_PHP_VERSION_PROJECT_ONLY=true + # Custom icon. + # typeset -g POWERLEVEL9K_PHP_VERSION_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ##########[ laravel_version: laravel php framework version (https://laravel.com/) ]########### + # Laravel version color. + typeset -g POWERLEVEL9K_LARAVEL_VERSION_FOREGROUND=161 + # Custom icon. + # typeset -g POWERLEVEL9K_LARAVEL_VERSION_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ####################[ java_version: java version (https://www.java.com/) ]#################### + # Java version color. + typeset -g POWERLEVEL9K_JAVA_VERSION_FOREGROUND=32 + # Show java version only when in a java project subdirectory. + typeset -g POWERLEVEL9K_JAVA_VERSION_PROJECT_ONLY=true + # Show brief version. + typeset -g POWERLEVEL9K_JAVA_VERSION_FULL=false + # Custom icon. + # typeset -g POWERLEVEL9K_JAVA_VERSION_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ###[ package: name@version from package.json (https://docs.npmjs.com/files/package.json) ]#### + # Package color. + typeset -g POWERLEVEL9K_PACKAGE_FOREGROUND=117 + # Package format. The following parameters are available within the expansion. + # + # - P9K_PACKAGE_NAME The value of `name` field in package.json. + # - P9K_PACKAGE_VERSION The value of `version` field in package.json. + # + # typeset -g POWERLEVEL9K_PACKAGE_CONTENT_EXPANSION='${P9K_PACKAGE_NAME//\%/%%}@${P9K_PACKAGE_VERSION//\%/%%}' + # Custom icon. + # typeset -g POWERLEVEL9K_PACKAGE_VISUAL_IDENTIFIER_EXPANSION='⭐' + + #############[ rbenv: ruby version from rbenv (https://github.com/rbenv/rbenv) ]############## + # Rbenv color. + typeset -g POWERLEVEL9K_RBENV_FOREGROUND=168 + # Hide ruby version if it doesn't come from one of these sources. + typeset -g POWERLEVEL9K_RBENV_SOURCES=(shell local global) + # If set to false, hide ruby version if it's the same as global: + # $(rbenv version-name) == $(rbenv global). + typeset -g POWERLEVEL9K_RBENV_PROMPT_ALWAYS_SHOW=false + # If set to false, hide ruby version if it's equal to "system". + typeset -g POWERLEVEL9K_RBENV_SHOW_SYSTEM=true + # Custom icon. + # typeset -g POWERLEVEL9K_RBENV_VISUAL_IDENTIFIER_EXPANSION='⭐' + + #######################[ rvm: ruby version from rvm (https://rvm.io) ]######################## + # Rvm color. + typeset -g POWERLEVEL9K_RVM_FOREGROUND=168 + # Don't show @gemset at the end. + typeset -g POWERLEVEL9K_RVM_SHOW_GEMSET=false + # Don't show ruby- at the front. + typeset -g POWERLEVEL9K_RVM_SHOW_PREFIX=false + # Custom icon. + # typeset -g POWERLEVEL9K_RVM_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ###########[ fvm: flutter version management (https://github.com/leoafarias/fvm) ]############ + # Fvm color. + typeset -g POWERLEVEL9K_FVM_FOREGROUND=38 + # Custom icon. + # typeset -g POWERLEVEL9K_FVM_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ##########[ luaenv: lua version from luaenv (https://github.com/cehoffman/luaenv) ]########### + # Lua color. + typeset -g POWERLEVEL9K_LUAENV_FOREGROUND=32 + # Hide lua version if it doesn't come from one of these sources. + typeset -g POWERLEVEL9K_LUAENV_SOURCES=(shell local global) + # If set to false, hide lua version if it's the same as global: + # $(luaenv version-name) == $(luaenv global). + typeset -g POWERLEVEL9K_LUAENV_PROMPT_ALWAYS_SHOW=false + # If set to false, hide lua version if it's equal to "system". + typeset -g POWERLEVEL9K_LUAENV_SHOW_SYSTEM=true + # Custom icon. + # typeset -g POWERLEVEL9K_LUAENV_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ###############[ jenv: java version from jenv (https://github.com/jenv/jenv) ]################ + # Java color. + typeset -g POWERLEVEL9K_JENV_FOREGROUND=32 + # Hide java version if it doesn't come from one of these sources. + typeset -g POWERLEVEL9K_JENV_SOURCES=(shell local global) + # If set to false, hide java version if it's the same as global: + # $(jenv version-name) == $(jenv global). + typeset -g POWERLEVEL9K_JENV_PROMPT_ALWAYS_SHOW=false + # If set to false, hide java version if it's equal to "system". + typeset -g POWERLEVEL9K_JENV_SHOW_SYSTEM=true + # Custom icon. + # typeset -g POWERLEVEL9K_JENV_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ###########[ plenv: perl version from plenv (https://github.com/tokuhirom/plenv) ]############ + # Perl color. + typeset -g POWERLEVEL9K_PLENV_FOREGROUND=67 + # Hide perl version if it doesn't come from one of these sources. + typeset -g POWERLEVEL9K_PLENV_SOURCES=(shell local global) + # If set to false, hide perl version if it's the same as global: + # $(plenv version-name) == $(plenv global). + typeset -g POWERLEVEL9K_PLENV_PROMPT_ALWAYS_SHOW=false + # If set to false, hide perl version if it's equal to "system". + typeset -g POWERLEVEL9K_PLENV_SHOW_SYSTEM=true + # Custom icon. + # typeset -g POWERLEVEL9K_PLENV_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ############[ phpenv: php version from phpenv (https://github.com/phpenv/phpenv) ]############ + # PHP color. + typeset -g POWERLEVEL9K_PHPENV_FOREGROUND=99 + # Hide php version if it doesn't come from one of these sources. + typeset -g POWERLEVEL9K_PHPENV_SOURCES=(shell local global) + # If set to false, hide php version if it's the same as global: + # $(phpenv version-name) == $(phpenv global). + typeset -g POWERLEVEL9K_PHPENV_PROMPT_ALWAYS_SHOW=false + # If set to false, hide php version if it's equal to "system". + typeset -g POWERLEVEL9K_PHPENV_SHOW_SYSTEM=true + # Custom icon. + # typeset -g POWERLEVEL9K_PHPENV_VISUAL_IDENTIFIER_EXPANSION='⭐' + + #######[ scalaenv: scala version from scalaenv (https://github.com/scalaenv/scalaenv) ]####### + # Scala color. + typeset -g POWERLEVEL9K_SCALAENV_FOREGROUND=160 + # Hide scala version if it doesn't come from one of these sources. + typeset -g POWERLEVEL9K_SCALAENV_SOURCES=(shell local global) + # If set to false, hide scala version if it's the same as global: + # $(scalaenv version-name) == $(scalaenv global). + typeset -g POWERLEVEL9K_SCALAENV_PROMPT_ALWAYS_SHOW=false + # If set to false, hide scala version if it's equal to "system". + typeset -g POWERLEVEL9K_SCALAENV_SHOW_SYSTEM=true + # Custom icon. + # typeset -g POWERLEVEL9K_SCALAENV_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ##########[ haskell_stack: haskell version from stack (https://haskellstack.org/) ]########### + # Haskell color. + typeset -g POWERLEVEL9K_HASKELL_STACK_FOREGROUND=172 + # Hide haskell version if it doesn't come from one of these sources. + # + # shell: version is set by STACK_YAML + # local: version is set by stack.yaml up the directory tree + # global: version is set by the implicit global project (~/.stack/global-project/stack.yaml) + typeset -g POWERLEVEL9K_HASKELL_STACK_SOURCES=(shell local) + # If set to false, hide haskell version if it's the same as in the implicit global project. + typeset -g POWERLEVEL9K_HASKELL_STACK_ALWAYS_SHOW=true + # Custom icon. + # typeset -g POWERLEVEL9K_HASKELL_STACK_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ################[ terraform: terraform workspace (https://www.terraform.io) ]################# + # Don't show terraform workspace if it's literally "default". + typeset -g POWERLEVEL9K_TERRAFORM_SHOW_DEFAULT=false + # POWERLEVEL9K_TERRAFORM_CLASSES is an array with even number of elements. The first element + # in each pair defines a pattern against which the current terraform workspace gets matched. + # More specifically, it's P9K_CONTENT prior to the application of context expansion (see below) + # that gets matched. If you unset all POWERLEVEL9K_TERRAFORM_*CONTENT_EXPANSION parameters, + # you'll see this value in your prompt. The second element of each pair in + # POWERLEVEL9K_TERRAFORM_CLASSES defines the workspace class. Patterns are tried in order. The + # first match wins. + # + # For example, given these settings: + # + # typeset -g POWERLEVEL9K_TERRAFORM_CLASSES=( + # '*prod*' PROD + # '*test*' TEST + # '*' OTHER) + # + # If your current terraform workspace is "project_test", its class is TEST because "project_test" + # doesn't match the pattern '*prod*' but does match '*test*'. + # + # You can define different colors, icons and content expansions for different classes: + # + # typeset -g POWERLEVEL9K_TERRAFORM_TEST_FOREGROUND=28 + # typeset -g POWERLEVEL9K_TERRAFORM_TEST_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_TERRAFORM_TEST_CONTENT_EXPANSION='> ${P9K_CONTENT} <' + typeset -g POWERLEVEL9K_TERRAFORM_CLASSES=( + # '*prod*' PROD # These values are examples that are unlikely + # '*test*' TEST # to match your needs. Customize them as needed. + '*' OTHER) + typeset -g POWERLEVEL9K_TERRAFORM_OTHER_FOREGROUND=38 + # typeset -g POWERLEVEL9K_TERRAFORM_OTHER_VISUAL_IDENTIFIER_EXPANSION='⭐' + + #############[ terraform_version: terraform version (https://www.terraform.io) ]############## + # Terraform version color. + typeset -g POWERLEVEL9K_TERRAFORM_VERSION_FOREGROUND=38 + # Custom icon. + # typeset -g POWERLEVEL9K_TERRAFORM_VERSION_VISUAL_IDENTIFIER_EXPANSION='⭐' + + #############[ kubecontext: current kubernetes context (https://kubernetes.io/) ]############# + # Show kubecontext only when the the command you are typing invokes one of these tools. + # Tip: Remove the next line to always show kubecontext. + typeset -g POWERLEVEL9K_KUBECONTEXT_SHOW_ON_COMMAND='kubectl|helm|kubens|kubectx|oc|istioctl|kogito|k9s|helmfile|flux|fluxctl|stern' + + # Kubernetes context classes for the purpose of using different colors, icons and expansions with + # different contexts. + # + # POWERLEVEL9K_KUBECONTEXT_CLASSES is an array with even number of elements. The first element + # in each pair defines a pattern against which the current kubernetes context gets matched. + # More specifically, it's P9K_CONTENT prior to the application of context expansion (see below) + # that gets matched. If you unset all POWERLEVEL9K_KUBECONTEXT_*CONTENT_EXPANSION parameters, + # you'll see this value in your prompt. The second element of each pair in + # POWERLEVEL9K_KUBECONTEXT_CLASSES defines the context class. Patterns are tried in order. The + # first match wins. + # + # For example, given these settings: + # + # typeset -g POWERLEVEL9K_KUBECONTEXT_CLASSES=( + # '*prod*' PROD + # '*test*' TEST + # '*' DEFAULT) + # + # If your current kubernetes context is "deathray-testing/default", its class is TEST + # because "deathray-testing/default" doesn't match the pattern '*prod*' but does match '*test*'. + # + # You can define different colors, icons and content expansions for different classes: + # + # typeset -g POWERLEVEL9K_KUBECONTEXT_TEST_FOREGROUND=28 + # typeset -g POWERLEVEL9K_KUBECONTEXT_TEST_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_KUBECONTEXT_TEST_CONTENT_EXPANSION='> ${P9K_CONTENT} <' + typeset -g POWERLEVEL9K_KUBECONTEXT_CLASSES=( + # '*prod*' PROD # These values are examples that are unlikely + # '*test*' TEST # to match your needs. Customize them as needed. + '*' DEFAULT) + typeset -g POWERLEVEL9K_KUBECONTEXT_DEFAULT_FOREGROUND=134 + typeset -g POWERLEVEL9K_KUBECONTEXT_DEFAULT_VISUAL_IDENTIFIER_EXPANSION='○' + + # Use POWERLEVEL9K_KUBECONTEXT_CONTENT_EXPANSION to specify the content displayed by kubecontext + # segment. Parameter expansions are very flexible and fast, too. See reference: + # http://zsh.sourceforge.net/Doc/Release/Expansion.html#Parameter-Expansion. + # + # Within the expansion the following parameters are always available: + # + # - P9K_CONTENT The content that would've been displayed if there was no content + # expansion defined. + # - P9K_KUBECONTEXT_NAME The current context's name. Corresponds to column NAME in the + # output of `kubectl config get-contexts`. + # - P9K_KUBECONTEXT_CLUSTER The current context's cluster. Corresponds to column CLUSTER in the + # output of `kubectl config get-contexts`. + # - P9K_KUBECONTEXT_NAMESPACE The current context's namespace. Corresponds to column NAMESPACE + # in the output of `kubectl config get-contexts`. If there is no + # namespace, the parameter is set to "default". + # - P9K_KUBECONTEXT_USER The current context's user. Corresponds to column AUTHINFO in the + # output of `kubectl config get-contexts`. + # + # If the context points to Google Kubernetes Engine (GKE) or Elastic Kubernetes Service (EKS), + # the following extra parameters are available: + # + # - P9K_KUBECONTEXT_CLOUD_NAME Either "gke" or "eks". + # - P9K_KUBECONTEXT_CLOUD_ACCOUNT Account/project ID. + # - P9K_KUBECONTEXT_CLOUD_ZONE Availability zone. + # - P9K_KUBECONTEXT_CLOUD_CLUSTER Cluster. + # + # P9K_KUBECONTEXT_CLOUD_* parameters are derived from P9K_KUBECONTEXT_CLUSTER. For example, + # if P9K_KUBECONTEXT_CLUSTER is "gke_my-account_us-east1-a_my-cluster-01": + # + # - P9K_KUBECONTEXT_CLOUD_NAME=gke + # - P9K_KUBECONTEXT_CLOUD_ACCOUNT=my-account + # - P9K_KUBECONTEXT_CLOUD_ZONE=us-east1-a + # - P9K_KUBECONTEXT_CLOUD_CLUSTER=my-cluster-01 + # + # If P9K_KUBECONTEXT_CLUSTER is "arn:aws:eks:us-east-1:123456789012:cluster/my-cluster-01": + # + # - P9K_KUBECONTEXT_CLOUD_NAME=eks + # - P9K_KUBECONTEXT_CLOUD_ACCOUNT=123456789012 + # - P9K_KUBECONTEXT_CLOUD_ZONE=us-east-1 + # - P9K_KUBECONTEXT_CLOUD_CLUSTER=my-cluster-01 + typeset -g POWERLEVEL9K_KUBECONTEXT_DEFAULT_CONTENT_EXPANSION= + # Show P9K_KUBECONTEXT_CLOUD_CLUSTER if it's not empty and fall back to P9K_KUBECONTEXT_NAME. + POWERLEVEL9K_KUBECONTEXT_DEFAULT_CONTENT_EXPANSION+='${P9K_KUBECONTEXT_CLOUD_CLUSTER:-${P9K_KUBECONTEXT_NAME}}' + # Append the current context's namespace if it's not "default". + POWERLEVEL9K_KUBECONTEXT_DEFAULT_CONTENT_EXPANSION+='${${:-/$P9K_KUBECONTEXT_NAMESPACE}:#/default}' + + # Custom prefix. + # typeset -g POWERLEVEL9K_KUBECONTEXT_PREFIX='%248Fat ' + + #[ aws: aws profile (https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-profiles.html) ]# + # Show aws only when the the command you are typing invokes one of these tools. + # Tip: Remove the next line to always show aws. + typeset -g POWERLEVEL9K_AWS_SHOW_ON_COMMAND='aws|awless|terraform|pulumi|terragrunt' + + # POWERLEVEL9K_AWS_CLASSES is an array with even number of elements. The first element + # in each pair defines a pattern against which the current AWS profile gets matched. + # More specifically, it's P9K_CONTENT prior to the application of context expansion (see below) + # that gets matched. If you unset all POWERLEVEL9K_AWS_*CONTENT_EXPANSION parameters, + # you'll see this value in your prompt. The second element of each pair in + # POWERLEVEL9K_AWS_CLASSES defines the profile class. Patterns are tried in order. The + # first match wins. + # + # For example, given these settings: + # + # typeset -g POWERLEVEL9K_AWS_CLASSES=( + # '*prod*' PROD + # '*test*' TEST + # '*' DEFAULT) + # + # If your current AWS profile is "company_test", its class is TEST + # because "company_test" doesn't match the pattern '*prod*' but does match '*test*'. + # + # You can define different colors, icons and content expansions for different classes: + # + # typeset -g POWERLEVEL9K_AWS_TEST_FOREGROUND=28 + # typeset -g POWERLEVEL9K_AWS_TEST_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_AWS_TEST_CONTENT_EXPANSION='> ${P9K_CONTENT} <' + typeset -g POWERLEVEL9K_AWS_CLASSES=( + # '*prod*' PROD # These values are examples that are unlikely + # '*test*' TEST # to match your needs. Customize them as needed. + '*' DEFAULT) + typeset -g POWERLEVEL9K_AWS_DEFAULT_FOREGROUND=208 + # typeset -g POWERLEVEL9K_AWS_DEFAULT_VISUAL_IDENTIFIER_EXPANSION='⭐' + + # AWS segment format. The following parameters are available within the expansion. + # + # - P9K_AWS_PROFILE The name of the current AWS profile. + # - P9K_AWS_REGION The region associated with the current AWS profile. + typeset -g POWERLEVEL9K_AWS_CONTENT_EXPANSION='${P9K_AWS_PROFILE//\%/%%}${P9K_AWS_REGION:+ ${P9K_AWS_REGION//\%/%%}}' + + #[ aws_eb_env: aws elastic beanstalk environment (https://aws.amazon.com/elasticbeanstalk/) ]# + # AWS Elastic Beanstalk environment color. + typeset -g POWERLEVEL9K_AWS_EB_ENV_FOREGROUND=70 + # Custom icon. + typeset -g POWERLEVEL9K_AWS_EB_ENV_VISUAL_IDENTIFIER_EXPANSION='eb' + + ##########[ azure: azure account name (https://docs.microsoft.com/en-us/cli/azure) ]########## + # Show azure only when the the command you are typing invokes one of these tools. + # Tip: Remove the next line to always show azure. + typeset -g POWERLEVEL9K_AZURE_SHOW_ON_COMMAND='az|terraform|pulumi|terragrunt' + # Azure account name color. + typeset -g POWERLEVEL9K_AZURE_FOREGROUND=32 + # Custom icon. + typeset -g POWERLEVEL9K_AZURE_VISUAL_IDENTIFIER_EXPANSION='az' + + ##########[ gcloud: google cloud account and project (https://cloud.google.com/) ]########### + # Show gcloud only when the the command you are typing invokes one of these tools. + # Tip: Remove the next line to always show gcloud. + typeset -g POWERLEVEL9K_GCLOUD_SHOW_ON_COMMAND='gcloud|gcs' + # Google cloud color. + typeset -g POWERLEVEL9K_GCLOUD_FOREGROUND=32 + + # Google cloud format. Change the value of POWERLEVEL9K_GCLOUD_PARTIAL_CONTENT_EXPANSION and/or + # POWERLEVEL9K_GCLOUD_COMPLETE_CONTENT_EXPANSION if the default is too verbose or not informative + # enough. You can use the following parameters in the expansions. Each of them corresponds to the + # output of `gcloud` tool. + # + # Parameter | Source + # -------------------------|-------------------------------------------------------------------- + # P9K_GCLOUD_CONFIGURATION | gcloud config configurations list --format='value(name)' + # P9K_GCLOUD_ACCOUNT | gcloud config get-value account + # P9K_GCLOUD_PROJECT_ID | gcloud config get-value project + # P9K_GCLOUD_PROJECT_NAME | gcloud projects describe $P9K_GCLOUD_PROJECT_ID --format='value(name)' + # + # Note: ${VARIABLE//\%/%%} expands to ${VARIABLE} with all occurrences of '%' replaced with '%%'. + # + # Obtaining project name requires sending a request to Google servers. This can take a long time + # and even fail. When project name is unknown, P9K_GCLOUD_PROJECT_NAME is not set and gcloud + # prompt segment is in state PARTIAL. When project name gets known, P9K_GCLOUD_PROJECT_NAME gets + # set and gcloud prompt segment transitions to state COMPLETE. + # + # You can customize the format, icon and colors of gcloud segment separately for states PARTIAL + # and COMPLETE. You can also hide gcloud in state PARTIAL by setting + # POWERLEVEL9K_GCLOUD_PARTIAL_VISUAL_IDENTIFIER_EXPANSION and + # POWERLEVEL9K_GCLOUD_PARTIAL_CONTENT_EXPANSION to empty. + typeset -g POWERLEVEL9K_GCLOUD_PARTIAL_CONTENT_EXPANSION='${P9K_GCLOUD_PROJECT_ID//\%/%%}' + typeset -g POWERLEVEL9K_GCLOUD_COMPLETE_CONTENT_EXPANSION='${P9K_GCLOUD_PROJECT_NAME//\%/%%}' + + # Send a request to Google (by means of `gcloud projects describe ...`) to obtain project name + # this often. Negative value disables periodic polling. In this mode project name is retrieved + # only when the current configuration, account or project id changes. + typeset -g POWERLEVEL9K_GCLOUD_REFRESH_PROJECT_NAME_SECONDS=60 + + # Custom icon. + # typeset -g POWERLEVEL9K_GCLOUD_VISUAL_IDENTIFIER_EXPANSION='⭐' + + #[ google_app_cred: google application credentials (https://cloud.google.com/docs/authentication/production) ]# + # Show google_app_cred only when the the command you are typing invokes one of these tools. + # Tip: Remove the next line to always show google_app_cred. + typeset -g POWERLEVEL9K_GOOGLE_APP_CRED_SHOW_ON_COMMAND='terraform|pulumi|terragrunt' + + # Google application credentials classes for the purpose of using different colors, icons and + # expansions with different credentials. + # + # POWERLEVEL9K_GOOGLE_APP_CRED_CLASSES is an array with even number of elements. The first + # element in each pair defines a pattern against which the current kubernetes context gets + # matched. More specifically, it's P9K_CONTENT prior to the application of context expansion + # (see below) that gets matched. If you unset all POWERLEVEL9K_GOOGLE_APP_CRED_*CONTENT_EXPANSION + # parameters, you'll see this value in your prompt. The second element of each pair in + # POWERLEVEL9K_GOOGLE_APP_CRED_CLASSES defines the context class. Patterns are tried in order. + # The first match wins. + # + # For example, given these settings: + # + # typeset -g POWERLEVEL9K_GOOGLE_APP_CRED_CLASSES=( + # '*:*prod*:*' PROD + # '*:*test*:*' TEST + # '*' DEFAULT) + # + # If your current Google application credentials is "service_account deathray-testing x@y.com", + # its class is TEST because it doesn't match the pattern '* *prod* *' but does match '* *test* *'. + # + # You can define different colors, icons and content expansions for different classes: + # + # typeset -g POWERLEVEL9K_GOOGLE_APP_CRED_TEST_FOREGROUND=28 + # typeset -g POWERLEVEL9K_GOOGLE_APP_CRED_TEST_VISUAL_IDENTIFIER_EXPANSION='⭐' + # typeset -g POWERLEVEL9K_GOOGLE_APP_CRED_TEST_CONTENT_EXPANSION='$P9K_GOOGLE_APP_CRED_PROJECT_ID' + typeset -g POWERLEVEL9K_GOOGLE_APP_CRED_CLASSES=( + # '*:*prod*:*' PROD # These values are examples that are unlikely + # '*:*test*:*' TEST # to match your needs. Customize them as needed. + '*' DEFAULT) + typeset -g POWERLEVEL9K_GOOGLE_APP_CRED_DEFAULT_FOREGROUND=32 + # typeset -g POWERLEVEL9K_GOOGLE_APP_CRED_DEFAULT_VISUAL_IDENTIFIER_EXPANSION='⭐' + + # Use POWERLEVEL9K_GOOGLE_APP_CRED_CONTENT_EXPANSION to specify the content displayed by + # google_app_cred segment. Parameter expansions are very flexible and fast, too. See reference: + # http://zsh.sourceforge.net/Doc/Release/Expansion.html#Parameter-Expansion. + # + # You can use the following parameters in the expansion. Each of them corresponds to one of the + # fields in the JSON file pointed to by GOOGLE_APPLICATION_CREDENTIALS. + # + # Parameter | JSON key file field + # ---------------------------------+--------------- + # P9K_GOOGLE_APP_CRED_TYPE | type + # P9K_GOOGLE_APP_CRED_PROJECT_ID | project_id + # P9K_GOOGLE_APP_CRED_CLIENT_EMAIL | client_email + # + # Note: ${VARIABLE//\%/%%} expands to ${VARIABLE} with all occurrences of '%' replaced by '%%'. + typeset -g POWERLEVEL9K_GOOGLE_APP_CRED_DEFAULT_CONTENT_EXPANSION='${P9K_GOOGLE_APP_CRED_PROJECT_ID//\%/%%}' + + ##############[ toolbox: toolbox name (https://github.com/containers/toolbox) ]############### + # Toolbox color. + typeset -g POWERLEVEL9K_TOOLBOX_FOREGROUND=178 + # Don't display the name of the toolbox if it matches fedora-toolbox-*. + typeset -g POWERLEVEL9K_TOOLBOX_CONTENT_EXPANSION='${P9K_TOOLBOX_NAME:#fedora-toolbox-*}' + # Custom icon. + # typeset -g POWERLEVEL9K_TOOLBOX_VISUAL_IDENTIFIER_EXPANSION='⭐' + # Custom prefix. + # typeset -g POWERLEVEL9K_TOOLBOX_PREFIX='%248Fin ' + + ###############################[ public_ip: public IP address ]############################### + # Public IP color. + typeset -g POWERLEVEL9K_PUBLIC_IP_FOREGROUND=94 + # Custom icon. + # typeset -g POWERLEVEL9K_PUBLIC_IP_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ########################[ vpn_ip: virtual private network indicator ]######################### + # VPN IP color. + typeset -g POWERLEVEL9K_VPN_IP_FOREGROUND=81 + # When on VPN, show just an icon without the IP address. + # Tip: To display the private IP address when on VPN, remove the next line. + typeset -g POWERLEVEL9K_VPN_IP_CONTENT_EXPANSION= + # Regular expression for the VPN network interface. Run `ifconfig` or `ip -4 a show` while on VPN + # to see the name of the interface. + typeset -g POWERLEVEL9K_VPN_IP_INTERFACE='(gpd|wg|(.*tun)|tailscale)[0-9]*' + # If set to true, show one segment per matching network interface. If set to false, show only + # one segment corresponding to the first matching network interface. + # Tip: If you set it to true, you'll probably want to unset POWERLEVEL9K_VPN_IP_CONTENT_EXPANSION. + typeset -g POWERLEVEL9K_VPN_IP_SHOW_ALL=false + # Custom icon. + # typeset -g POWERLEVEL9K_VPN_IP_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ###########[ ip: ip address and bandwidth usage for a specified network interface ]########### + # IP color. + typeset -g POWERLEVEL9K_IP_FOREGROUND=38 + # The following parameters are accessible within the expansion: + # + # Parameter | Meaning + # ----------------------+------------------------------------------- + # P9K_IP_IP | IP address + # P9K_IP_INTERFACE | network interface + # P9K_IP_RX_BYTES | total number of bytes received + # P9K_IP_TX_BYTES | total number of bytes sent + # P9K_IP_RX_BYTES_DELTA | number of bytes received since last prompt + # P9K_IP_TX_BYTES_DELTA | number of bytes sent since last prompt + # P9K_IP_RX_RATE | receive rate (since last prompt) + # P9K_IP_TX_RATE | send rate (since last prompt) + typeset -g POWERLEVEL9K_IP_CONTENT_EXPANSION='${P9K_IP_RX_RATE:+%70F⇣$P9K_IP_RX_RATE }${P9K_IP_TX_RATE:+%215F⇡$P9K_IP_TX_RATE }%38F$P9K_IP_IP' + # Show information for the first network interface whose name matches this regular expression. + # Run `ifconfig` or `ip -4 a show` to see the names of all network interfaces. + typeset -g POWERLEVEL9K_IP_INTERFACE='[ew].*' + # Custom icon. + # typeset -g POWERLEVEL9K_IP_VISUAL_IDENTIFIER_EXPANSION='⭐' + + #########################[ proxy: system-wide http/https/ftp proxy ]########################## + # Proxy color. + typeset -g POWERLEVEL9K_PROXY_FOREGROUND=68 + # Custom icon. + # typeset -g POWERLEVEL9K_PROXY_VISUAL_IDENTIFIER_EXPANSION='⭐' + + ################################[ battery: internal battery ]################################# + # Show battery in red when it's below this level and not connected to power supply. + typeset -g POWERLEVEL9K_BATTERY_LOW_THRESHOLD=20 + typeset -g POWERLEVEL9K_BATTERY_LOW_FOREGROUND=160 + # Show battery in green when it's charging or fully charged. + typeset -g POWERLEVEL9K_BATTERY_{CHARGING,CHARGED}_FOREGROUND=70 + # Show battery in yellow when it's discharging. + typeset -g POWERLEVEL9K_BATTERY_DISCONNECTED_FOREGROUND=178 + # Battery pictograms going from low to high level of charge. + typeset -g POWERLEVEL9K_BATTERY_STAGES=('%K{232}▁' '%K{232}▂' '%K{232}▃' '%K{232}▄' '%K{232}▅' '%K{232}▆' '%K{232}▇' '%K{232}█') + # Don't show the remaining time to charge/discharge. + typeset -g POWERLEVEL9K_BATTERY_VERBOSE=false + + #####################################[ wifi: wifi speed ]##################################### + # WiFi color. + typeset -g POWERLEVEL9K_WIFI_FOREGROUND=68 + # Custom icon. + # typeset -g POWERLEVEL9K_WIFI_VISUAL_IDENTIFIER_EXPANSION='⭐' + + # Use different colors and icons depending on signal strength ($P9K_WIFI_BARS). + # + # # Wifi colors and icons for different signal strength levels (low to high). + # typeset -g my_wifi_fg=(68 68 68 68 68) # <-- change these values + # typeset -g my_wifi_icon=('WiFi' 'WiFi' 'WiFi' 'WiFi' 'WiFi') # <-- change these values + # + # typeset -g POWERLEVEL9K_WIFI_CONTENT_EXPANSION='%F{${my_wifi_fg[P9K_WIFI_BARS+1]}}$P9K_WIFI_LAST_TX_RATE Mbps' + # typeset -g POWERLEVEL9K_WIFI_VISUAL_IDENTIFIER_EXPANSION='%F{${my_wifi_fg[P9K_WIFI_BARS+1]}}${my_wifi_icon[P9K_WIFI_BARS+1]}' + # + # The following parameters are accessible within the expansions: + # + # Parameter | Meaning + # ----------------------+--------------- + # P9K_WIFI_SSID | service set identifier, a.k.a. network name + # P9K_WIFI_LINK_AUTH | authentication protocol such as "wpa2-psk" or "none"; empty if unknown + # P9K_WIFI_LAST_TX_RATE | wireless transmit rate in megabits per second + # P9K_WIFI_RSSI | signal strength in dBm, from -120 to 0 + # P9K_WIFI_NOISE | noise in dBm, from -120 to 0 + # P9K_WIFI_BARS | signal strength in bars, from 0 to 4 (derived from P9K_WIFI_RSSI and P9K_WIFI_NOISE) + + ####################################[ time: current time ]#################################### + # Current time color. + typeset -g POWERLEVEL9K_TIME_FOREGROUND=66 + # Format for the current time: 09:51:02. See `man 3 strftime`. + typeset -g POWERLEVEL9K_TIME_FORMAT='%D{%H:%M:%S}' + # If set to true, time will update when you hit enter. This way prompts for the past + # commands will contain the start times of their commands as opposed to the default + # behavior where they contain the end times of their preceding commands. + typeset -g POWERLEVEL9K_TIME_UPDATE_ON_COMMAND=false + # Custom icon. + typeset -g POWERLEVEL9K_TIME_VISUAL_IDENTIFIER_EXPANSION= + # Custom prefix. + # typeset -g POWERLEVEL9K_TIME_PREFIX='%248Fat ' + + # Example of a user-defined prompt segment. Function prompt_example will be called on every + # prompt if `example` prompt segment is added to POWERLEVEL9K_LEFT_PROMPT_ELEMENTS or + # POWERLEVEL9K_RIGHT_PROMPT_ELEMENTS. It displays an icon and orange text greeting the user. + # + # Type `p10k help segment` for documentation and a more sophisticated example. + function prompt_example() { + p10k segment -f 208 -i '⭐' -t 'hello, %n' + } + + # User-defined prompt segments may optionally provide an instant_prompt_* function. Its job + # is to generate the prompt segment for display in instant prompt. See + # https://github.com/romkatv/powerlevel10k/blob/master/README.md#instant-prompt. + # + # Powerlevel10k will call instant_prompt_* at the same time as the regular prompt_* function + # and will record all `p10k segment` calls it makes. When displaying instant prompt, Powerlevel10k + # will replay these calls without actually calling instant_prompt_*. It is imperative that + # instant_prompt_* always makes the same `p10k segment` calls regardless of environment. If this + # rule is not observed, the content of instant prompt will be incorrect. + # + # Usually, you should either not define instant_prompt_* or simply call prompt_* from it. If + # instant_prompt_* is not defined for a segment, the segment won't be shown in instant prompt. + function instant_prompt_example() { + # Since prompt_example always makes the same `p10k segment` calls, we can call it from + # instant_prompt_example. This will give us the same `example` prompt segment in the instant + # and regular prompts. + prompt_example + } + + # User-defined prompt segments can be customized the same way as built-in segments. + # typeset -g POWERLEVEL9K_EXAMPLE_FOREGROUND=208 + # typeset -g POWERLEVEL9K_EXAMPLE_VISUAL_IDENTIFIER_EXPANSION='⭐' + + # Transient prompt works similarly to the builtin transient_rprompt option. It trims down prompt + # when accepting a command line. Supported values: + # + # - off: Don't change prompt when accepting a command line. + # - always: Trim down prompt when accepting a command line. + # - same-dir: Trim down prompt when accepting a command line unless this is the first command + # typed after changing current working directory. + typeset -g POWERLEVEL9K_TRANSIENT_PROMPT=always + + # Instant prompt mode. + # + # - off: Disable instant prompt. Choose this if you've tried instant prompt and found + # it incompatible with your zsh configuration files. + # - quiet: Enable instant prompt and don't print warnings when detecting console output + # during zsh initialization. Choose this if you've read and understood + # https://github.com/romkatv/powerlevel10k/blob/master/README.md#instant-prompt. + # - verbose: Enable instant prompt and print a warning when detecting console output during + # zsh initialization. Choose this if you've never tried instant prompt, haven't + # seen the warning, or if you are unsure what this all means. + typeset -g POWERLEVEL9K_INSTANT_PROMPT=verbose + + # Hot reload allows you to change POWERLEVEL9K options after Powerlevel10k has been initialized. + # For example, you can type POWERLEVEL9K_BACKGROUND=red and see your prompt turn red. Hot reload + # can slow down prompt by 1-2 milliseconds, so it's better to keep it turned off unless you + # really need it. + typeset -g POWERLEVEL9K_DISABLE_HOT_RELOAD=true + + # If p10k is already loaded, reload configuration. + # This works even with POWERLEVEL9K_DISABLE_HOT_RELOAD=true. + (( ! $+functions[p10k] )) || p10k reload +} + +# Tell `p10k configure` which file it should overwrite. +typeset -g POWERLEVEL9K_CONFIG_FILE=${${(%):-%x}:a} + +(( ${#p10k_config_opts} )) && setopt ${p10k_config_opts[@]} +'builtin' 'unset' 'p10k_config_opts' diff --git a/.devcontainer/.zshrc b/.devcontainer/.zshrc new file mode 100644 index 0000000..10e40ac --- /dev/null +++ b/.devcontainer/.zshrc @@ -0,0 +1,120 @@ +# Enable Powerlevel11k instant prompt. Should stay close to the top of ~/.zshrc. +# Initialization code that may require console input (password prompts, [y/n] +# confirmations, etc.) must go above this block; everything else may go below. +if [[ -r "${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-${(%):-%n}.zsh" ]]; then + source "${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-${(%):-%n}.zsh" +fi + +# If you come from bash you might have to change your $PATH. +# export PATH=$HOME/bin:/usr/local/bin:$PATH + +# Path to your oh-my-zsh installation. +export ZSH=$HOME/.oh-my-zsh + +# Set name of the theme to load --- if set to "random", it will +# load a random theme each time oh-my-zsh is loaded, in which case, +# to know which specific one was loaded, run: echo $RANDOM_THEME +# See https://github.com/ohmyzsh/ohmyzsh/wiki/Themes +#ZSH_THEME="codespaces" +ZSH_THEME="powerlevel10k/powerlevel10k" + +# Set list of themes to pick from when loading at random +# Setting this variable when ZSH_THEME="codespaces" +# a theme from this variable instead of looking in $ZSH/themes/ +# If set to an empty array, this variable will have no effect. + +# Enable Powerlevel11k instant prompt. Should stay close to the top of ~/.zshrc. +# Initialization code that may require console input (password prompts, [y/n] +# confirmations, etc.) must go above this block; everything else may go below. +if [[ -r "${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-${(%):-%n}.zsh" ]]; then + source "${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-${(%):-%n}.zsh" +fi + +# Uncomment the following line to use case-sensitive completion. +# CASE_SENSITIVE="true" + +# Uncomment the following line to use hyphen-insensitive completion. +# Case-sensitive completion must be off. _ and - will be interchangeable. +# HYPHEN_INSENSITIVE="true" + +# Uncomment one of the following lines to change the auto-update behavior +# zstyle ':omz:update' mode disabled # disable automatic updates +# zstyle ':omz:update' mode auto # update automatically without asking +# zstyle ':omz:update' mode reminder # just remind me to update when it's time + +# Uncomment the following line to change how often to auto-update (in days). +# zstyle ':omz:update' frequency 13 + +# Uncomment the following line if pasting URLs and other text is messed up. +# DISABLE_MAGIC_FUNCTIONS="true" + +# Uncomment the following line to disable colors in ls. +# DISABLE_LS_COLORS="true" + +# Uncomment the following line to disable auto-setting terminal title. +# DISABLE_AUTO_TITLE="true" + +# Uncomment the following line to enable command auto-correction. +# ENABLE_CORRECTION="true" + +# Uncomment the following line to display red dots whilst waiting for completion. +# You can also set it to another string to have that shown instead of the default red dots. +# e.g. COMPLETION_WAITING_DOTS="%F{yellow}waiting...%f" +# Caution: this setting can cause issues with multiline prompts in zsh < 5.7.1 (see #5765) +# COMPLETION_WAITING_DOTS="true" + +# Uncomment the following line if you want to disable marking untracked files +# under VCS as dirty. This makes repository status check for large repositories +# much, much faster. +# DISABLE_UNTRACKED_FILES_DIRTY="true" + +# Uncomment the following line if you want to change the command execution time +# stamp shown in the history command output. +# You can set one of the optional three formats: +# "mm/dd/yyyy"|"dd.mm.yyyy"|"yyyy-mm-dd" +# or set a custom format using the strftime function format specifications, +# see 'man strftime' for details. +# HIST_STAMPS="mm/dd/yyyy" + +# Would you like to use another custom folder than $ZSH/custom? +# ZSH_CUSTOM=/path/to/new-custom-folder + +# Which plugins would you like to load? +# Standard plugins can be found in $ZSH/plugins/ +# Custom plugins may be added to $ZSH_CUSTOM/plugins/ +# Example format: plugins=(rails git textmate ruby lighthouse) +# Add wisely, as too many plugins slow down shell startup. +plugins=(git debian zsh-autosuggestions zsh-syntax-highlighting) + +source $ZSH/oh-my-zsh.sh + +# User configuration + +# export MANPATH="/usr/local/man:$MANPATH" + +# You may need to manually set your language environment +# export LANG=en_US.UTF-8 + +# Preferred editor for local and remote sessions +# if [[ -n $SSH_CONNECTION ]]; then +# export EDITOR='vim' +# else +# export EDITOR='mvim' +# fi + +# Compilation flags +# export ARCHFLAGS="-arch x86_64" + +# Set personal aliases, overriding those provided by oh-my-zsh libs, +# plugins, and themes. Aliases can be placed here, though oh-my-zsh +# users are encouraged to define aliases within the ZSH_CUSTOM folder. +# For a full list of active aliases, run `alias`. +# +# Example aliases +# alias zshconfig="mate ~/.zshrc" +# alias ohmyzsh="mate ~/.oh-my-zsh" +DISABLE_AUTO_UPDATE=true +DISABLE_UPDATE_PROMPT=true + +# To customize prompt, run `p10k configure` or edit ~/.p10k.zsh. +[[ ! -f ~/.p10k.zsh ]] || source ~/.p10k.zsh diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 0000000..188cb28 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,65 @@ +# See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.205.2/containers/python-3/.devcontainer/base.Dockerfile + +# [Choice] Python version (use -bullseye variants on local arm64/Apple Silicon): 3, 3.10, 3.9, 3.8, 3.7, 3.6, 3-bullseye, 3.10-bullseye, 3.9-bullseye, 3.8-bullseye, 3.7-bullseye, 3.6-bullseye, 3-buster, 3.10-buster, 3.9-buster, 3.8-buster, 3.7-buster, 3.6-buster +ARG VARIANT="3.10-bullseye" +FROM mcr.microsoft.com/vscode/devcontainers/python:0-${VARIANT} + +# [Choice] Node.js version: none, lts/*, 16, 14, 12, 10 +ARG NODE_VERSION="none" +RUN if [ "${NODE_VERSION}" != "none" ]; then su vscode -c "umask 0002 && . /usr/local/share/nvm/nvm.sh && nvm install ${NODE_VERSION} 2>&1"; fi + +RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ + && apt-get -y install lsb-release wget software-properties-common \ + && apt-get -y install build-essential ninja-build valgrind gdb lcov doxygen graphviz + +# Install latest cmake 3.22 so we have support for cmake presets and a better integration in IDEs +RUN wget https://github.com/Kitware/CMake/releases/download/v3.22.0/cmake-3.22.0-linux-x86_64.sh \ + -q -O /tmp/cmake-install.sh \ + && chmod u+x /tmp/cmake-install.sh \ + && /tmp/cmake-install.sh --skip-license --prefix=/usr \ + && rm /tmp/cmake-install.sh + +# Install clang-14 +RUN wget -qO - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add - +RUN add-apt-repository 'deb http://apt.llvm.org/bullseye/ llvm-toolchain-bullseye main' +RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ + && apt-get -y install clang-14 clangd-14 lldb-14 llvm-14 clang-format-14 clang-tidy-14 + +RUN apt-get autoremove -y && apt-get clean -y && rm -rf /var/lib/apt/lists/* + +RUN update-alternatives --install /usr/bin/clang clang /usr/bin/clang-14 100 +RUN update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-14 100 +RUN update-alternatives --install /usr/bin/clang-apply-replacements clang-apply-replacements /usr/bin/clang-apply-replacements-14 100 +RUN update-alternatives --install /usr/bin/clang-check clang-check /usr/bin/clang-check-14 100 +RUN update-alternatives --install /usr/bin/clang-query clang-query /usr/bin/clang-query-14 100 +RUN update-alternatives --install /usr/bin/clang-tidy clang-tidy /usr/bin/clang-tidy-14 100 +RUN update-alternatives --install /usr/bin/clang-format clang-format /usr/bin/clang-format-14 100 +RUN update-alternatives --install /usr/bin/scan-build scan-build /usr/bin/scan-build-14 100 +RUN update-alternatives --install /usr/bin/scan-view scan-view /usr/bin/scan-view-14 100 +RUN update-alternatives --install /usr/bin/llvm-cov llvm-cov /usr/bin/llvm-cov-14 100 +RUN update-alternatives --install /usr/bin/llvm-profdata llvm-profdata /usr/bin/llvm-profdata-14 100 + +# Our pip requirements rarely change, add them to the image. +COPY requirements.txt /tmp/pip-tmp/ +RUN if [ -f "/tmp/pip-tmp/requirements.txt" ]; then \ + pip3 --disable-pip-version-check --no-cache-dir install -r /tmp/pip-tmp/requirements.txt \ + && rm -rf /tmp/pip-tmp; \ + fi + +# Install global node packages and enable husky. +RUN su vscode -c "source /usr/local/share/nvm/nvm.sh && npm install -g husky standard-version @commitlint/cli @commitlint/config-conventional " 2>&1 + +# Setup oh-my-zsh/p10k/plugins for user vscode +ARG USERNAME=vscode +ARG USER_UID=1000 +ARG USER_GID=$USER_UID +COPY --chown=$USER_UID:$USER_GID .devcontainer/.zshrc /home/$USERNAME +RUN chmod 644 /home/$USERNAME/.zshrc +COPY --chown=$USER_UID:$USER_GID .devcontainer/.p10k.zsh /home/$USERNAME +RUN chmod 644 /home/$USERNAME/.p10k.zsh +# the user we're applying this too (otherwise it most likely install for root) +USER $USERNAME +RUN git clone https://github.com/romkatv/powerlevel10k.git ~/.oh-my-zsh/custom/themes/powerlevel10k +RUN git clone https://github.com/zsh-users/zsh-autosuggestions ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-autosuggestions +RUN git clone https://github.com/zsh-users/zsh-syntax-highlighting.git ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-syntax-highlighting +ENV SHELL=/bin/zsh diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..ba99ba3 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,59 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at: +// https://github.com/microsoft/vscode-dev-containers/tree/v0.205.2/containers/python-3 +{ + "name": "asap_dev", + "build": { + "dockerfile": "Dockerfile", + "context": "..", + "args": { + // Update 'VARIANT' to pick a Python version: 3, 3.10, 3.9, 3.8, 3.7, 3.6 + // Append -bullseye or -buster to pin to an OS version. + // Use -bullseye variants on local on arm64/Apple Silicon. + "VARIANT": "3.10-bullseye", + // Options + "NODE_VERSION": "16" + } + }, + // Set *default* container specific settings.json values on container create. + "settings": { + "python.defaultInterpreterPath": "/usr/local/bin/python", + "python.linting.enabled": true, + "python.linting.pylintEnabled": true, + "python.formatting.autopep8Path": "/usr/local/py-utils/bin/autopep8", + "python.formatting.blackPath": "/usr/local/py-utils/bin/black", + "python.formatting.yapfPath": "/usr/local/py-utils/bin/yapf", + "python.linting.banditPath": "/usr/local/py-utils/bin/bandit", + "python.linting.flake8Path": "/usr/local/py-utils/bin/flake8", + "python.linting.mypyPath": "/usr/local/py-utils/bin/mypy", + "python.linting.pycodestylePath": "/usr/local/py-utils/bin/pycodestyle", + "python.linting.pydocstylePath": "/usr/local/py-utils/bin/pydocstyle", + "python.linting.pylintPath": "/usr/local/py-utils/bin/pylint", + "python.experiments.optInto": [ + "pythonDeprecatePythonPath" + ], + "clangd.path": "/usr/bin/clangd-14", + }, + // Add the IDs of extensions you want installed when the container is created. + "extensions": [ + "ms-python.python", + "ms-python.vscode-pylance", + "ms-vscode.cpptools", + "matepek.vscode-catch2-test-adapter", + "llvm-vs-code-extensions.vscode-clangd", + "twxs.cmake", + "ms-vscode.cmake-tools", + "swyddfa.esbonio", + "eamodio.gitlens", + "hbenl.vscode-test-explorer", + "guyutongxue.cpp-reference", + "cheshirekow.cmake-format", + "kevinkyang.auto-comment-blocks", + "editorconfig.editorconfig" + ], + // Use 'forwardPorts' to make a list of ports inside the container available locally. + // "forwardPorts": [], + // Use 'postCreateCommand' to run commands after the container is created. + "postCreateCommand": "npx husky install < /dev/null", + // Comment out connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root. + "remoteUser": "vscode" +} diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..5c33de1 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,11 @@ +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +trim_trailing_whitespace = true +insert_final_newline = true +guidelines = 80, 100 + +[*.{css,html,html.in,yml,rb}] +indent_style = space +indent_size = 2 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..1fd4e6a --- /dev/null +++ b/.gitattributes @@ -0,0 +1,12 @@ +# Set default behavior to automatically normalize line endings. +* text=auto eol=lf + +# Force batch scripts to always use CRLF line endings so that if a repo is accessed +# in Windows via a file share from Linux, the scripts will work. +*.{cmd,[cC][mM][dD]} text eol=crlf +*.{bat,[bB][aA][tT]} text eol=crlf + +# Force bash scripts to always use LF line endings so that if a repo is accessed +# in Unix via a file share from Windows, the scripts will work. +*.sh text eol=lf +.husky/commit-msg eol=lf diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml new file mode 100644 index 0000000..813533c --- /dev/null +++ b/.github/workflows/gh-pages.yml @@ -0,0 +1,64 @@ +name: github pages + +on: + push: + branches: + - master + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + submodules: recursive + + - name: Install doxygen + run: sudo apt-get install -y doxygen + + - name: Install Graphviz + run: sudo apt-get install -y graphviz + + - name: Setup Python + uses: actions/setup-python@v2 + with: + python-version: '3.9' + + - name: Upgrade pip + run: | + # install pip=>20.1 to use "pip cache dir" + python3 -m pip install --upgrade pip + + - name: Get pip cache dir + id: pip-cache + run: echo "::set-output name=dir::$(pip cache dir)" + + - name: Cache dependencies + uses: actions/cache@v2 + with: + path: ${{ steps.pip-cache.outputs.dir }} + key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} + restore-keys: | + ${{ runner.os }}-pip- + + - name: Install dependencies + run: python3 -m pip install -r ./requirements.txt + + - name: Configure CMake + # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. + # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type + run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} + + - name: Build doxygen documentation + # Build your program with the given configuration + run: cmake --build ${{github.workspace}}/build --target dox + + - name: Build sphinx documentation + # Build your program with the given configuration + run: cmake --build ${{github.workspace}}/build --target sphinx + + - name: Deploy + uses: peaceiris/actions-gh-pages@v3 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: ./build/sphinx diff --git a/.github/workflows/linux-build.yml b/.github/workflows/linux-build.yml new file mode 100644 index 0000000..e563ca4 --- /dev/null +++ b/.github/workflows/linux-build.yml @@ -0,0 +1,50 @@ +name: Linux Build (Ubuntu latest) + +on: + push: + branches: + - "**" + pull_request: + branches: + - "develop" + - "master" + +env: + # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) + BUILD_TYPE: Release + +jobs: + build: + # The CMake configure and build commands are platform agnostic and should work equally + # well on Windows or Mac. You can convert this to a matrix build if you need + # cross-platform coverage. + # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix + runs-on: ubuntu-latest + + steps: + - name: Install GCC11 + shell: bash + run: | + sudo apt-get update + sudo apt-get install gcc-11 g++-11 + sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-11 110 --slave /usr/bin/g++ g++ /usr/bin/g++-11 --slave /usr/bin/gcov gcov /usr/bin/gcov-11 + + - name: Checkout + uses: actions/checkout@v2 + with: + submodules: recursive + + - name: Configure CMake + # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. + # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type + run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DASAP_BUILD_TESTS=ON + + - name: Build + # Build your program with the given configuration + run: cmake --build ${{github.workspace}}/build --target build-all-tests --config ${{env.BUILD_TYPE}} + + - name: Test + working-directory: ${{github.workspace}}/build + # Execute tests defined by the CMake configuration. + # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail + run: ctest --output-on-failure -C ${{env.BUILD_TYPE}} diff --git a/.github/workflows/windows-build.yml b/.github/workflows/windows-build.yml new file mode 100644 index 0000000..49a93f7 --- /dev/null +++ b/.github/workflows/windows-build.yml @@ -0,0 +1,47 @@ +name: Windows Build (2022) + +on: + push: + branches: + - "**" + pull_request: + branches: + - "develop" + - "master" + +env: + # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) + BUILD_TYPE: Release + +jobs: + build: + # The CMake configure and build commands are platform-agnostic and should work equally + # well on Windows or Mac. You can convert this to a matrix build if you need + # cross-platform coverage. + # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix + runs-on: windows-2022 + + steps: + - name: Checkout + uses: actions/checkout@v2 + with: + submodules: recursive + + - name: Configure CMake + # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. + # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type + run: > + cmake -G "Visual Studio 17 2022" -A x64 + -B ${{github.workspace}}/build + -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} + -DASAP_BUILD_TESTS=ON + + - name: Build + # Build your program with the given configuration + run: cmake --build ${{github.workspace}}/build --target build-all-tests --config ${{env.BUILD_TYPE}} + + - name: Test + working-directory: ${{github.workspace}}/build + # Execute tests defined by the CMake configuration. + # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail + run: ctest --output-on-failure -C ${{env.BUILD_TYPE}} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c5a05fd --- /dev/null +++ b/.gitignore @@ -0,0 +1,239 @@ +# Build directories +build/ +build-*/ +*-build/ +_build/ +_build-*/ +cmake-build*/ +out/ + +.vs +.sphinx +.cache +.idea + +# In this specific project we ignore .clangd as it is dynamycally generated by cmake during the +# configuration stage. +.clangd + +# Generated files inside source tree +doc/conf.py + +# clang-tidy fixes +fixes*.yaml + +### Linux template +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### macOS template +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk +### Dropbox template +# Dropbox settings and caches +.dropbox +.dropbox.attr +.dropbox.cache +### Ninja template +.ninja_deps +.ninja_log +### Tags template +# Ignore tags created by etags, ctags, gtags (GNU global) and cscope +TAGS +.TAGS +!TAGS/ +tags +.tags +!tags/ +gtags.files +GTAGS +GRTAGS +GPATH +GSYMS +cscope.files +cscope.out +cscope.in.out +cscope.po.out + +### Vim template +# Swap +[._]*.s[a-v][a-z] +[._]*.sw[a-p] +[._]s[a-rt-v][a-z] +[._]ss[a-gi-z] +[._]sw[a-p] + +# Session +Session.vim + +# Temporary +.netrwhist +# Persistent undo +[._]*.un~ +### C++ template +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app +### VisualStudioCode template +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json + +### Xcode template +# Xcode +# +# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore + +## User settings +xcuserdata/ + +## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) +*.xcscmblueprint +*.xccheckout + +## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) +build/ +DerivedData/ +*.moved-aside +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 + +### Backup template +*.bak +*.gho +*.ori +*.orig +*.tmp +### Vagrant template +# General +.vagrant/ + +# Log files (if you are creating logs in debug mode, uncomment this) +*.log +### C template + +# Object files +*.ko +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Executables +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf + +### Windows template +# Windows thumbnail cache files +Thumbs.db +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..421e174 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,9 @@ +[submodule "third_party/gsl"] + path = third_party/gsl + url = https://github.com/microsoft/GSL.git +[submodule "cmake/common"] + path = cmake/common + url = https://github.com/abdes/cmake +[submodule "doxygen/doxygen-awesome-css"] + path = doxygen/doxygen-awesome-css + url = https://github.com/jothepro/doxygen-awesome-css.git diff --git a/.husky/commit-msg b/.husky/commit-msg new file mode 100755 index 0000000..e8511ea --- /dev/null +++ b/.husky/commit-msg @@ -0,0 +1,4 @@ +#!/bin/sh +. "$(dirname "$0")/_/husky.sh" + +npx --no-install commitlint --edit $1 diff --git a/.versionrc.json b/.versionrc.json new file mode 100644 index 0000000..7628a80 --- /dev/null +++ b/.versionrc.json @@ -0,0 +1,46 @@ +{ + "types": [ + { + "type": "feat", + "section": "Features" + }, + { + "type": "fix", + "section": "Bug Fixes" + }, + { + "type": "chore", + "hidden": true + }, + { + "type": "docs", + "hidden": true + }, + { + "type": "style", + "hidden": true + }, + { + "type": "refactor", + "hidden": true + }, + { + "type": "perf", + "hidden": true + }, + { + "type": "test", + "hidden": true + } + ], + "bumpFiles": [ + { + "filename": "CMakeLists.txt", + "updater": "cmake/scripts/standard-version-updater.js" + } + ], + "commitUrlFormat": "http://github.com/abdes/asap/commit/{{hash}}", + "compareUrlFormat": "http://github.com/abdes/asap/compare/{{previousTag}}...{{currentTag}}", + "issueUrlFormat": "http://github.com/abdes/asap/issues/{{id}}", + "userUrlFormat": "http://github.com/abdes" +} \ No newline at end of file diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..4f2489d --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,23 @@ +{ + "recommendations": [ + "swyddfa.esbonio", + "stkb.rewrap", + "donjayamanne.githistory", + "eamodio.gitlens", + "kevinkyang.auto-comment-blocks", + "matepek.vscode-catch2-test-adapter", + "ms-python.python", + "ms-vscode.cmake-tools", + "twxs.cmake", + "hbenl.vscode-test-explorer", + "ms-vscode.test-adapter-converter", + "jeff-hykin.better-cpp-syntax", + "ms-vscode.cpptools", + "llvm-vs-code-extensions.vscode-clangd", + "vadimcn.vscode-lldb", + "guyutongxue.cpp-reference", + "cschlosser.doxdocgen", + "cheshirekow.cmake-format", + "editorconfig.editorconfig" + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..d799df7 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,291 @@ +{ + "esbonio.sphinx.confDir": "doc", + "editor.rulers": [ + 80, + 100 + ], + "rewrap.wrappingColumn": 80, + "rewrap.autoWrap.enabled": true, + "files.associations": { + "xtr1common": "cpp", + "memory": "cpp", + "system_error": "cpp", + "chrono": "cpp", + "iterator": "cpp", + "stdexcept": "cpp", + "algorithm": "cpp", + "any": "cpp", + "array": "cpp", + "atomic": "cpp", + "bit": "cpp", + "cctype": "cpp", + "charconv": "cpp", + "cinttypes": "cpp", + "clocale": "cpp", + "cmath": "cpp", + "compare": "cpp", + "complex": "cpp", + "concepts": "cpp", + "condition_variable": "cpp", + "csignal": "cpp", + "cstdarg": "cpp", + "cstddef": "cpp", + "cstdint": "cpp", + "cstdio": "cpp", + "cstdlib": "cpp", + "cstring": "cpp", + "ctime": "cpp", + "cwchar": "cpp", + "deque": "cpp", + "exception": "cpp", + "format": "cpp", + "forward_list": "cpp", + "fstream": "cpp", + "functional": "cpp", + "future": "cpp", + "initializer_list": "cpp", + "iomanip": "cpp", + "ios": "cpp", + "iosfwd": "cpp", + "iostream": "cpp", + "istream": "cpp", + "limits": "cpp", + "list": "cpp", + "locale": "cpp", + "map": "cpp", + "mutex": "cpp", + "new": "cpp", + "numeric": "cpp", + "optional": "cpp", + "ostream": "cpp", + "random": "cpp", + "ratio": "cpp", + "regex": "cpp", + "set": "cpp", + "sstream": "cpp", + "stack": "cpp", + "stop_token": "cpp", + "streambuf": "cpp", + "string": "cpp", + "thread": "cpp", + "tuple": "cpp", + "type_traits": "cpp", + "typeindex": "cpp", + "typeinfo": "cpp", + "unordered_map": "cpp", + "utility": "cpp", + "variant": "cpp", + "vector": "cpp", + "xfacet": "cpp", + "xhash": "cpp", + "xiosbase": "cpp", + "xlocale": "cpp", + "xlocbuf": "cpp", + "xlocinfo": "cpp", + "xlocmes": "cpp", + "xlocmon": "cpp", + "xlocnum": "cpp", + "xloctime": "cpp", + "xmemory": "cpp", + "xstddef": "cpp", + "xstring": "cpp", + "xtree": "cpp", + "xutility": "cpp" + }, + // Disable automatic detection of file indent mode and indent size, that is, automatically change + // the file to the indent format configured by VSCode after opening the file + "editor.detectIndentation": false, + // Format file when pasting + "editor.formatOnPaste": false, + // Format file on save + "editor.formatOnSave": true, + // Automatically display inline suggestions in the editor + "editor.inlineSuggest.enabled": true, + // Whether to display a small panel with parameter document and type information when entering + "editor.parameterHints.enabled": true, + // Controls whether suggestions are automatically displayed as you type code + "editor.quickSuggestions": { + // Not allowed when typing comments + "comments": false, + // Not allowed when typing string + "strings": false, + // Allow when typing other + "other": true + }, + // Controls the wait time (in milliseconds) before quick suggestions are displayed + "editor.quickSuggestionsDelay": 0, + // Controls how the editor displays symbols on white space characters + "editor.renderWhitespace": "none", + // Code snippets are recommended over other suggestions + "editor.snippetSuggestions": "top", + // Simulate the behavior of tabs when using space indentation to facilitate alignment + "editor.stickyTabStops": true, + // Controls whether words close to the cursor are prioritized when sorting + "editor.suggest.localityBonus": true, + "editor.suggest.shareSuggestSelections": true, + // Controls whether suggestions are automatically displayed after the trigger character is typed + "editor.suggestOnTriggerCharacters": true, + // One tab = 2 spaces + "editor.tabSize": 2, + // Controls whether a list of suggestions is provided based on the text in the document + "editor.wordBasedSuggestions": true, + // There is no need to confirm when deleting files + "explorer.confirmDelete": false, + // No confirmation is required when moving files + "explorer.confirmDragAndDrop": false, + // Rename method when pasting a file with the same name + // smart: intelligently add / increment numbers at the end of duplicate names + "explorer.incrementalNaming": "smart", + // Ignore notification of extension suggestions + "extensions.ignoreRecommendations": true, + // Auto save unsaved editor: after waiting time + "files.autoSave": "afterDelay", + // Wait time for auto save (MS) + "files.autoSaveDelay": 10000, + // Displays the line number of the search results + "search.showLineNumbers": true, + // When the search term is lowercase, the search is case insensitive + // Otherwise, it is case sensitive + "search.smartCase": true, + // The integrated terminal defaults to PowerShell + "terminal.integrated.defaultProfile.windows": "PowerShell", + // Enable visual ringing tone for integrated terminal + "terminal.integrated.enableBell": true, + // Integrated terminal code: zh_CN.UTF-8 + "terminal.integrated.env.windows": { + "LC_ALL": "en_US.UTF-8" + }, + // The integrated terminal uses GPU acceleration + "terminal.integrated.gpuAcceleration": "on", + // When the integration terminal right clicks, select the word below the cursor and open the context menu + "terminal.integrated.rightClickBehavior": "selectWord", + // Window zoom level: 1 (original level is 0) + "window.zoomLevel": 0, + // Do not open the editor at startup without recovering information from the previous session + "workbench.startupEditor": "none", + // Displays the action items for the view header + "workbench.view.alwaysShowHeaderActions": true, + // Clangd operation parameters (enter clangd -- help list hidden on the terminal / command line to view more) + "clangd.arguments": [ + // Let Clangd generate more detailed logs + "--log=verbose", + // The output JSON file is more beautiful + "--pretty", + // Global completion (the pop-up suggestions during input will provide possible symbols in all files configured in CMakeLists.txt, and the header file will be automatically supplemented) + "--all-scopes-completion", + // More detailed completion + "--completion-style=detailed", + // Allow supplementary header files + "--header-insertion=iwyu", + // In input suggestions, items that contain header files are distinguished by dots from items that do not yet contain header files + "--header-insertion-decorators", + // Automatically analyze files in the background (based on complie)_ Commands, which we generate with CMake) + "--background-index", + // Enable clang tidy to provide static checking + "--clang-tidy", + // Default formatting style: Google open source project code Guide + "--fallback-style=Google", + // Number of tasks opened at the same time + "-j=12", + // pch optimized location (memory or disk, selecting memory will increase memory overhead, but will improve performance) + "--pch-storage=memory", + // After adding this item, placeholders will be provided for the parameters when completing the function. After typing, press Tab to switch to the next placeholder or even the end of the function + "--function-arg-placeholders" + ], + // Automatically detect Cland updates + "clangd.checkUpdates": true, + // Cland's snippets have many jump points. Without this, Intellisense must be triggered manually + "editor.suggest.snippetsPreventQuickSuggestions": false, + // When you save the contents of cmake.sourceDirectory or CMakeLists.txt, the CMake project directory is not automatically configured + "cmake.configureOnEdit": false, + // Automatically configure the CMake project directory when it is opened + "cmake.configureOnOpen": true, + "cmake.preferredGenerators": [ + "Ninja" + ], + "cmake.allowCommentsInPresetsFile": true, + // Automatically extract submissions from the default remote library of the current Git repository + "git.autofetch": true, + // Confirm before synchronizing Git repository + "git.confirmSync": false, + // When there are no pending changes, commit all changes directly + "git.enableSmartCommit": true, + // Controls whether brackets are shaded + "editor.bracketPairColorization.enabled": true, + // I got used to the blue brackets in the Bracket Pair Colorizer 2, so I changed the color of the blue brackets in VSCode + "workbench.colorCustomizations": { + "[Default Dark+]": { + "editorBracketHighlight.foreground3": "#9CDCFE" + } + }, + // Semantic highlighting + "editor.semanticHighlighting.enabled": true, + // Semantic highlight customization + "editor.semanticTokenColorCustomizations": { + "enabled": true, + "rules": { + // Static quantity (static variable, static function) + "*.static": { + "fontStyle": "italic" + }, + // macro + "macro": { + "foreground": "#8f5daf" + }, + // Member function + "method": { + "fontStyle": "underline" + }, + // Namespace + "namespace": { + "foreground": "#00d780" + }, + // Function parameters + "parameter": { + "foreground": "#C8ECFF" + }, + // Member variables seem to need more than cland12 + "property": { + "fontStyle": "underline", + "foreground": "#C8ECFF" + }, + // Type parameter + "typeParameter": { + "foreground": "#31A567" + }, + // Read only quantities are equivalent to macros + "variable.readonly": { + "foreground": "#8f5daf" + } + } + }, + "editor.tokenColorCustomizations": { + "textMateRules": [ + { + "scope": "googletest.failed", + "settings": { + "foreground": "#f00" + } + }, + { + "scope": "googletest.passed", + "settings": { + "foreground": "#0f0" + } + }, + { + "scope": "googletest.run", + "settings": { + "foreground": "#0f0" + } + } + ] + }, + "cSpell.enableFiletypes": [ + "rst" + ], + "cmakeFormat.args": [ + "--config-file=${workspaceFolder}/.cmake-format.yaml" + ], + "rewrap.reformat": true, +} diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..0e590e6 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,11 @@ +# Names should be added to this file with this pattern: +# +# For individuals: +# Name +# +# For organizations: +# Organization +# +# See python fnmatch module documentation for more information. + +Abdessattar Sassi <457645+abdes@users.noreply.github.com> diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..621c767 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,168 @@ +# Changelog + +All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. + +## [4.0.1](http://github.com/abdes/asap/compare/v4.0.1...v4.0.0) (2022-02-17) + +* update the GitHub action for windows build to 2022 + +## [4.0.0](http://github.com/abdes/asap/compare/v3.1.2...v4.0.0) (2022-02-17) + +### Features + +* overload pattern for variant visitation on the fly ([53ea7cc](http://github.com/abdes/asap/commit/53ea7cc74e89913cbf59eda2e30bd36c356e6acd)) +* refactor: trim the project to the bare minimum([8d48a56](https://github.com/abdes/asap/commit/8d48a56ef0db2359aa80b5e5b2854802dc6a45f0)) + + The intention from the asap base project is to be a template starter repo that + can be cloned and used for new projects. The bare minimum is the cmake build + infrastructure, documentation, the common submodule and the contract checking + assertions submodule. + + Contract checking library is now in a separate submodule to keep the common + module as minimal as possible. Projects that do not intend to use contract + checking will not have to depend on it. + +### Bug Fixes + +* use separate sphinx cache dir for each module ([03f36fc](http://github.com/abdes/asap/commit/03f36fc96c78c51b441feb9a7b9c70d19a2125c7)) + +## [3.1.2](http://github.com/abdes/asap/compare/v3.1.1...v3.1.2) (2021-12-19) + + +### Features + +* doxygen snippets from `test` or `examples` source directories ([85b8000](http://github.com/abdes/asap/commit/85b8000e91dd105c0f90a62b1824957b68ce9c03)) + + +### Bug Fixes + +* **doc:** wrong chapter title in logging module page ([5966912](http://github.com/abdes/asap/commit/59669122d3ea8565878667e6f09b124bc7da1b99)) +* doxygen doc generation uses wrong module info ([8833474](http://github.com/abdes/asap/commit/8833474402c523a3f2eda10b8927da46989f4bba)) +* need to hardcode `asap` when linking `common` ([e038a70](http://github.com/abdes/asap/commit/e038a702ce8727fa1a6c8b3a45cb4bb211281db5)) + +## [3.1.1](http://github.com/abdes/asap/compare/v3.1.0...v3.1.1) (2021-12-19) + +### Documentation + +* minor tweaks to the sphinx config + +## [3.1.0](http://github.com/abdes/asap/compare/v3.0.0...v3.1.0) (2021-12-18) + + +### ⚠ BREAKING CHANGES + +* The logging `Registry` is now implemented as a singleton class and therefore it needs to be + accessed via its instance() method. All other methods in its interface are not static anymore. +* Prefix the build options with `ASAP_` to make them unique and avoid clashing with other projects + that may use the generic `OPTION_xxx` names. Additionally, the build presets now always activate + building of tests and examples except in release builds where examples are not built. +* The cmake option `OPTION_SELF_CONTAINED` is no longer relevant as we believe that 3rd party + dependencies should be installed using their own projects. In the wrostcase scenario, they should + be explicitly added as install instructions to the project in a visible and documented way. + +### Features + +* add support for .editorconfig ([5a7a689](http://github.com/abdes/asap/commit/5a7a6892f7c05798e79c6f81e9eef3b0a6256ca1)) +* provide a way to distinguish between debug and release builds via preprocessor define ([bbd84a2](http://github.com/abdes/asap/commit/bbd84a2c32c9833f79b442e202b647f93946b105)) + + +### Bug Fixes + +* adjust doc target names to work in renamed projects ([60acc65](http://github.com/abdes/asap/commit/60acc65fcc8d529a69f3617d792376be3e54b6ce)) +* cmake option was placed in the wrong command ([b634b15](http://github.com/abdes/asap/commit/b634b153db975a8bc8715e82f7fafdc214504cfa)) +* clean the logging API implementation ([25535c7](http://github.com/abdes/asap/commit/25535c76c0586d53f7c6c55db250a251f0c5390b)) + + +## [3.0.0](http://github.com/abdes/asap/compare/v2.0.1...v3.0.0) (2021-12-08) + + +### ⚠ BREAKING CHANGES + +* The project requires C++17 as it is widely available in compilers now. Logical traits such as + conjunction, disjunction and negation are available from the standard include. +* major redesign of the cmake build system, many macros and functions have been changed and the + build system overall has been simplified. +* `catch2` has been replaced by Google Test/Mock, which provides more features, less compiler + warnings and is more popular. Catch2 or any other framework can still be easily added to an `asap` + based project. +* `hedely` was removed from `common` and was replaced by a much lighter new file `compilers.h`. +* `nowide` was removed from `common` and will be replaced by the standalone boost nowide library + when needed. +* `filesystem` footprint is too large to be included by default in `asap` starter project. It will + be provided separately and a mechanism to easily add it into an `asap` based project will be + implemented in a future update. + +### Features + +* add gsl library + ([9b982f1](http://github.com/abdes/asap/commit/9b982f17aeb9362619cf871ed9bbedb65344ab43)) +* add support for CMake presets + ([bdcfa4d](http://github.com/abdes/asap/commit/bdcfa4d6a0cfffe0026af39b635c6dcd4e61e3a8)) +* add contract checking api (assertions) + ([c691446](http://github.com/abdes/asap/commit/c691446fd18242840e62529f1aabdaf8480b7ec6)) +* convert git submodule to local module + ([4e32e8a](http://github.com/abdes/asap/commit/4e32e8a6d1152413af12c7013ee759eca5e2d51d)) +* redesign cmake build system + ([31ce14a](http://github.com/abdes/asap/commit/31ce14a301ac2e725b7760581e696f089225b161)) +* remove filesystem submodule + ([e2089ae](http://github.com/abdes/asap/commit/e2089ae7707c1448c78db23db2f53cbfab461599)) +* remove logical traits backport + ([5ed0fe6](http://github.com/abdes/asap/commit/5ed0fe6e9e03399e640221a285f87a1a8a015cd1)) + +* significantly enhance the documentation, both doxygen based for APIs and sphinx based for the + project docs + +### Bug Fixes + +* configure compilation database for clangd when using cmake presets + ([7493c24](http://github.com/abdes/asap/commit/7493c240ce97396040d9a7bd09cb32ed56ea2340)) +* hardcode `asap` in places where we should not use the custom project name + ([7f81298](http://github.com/abdes/asap/commit/7f81298b31e5a235bf4bce4778525a2e4594befa)) +* match Clang and Apple Clang for compiler options + ([7e1e123](http://github.com/abdes/asap/commit/7e1e12317b62099bb58ce5dd4adfdbf1a759ad03)) +* convert unit testing to gtest/gmock + ([b99c8d0](http://github.com/abdes/asap/commit/b99c8d0a315bb8200ca9cec93c9d5f64880c9271)) + +## [2.0.1](http://github.com/abdes/asap/compare/v2.0.0...v2.0.1) (2021-11-06) + + +### Bug Fixes + +* "-Wreserved-identifier" only if not APPLE ([05fac12](http://github.com/abdes/asap/commit/05fac12d37978c6651c299e6ad1cc64dfed88fa0)) +* cmake require c++ 14 in all modules ([f05c9a6](http://github.com/abdes/asap/commit/f05c9a63e97dbcbef3e7d8188a04e719e2247d62)) +* use lower-case name for sphinx ([f3005b5](http://github.com/abdes/asap/commit/f3005b5bd4055b16a57cdcb3af70a2f32b1c5809)) + +## [2.0.0](http://github.com/abdes/asap/compare/v1.0.0...v2.0.0) (2021-11-06) + +### Features + +* Cleanup the code to remove most compiler and linter warnings. +* Refactor cmake build files to have robust support for sanitizers and linters. +* Rationalize the compiler options to strictly stick to those options that won't + conflict with application decisions. +* Enhanced the documentation + +### To be continued + +* Refactoring of cmake build files. +* Remove additional warnings from code. +* Documenattion. + +### ⚠ BREAKING CHANGES + +* logging is in a new module: asap::logging. + +Many modules would want to only get the basic common functionality +without pulling extra 3rd party dependencies such as spdlog, fmt etc... +For this reason, the logging functionality is taken out of the common +submodule and moved to logging submodule. + +* move logging functionality to separate module ([46f4dd9](http://github.com/abdes/asap/commit/46f4dd96edb1148e0772a1539b989fd80f3821e4)) + +## 1.0.0 (2021-11-03) + + +### Features + +* add support for Conventional Commits and auto Changelog ([7b827fa](http://github.com/abdes/asap/commit/7b827fab2ae28ba903c69ab5acdf249cb3e55d85)) +* replace cmake compiler detection with hedley ([8bf0a3d](http://github.com/abdes/asap/commit/8bf0a3d53fcdba65497491cbd63c1dcffcba8467)) diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..2afe4b9 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,363 @@ +# ===-----------------------------------------------------------------------===# +# Distributed under the 3-Clause BSD License. See accompanying file LICENSE or +# copy at https://opensource.org/licenses/BSD-3-Clause). +# SPDX-License-Identifier: BSD-3-Clause +# ===-----------------------------------------------------------------------===# + +# ------------------------------------------------------------------------------ +# CMake basic options +# ------------------------------------------------------------------------------ + +# It's time to move on! We require 3.14 for modern CMake with nice quality of +# life features and simpler scripts. +cmake_minimum_required(VERSION 3.14) + +# List of directories specifying a search path for CMake modules to be loaded by +# the the include() or find_package() commands before checking the default +# modules that come with CMake. +list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake) + +# ------------------------------------------------------------------------------ +# Project description and (meta) information +# ------------------------------------------------------------------------------ + +# Get git revision +find_package(Git REQUIRED) +include(GetGitRevisionDescription) +get_git_head_revision(GIT_REFSPEC GIT_SHA1) +string(SUBSTRING "${GIT_SHA1}" 0 12 GIT_REV) +if(NOT GIT_SHA1) + set(GIT_REV "0") +endif() + +# Meta information about the project +# cmake-format: off +set(META_PROJECT_NAME "asap") +set(META_PROJECT_DESCRIPTION "Instantly start with a fully loaded CMake project") +set(META_AUTHOR_ORGANIZATION "The Authors") +set(META_GITHUB_REPO "https://github.com/abdes/asap") +set(META_AUTHOR_DOMAIN "https://github.com/abdes/asap") +set(META_AUTHOR_MAINTAINER "Abdessattar Sassi") +set(META_VERSION_MAJOR "4") +set(META_VERSION_MINOR "0") +set(META_VERSION_PATCH "1") +set(META_VERSION_REVISION "${GIT_REV}") +set(META_VERSION "${META_VERSION_MAJOR}.${META_VERSION_MINOR}.${META_VERSION_PATCH}") +set(META_NAME_VERSION "${META_PROJECT_NAME} v${META_VERSION} (${META_VERSION_REVISION})") +set(META_CMAKE_INIT_SHA "${GIT_REV}") +# cmake-format: on + +string(MAKE_C_IDENTIFIER ${META_PROJECT_NAME} META_PROJECT_ID) +string(TOUPPER ${META_PROJECT_ID} META_PROJECT_ID) + +message("=> Project : ${META_NAME_VERSION}") + +# ------------------------------------------------------------------------------ +# Project configuration options +# ------------------------------------------------------------------------------ + +# Project options +# cmake-format: off +option(BUILD_SHARED_LIBS "Build shared instead of static libraries." ON) +option(ASAP_BUILD_TESTS "Build tests." OFF) +option(ASAP_BUILD_EXAMPLES "Build examples." OFF) +option(ASAP_WITH_GOOGLE_ASAN "Instrument code with address sanitizer" OFF) +option(ASAP_WITH_GOOGLE_UBSAN "Instrument code with undefined behavior sanitizer" OFF) +option(ASAP_WITH_GOOGLE_TSAN "Instrument code with thread sanitizer" OFF) +option(ASAP_WITH_VALGRIND "Builds targets with valgrind profilers added" OFF) +# cmake-format: on + +# ------------------------------------------------------------------------------ +# Project Declaration +# ------------------------------------------------------------------------------ + +# Generate folders for IDE targets (e.g., VisualStudio solutions) +set_property(GLOBAL PROPERTY USE_FOLDERS ON) +set(IDE_FOLDER "${META_PROJECT_NAME}") + +# Declare project +project( + ${META_PROJECT_NAME} + VERSION ${META_VERSION} + LANGUAGES C CXX) + +# Set output directories +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib) + +# Create version file +file(WRITE "${PROJECT_BINARY_DIR}/VERSION" "${META_NAME_VERSION}") + +# ------------------------------------------------------------------------------ +# Additional CMake modules +# ------------------------------------------------------------------------------ + +# Register general cmake commands +include(AsapTargets) +include(BuildHelpers) +include(GenerateExportHeader) +include(CompileOptions) +include(GoogleSanitizers) +include(CodeCoverage) +include(Valgrind) +include(ClangFormat) +include(ClangTidy) + +# The default build type provided by CMake is to include no compiler flags for +# optimization. For some projects you may want to set a default build type so +# that you do not have to remember to set it. +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + message( + STATUS "Setting build type to 'RelWithDebInfo' as none was specified.") + set(CMAKE_BUILD_TYPE + RelWithDebInfo + CACHE STRING "Choose the type of build." FORCE) + # Set the possible values of build type for cmake-gui + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" + "MinSizeRel" "RelWithDebInfo") +endif() + +# If no contract mode is specified, we set the contract mode based on the build +# type. +if(NOT OPTION_CONTRACT_MODE) + if(CMAKE_BUILD_TYPE STREQUAL "Debug") + set(OPTION_CONTRACT_MODE + "AUDIT" + CACHE STRING "Contract mode for assertions (common module).") + elseif(CMAKE_BUILD_TYPE STREQUAL RelWithDebInfo) + set(OPTION_CONTRACT_MODE + "DEFAULT" + CACHE STRING "Contract mode for assertions (common module).") + else() + set(OPTION_CONTRACT_MODE + "OFF" + CACHE STRING "Contract mode for assertions (common module).") + endif() +endif() +message( + "-- Building for ${CMAKE_BUILD_TYPE} with OPTION_CONTRACT_MODE : ${OPTION_CONTRACT_MODE}" +) + +# ------------------------------------------------------------------------------ +# Deployment/installation setup +# ------------------------------------------------------------------------------ + +# Get project name +set(project ${META_PROJECT_NAME}) + +# Check for system dir install +set(SYSTEM_DIR_INSTALL FALSE) +if("${CMAKE_INSTALL_PREFIX}" STREQUAL "/usr" OR "${CMAKE_INSTALL_PREFIX}" + STREQUAL "/usr/local") + set(SYSTEM_DIR_INSTALL TRUE) + set(ASAP_INSTALL_PREFIX_FULL_PATH ${CMAKE_INSTALL_PREFIX}) +else() + set(ASAP_INSTALL_PREFIX_FULL_PATH ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_PREFIX}) +endif() + +# Installation paths +include(GNUInstallDirs) +# cmake-format: off +if(UNIX AND SYSTEM_DIR_INSTALL) + # Install into the system (/usr/bin or /usr/local/bin) + set(ASAP_INSTALL_ROOT "${CMAKE_INSTALL_DATAROOTDIR}/${project}") # /usr/[local]/share/ + set(ASAP_INSTALL_LIB "${CMAKE_INSTALL_LIBDIR}") # /usr/[local]/lib + set(ASAP_INSTALL_SHARED "${ASAP_INSTALL_LIB}") # /usr/[local]/lib + set(ASAP_INSTALL_CMAKE "${CMAKE_INSTALL_DATAROOTDIR}/cmake/${project}") # /usr/[local]/share/cmake/ + set(ASAP_INSTALL_PKGCONFIG "${CMAKE_INSTALL_DATAROOTDIR}/pkgconfig") # /usr/[local]/share/pkgconfig + set(ASAP_INSTALL_EXAMPLES "${ASAP_INSTALL_ROOT}") # /usr/[local]/share/ + set(ASAP_INSTALL_DATA "${ASAP_INSTALL_ROOT}") # /usr/[local]/share//data + set(ASAP_INSTALL_BIN "${CMAKE_INSTALL_BINDIR}") # /usr/[local]/bin + set(ASAP_INSTALL_INCLUDE "${CMAKE_INSTALL_INCLUDEDIR}") # /usr/[local]/include + set(ASAP_INSTALL_DOC "${CMAKE_INSTALL_DOCDIR}") # /usr/[local]/share/doc/ + set(ASAP_INSTALL_SHORTCUTS "${CMAKE_INSTALL_DATAROOTDIR}/applications") # /usr/[local]/share/applications + set(ASAP_INSTALL_ICONS "${CMAKE_INSTALL_DATAROOTDIR}/pixmaps") # /usr/[local]/share/pixmaps + set(ASAP_INSTALL_INIT "/etc/init") # /etc/init (upstart init scripts) +else() + # Install into local directory + set(ASAP_INSTALL_ROOT ".") # ./ + set(ASAP_INSTALL_LIB "lib") # ./lib + set(ASAP_INSTALL_SHARED "${ASAP_INSTALL_LIB}") # ./lib + set(ASAP_INSTALL_CMAKE "${ASAP_INSTALL_ROOT}/share/cmake/${project}") # ./share/cmake/ + set(ASAP_INSTALL_PKGCONFIG "${ASAP_INSTALL_ROOT}/share/pkgconfig") # ./share/pkgconfig + set(ASAP_INSTALL_EXAMPLES "${ASAP_INSTALL_ROOT}") # ./ + set(ASAP_INSTALL_DATA "${ASAP_INSTALL_ROOT}") # ./data + set(ASAP_INSTALL_BIN "bin") # ./bin + set(ASAP_INSTALL_INCLUDE "include") # ./include + set(ASAP_INSTALL_DOC "doc") # ./doc + set(ASAP_INSTALL_SHORTCUTS "misc") # ./misc + set(ASAP_INSTALL_ICONS "misc") # ./misc + set(ASAP_INSTALL_INIT "misc") # ./misc +endif() +# cmake-format: on + +# Set runtime path +set(CMAKE_SKIP_BUILD_RPATH FALSE) # Add absolute path to all dependencies for + # BUILD +set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE) +set(CMAKE_INSTALL_RPATH_USE_LINK_PATH FALSE) + +if(NOT SYSTEM_DIR_INSTALL) + # Find libraries relative to binary + if(APPLE) + set(CMAKE_INSTALL_RPATH "@loader_path/../../../${ASAP_INSTALL_LIB}") + else() + set(CMAKE_INSTALL_RPATH "$ORIGIN/${ASAP_INSTALL_LIB}") + endif() +endif() + +# ------------------------------------------------------------------------------ +# Third party modules +# ------------------------------------------------------------------------------ + +add_subdirectory(third_party) + +# ------------------------------------------------------------------------------ +# Clean compiler settings and options +# ------------------------------------------------------------------------------ + +if("${CMAKE_CXX_COMPILER_ID}" MATCHES "MSVC") + # remove default warning level from CMAKE_CXX_FLAGS_INIT CMake adds compiler + # warning levels by default and for MSVC, it uses /W3 which we want to + # override with /W4. The override does make MSVC complain though, so we just + # strip any argument already added by cmake before we set ours. + string(REGEX REPLACE "/W[0-4]" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") +endif() + +# ------------------------------------------------------------------------------ +# Top level code generation +# ------------------------------------------------------------------------------ + +# Generate version-header +configure_file(templates/version.h.in + ${CMAKE_CURRENT_BINARY_DIR}/include/asap/asap-version.h) + +# Generate a clangd configuration file that points to the compilation database +# in the cmake build directory. We need to do this as the build directory is +# different for every preset and can be different as well when the user decides +# to build somewhere else. Currently we cannot configure this properly in vscode +# settings. See https://github.com/clangd/vscode-clangd/issues/48 +configure_file(.clangd.in ${CMAKE_SOURCE_DIR}/.clangd @ONLY) + +# ------------------------------------------------------------------------------ +# Test targets +# ------------------------------------------------------------------------------ + +# enable CTest. This will set BUILD_TESTING to ON unless otherwise specified on +# the command line +include(CTest) +enable_testing() +asap_add_code_coverage_all_targets(EXCLUDE */test/* *googlemock* *googletest* + /usr/*) + +include(FetchContent) +# We make sure that we have 'third_party' in the name so that the targets get +# excluded from the generated target lists for the various tools. +set(FETCHCONTENT_BASE_DIR ${CMAKE_BINARY_DIR}/third_party_deps) +FetchContent_Declare( + googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG # GoogleTest now follows the Abseil Live at Head philosophy. We + # recommend using the latest commit in the main branch in your + # projects. + origin/main) +# For Windows: Prevent overriding the parent project's compiler/linker settings +set(gtest_force_shared_crt + ON + CACHE BOOL "" FORCE) +FetchContent_MakeAvailable(googletest) +include(GoogleTest) + +# ============================================================================== +# Documentation - doxygen, sphinx/breathe/exhale +# ============================================================================== + +# Doxygen +set(DOXYGEN_BUILD_DIR "${CMAKE_BINARY_DIR}/dox") +include(DoxGeneration) + +# Sphinx/breathe/exhale +set(SPHINX_BUILD_DIR "${CMAKE_BINARY_DIR}/sphinx") +include(SphinxGeneration) + +# Setup sphinx doc master target and add other submodules as dependencies +if(SPHINX_FOUND) + asap_with_sphinx("${META_PROJECT_NAME}") + + # Add a target for copying the index.html from the doc dir to the sphinx build + # dir. A dependency on this target will be added to the master sphinx target. + add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/sphinx/index.html + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/doc/index.html + ${CMAKE_CURRENT_BINARY_DIR}/sphinx/index.html + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/doc/index.html) + add_custom_target(copy_doc_index ALL + DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/sphinx/index.html) + + add_dependencies( + ${META_PROJECT_NAME}_sphinx + copy_doc_index + # Hardcode `asap` in the module name as we do not want this prefix to change + # with the forked project name. + asap_common_sphinx + # Add more submodule documentation targets after this, using variables in + # the target names consistently with the module's CMakeLists.txt. + ) + +endif() + +# ============================================================================== +# Project modules +# ============================================================================== + +add_subdirectory(common) +add_subdirectory(contract) + +# ============================================================================== +# Code analyzers: clang-tidy, cppcheck, valgrind, sanitizers, etc... +# ============================================================================== + +asap_setup_clang_format() +asap_create_clang_tidy_targets() + +# ============================================================================== +# Deployment (global project files) +# ============================================================================== +set(runtime "${META_PROJECT_NAME}_runtime") +set(dev "${META_PROJECT_NAME}_dev") +set(meta "${META_PROJECT_NAME}_meta") +set(data "${META_PROJECT_NAME}_date") + +# Install version file +install( + FILES "${PROJECT_BINARY_DIR}/VERSION" + DESTINATION ${ASAP_INSTALL_ROOT} + COMPONENT ${runtime}) + +# Deploy generated top level headers (always in `asap` directory even when +# forked) +install( + DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/include/asap + DESTINATION ${ASAP_INSTALL_INCLUDE} + COMPONENT ${dev}) + +# Install the project meta files +install( + FILES AUTHORS + DESTINATION ${ASAP_INSTALL_ROOT} + COMPONENT meta) +install( + FILES LICENSE + DESTINATION ${ASAP_INSTALL_ROOT} + COMPONENT meta) +install( + FILES README.md + DESTINATION ${ASAP_INSTALL_ROOT} + COMPONENT meta) + +# Install data +install( + DIRECTORY ${PROJECT_SOURCE_DIR}/data + DESTINATION ${ASAP_INSTALL_DATA} + COMPONENT ${data}) diff --git a/CMakePresets.json b/CMakePresets.json new file mode 100644 index 0000000..3063eb3 --- /dev/null +++ b/CMakePresets.json @@ -0,0 +1,473 @@ +{ + "version": 3, + "cmakeMinimumRequired": { + "major": 3, + "minor": 20, + "patch": 0 + }, + "configurePresets": [ + { + "name": "base", + "description": "Base preset", + "hidden": true, + "generator": "Ninja", + "binaryDir": "${sourceDir}/out/build/${presetName}", + "installDir": "${sourceDir}/out/install/${presetName}", + "environment": { + "CMAKE_EXPORT_COMPILE_COMMANDS": "ON", + "caexcludepath": "${sourceDir}/third_party;${sourceDir}/out" + }, + "cacheVariables": { + "ASAP_BUILD_TESTS": "ON", + "ASAP_BUILD_EXAMPLES": "ON" + } + }, + { + "name": "debug", + "description": "Debug build", + "hidden": true, + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug" + } + }, + { + "name": "release", + "description": "Release build", + "hidden": true, + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release", + "ASAP_BUILD_EXAMPLES": "OFF" + } + }, + { + "name": "x64", + "description": "64bit build (on windows)", + "hidden": true, + "architecture": { + "value": "x64", + "strategy": "external" + }, + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Windows" + } + }, + { + "name": "code-coverage", + "description": "Add code coverage to the build", + "hidden": true, + "cacheVariables": { + "CODE_COVERAGE": "ON" + }, + "condition": { + "type": "inList", + "string": "${hostSystemName}", + "list": [ + "Linux", + "Darwin" + ] + } + }, + { + "name": "compiler-clang", + "hidden": true, + "description": "Use clang as the C/C++ compiler", + "cacheVariables": { + "CMAKE_C_COMPILER": "/usr/bin/clang", + "CMAKE_CXX_COMPILER": "/usr/bin/clang++" + }, + "condition": { + "type": "inList", + "string": "${hostSystemName}", + "list": [ + "Linux", + "Darwin" + ] + } + }, + { + "name": "compiler-gcc", + "hidden": true, + "description": "Use GCC as the C/C++ compiler", + "cacheVariables": { + "CMAKE_C_COMPILER": "/usr/bin/gcc", + "CMAKE_CXX_COMPILER": "/usr/bin/g++" + }, + "condition": { + "type": "inList", + "string": "${hostSystemName}", + "list": [ + "Linux", + "Darwin" + ] + } + }, + { + "name": "dev-windows", + "description": "Default build in a dev environment", + "inherits": [ + "base", + "debug", + "x64" + ] + }, + { + "name": "dev-linux", + "description": "Default build in a dev environment", + "inherits": [ + "base", + "debug", + "compiler-clang", + "code-coverage" + ], + "vendor": { + "jetbrains.com/clion": { + "toolchain": "LinuxWSL" + } + } + }, + { + "name": "dev-mac", + "description": "Default build in a dev environment", + "inherits": [ + "base", + "debug", + "compiler-clang", + "code-coverage" + ], + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Darwin" + } + }, + { + "name": "dev-clang", + "description": "Basic build in a dev environment using clang", + "inherits": [ + "base", + "debug", + "compiler-clang", + "code-coverage" + ], + "vendor": { + "jetbrains.com/clion": { + "toolchain": "LinuxWSL" + } + } + }, + { + "name": "dev-gcc", + "description": "Basic build in a dev environment using clang", + "inherits": [ + "base", + "debug", + "compiler-gcc", + "code-coverage" + ], + "vendor": { + "jetbrains.com/clion": { + "toolchain": "LinuxWSL" + } + } + }, + { + "name": "dev-valgrind", + "description": "Builds targets with valgrind profiles added", + "inherits": [ + "base", + "debug", + "compiler-clang" + ], + "cacheVariables": { + "CODE_COVERAGE": "OFF", + "ASAP_WITH_VALGRIND": "ON" + }, + "condition": { + "type": "inList", + "string": "${hostSystemName}", + "list": [ + "Linux", + "Darwin" + ] + }, + "vendor": { + "jetbrains.com/clion": { + "toolchain": "LinuxWSL" + } + } + }, + { + "name": "dev-sanitizers", + "description": "Builds targets with Google ASAN and UBSAN sanitizers added", + "inherits": [ + "base", + "debug", + "compiler-clang" + ], + "cacheVariables": { + "CODE_COVERAGE": "OFF", + "ASAP_WITH_GOOGLE_ASAN": "ON", + "ASAP_WITH_GOOGLE_UBSAN": "ON" + }, + "condition": { + "type": "inList", + "string": "${hostSystemName}", + "list": [ + "Linux", + "Darwin" + ] + }, + "vendor": { + "jetbrains.com/clion": { + "toolchain": "LinuxWSL" + } + } + }, + { + "name": "dev-sanitizers-thread", + "description": "Builds targets with Google thread sanitizer added", + "inherits": [ + "base", + "debug", + "compiler-clang" + ], + "cacheVariables": { + "ASAP_WITH_GOOGLE_TSAN": "ON" + }, + "condition": { + "type": "inList", + "string": "${hostSystemName}", + "list": [ + "Linux", + "Darwin" + ] + }, + "vendor": { + "jetbrains.com/clion": { + "toolchain": "LinuxWSL" + } + } + }, + { + "name": "rel-windows", + "description": "Default build in a dev environment", + "inherits": [ + "base", + "release", + "x64" + ] + }, + { + "name": "rel-linux", + "description": "Default build in a dev environment", + "inherits": [ + "base", + "release", + "compiler-clang" + ], + "vendor": { + "jetbrains.com/clion": { + "toolchain": "LinuxWSL" + } + } + }, + { + "name": "rel-mac", + "description": "Default build in a dev environment", + "inherits": [ + "base", + "release", + "compiler-clang" + ], + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Darwin" + } + } + ], + "buildPresets": [ + { + "name": "dev-base", + "hidden": true, + "jobs": 4, + "targets": [ + "all", + "ccov-all", + "ccov-clean", + "build-all-tests", + "dox", + "sphinx" + ], + "verbose": false + }, + { + "name": "dev-windows", + "configurePreset": "dev-windows", + "inherits": [ + "dev-base" + ], + "targets": [ + "all", + "build-all-tests", + "dox", + "sphinx" + ] + }, + { + "name": "dev-clang", + "configurePreset": "dev-clang", + "inherits": [ + "dev-base" + ], + "targets": [ + "all", + "ccov-all", + "ccov-clean", + "build-all-tests", + "dox", + "sphinx", + "clang-tidy-all", + "clang-tidy-diff", + "clang-format-all", + "clang-format-diff" + ] + }, + { + "name": "dev-linux", + "configurePreset": "dev-linux", + "inherits": [ + "dev-clang" + ] + }, + { + "name": "dev-mac", + "configurePreset": "dev-mac", + "inherits": [ + "dev-clang" + ] + }, + { + "name": "dev-gcc", + "configurePreset": "dev-gcc", + "inherits": [ + "dev-base" + ] + }, + { + "name": "dev-valgrind", + "configurePreset": "dev-valgrind", + "inherits": [ + "dev-base" + ], + "targets": [ + "do-all-valgrind" + ] + }, + { + "name": "dev-sanitizers", + "configurePreset": "dev-sanitizers", + "inherits": [ + "dev-base" + ], + "targets": [ + "do-all-tests" + ] + }, + { + "name": "dev-sanitizers-thread", + "configurePreset": "dev-sanitizers-thread", + "inherits": [ + "dev-base" + ], + "targets": [ + "do-all-tests" + ] + }, + { + "name": "rel-base", + "hidden": true, + "jobs": 4, + "targets": [ + "build-all-tests", + "dox", + "sphinx" + ], + "cleanFirst": true, + "verbose": true + }, + { + "name": "rel-windows", + "configurePreset": "rel-windows", + "inherits": [ + "rel-base" + ] + }, + { + "name": "rel-linux", + "configurePreset": "rel-linux", + "inherits": [ + "rel-base" + ] + }, + { + "name": "rel-mac", + "configurePreset": "rel-mac", + "inherits": [ + "rel-base" + ] + } + ], + "testPresets": [ + { + "name": "test-base", + "description": "Enable output on failure", + "hidden": true, + "output": { + "outputOnFailure": true + } + }, + { + "name": "dev-test-windows", + "inherits": "test-base", + "configurePreset": "dev-windows" + }, + { + "name": "rel-test-windows", + "inherits": "test-base", + "configurePreset": "rel-windows" + }, + { + "name": "dev-test-linux", + "inherits": "test-base", + "configurePreset": "dev-linux" + }, + { + "name": "rel-test-linux", + "inherits": "test-base", + "configurePreset": "rel-linux" + }, + { + "name": "dev-test-mac", + "inherits": "test-base", + "configurePreset": "dev-mac" + }, + { + "name": "rel-test-mac", + "inherits": "test-base", + "configurePreset": "rel-mac" + }, + { + "name": "dev-test-clang", + "inherits": "test-base", + "configurePreset": "dev-clang" + }, + { + "name": "dev-test-gcc", + "inherits": "test-base", + "configurePreset": "dev-gcc" + } + ] +} diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..db67eea --- /dev/null +++ b/LICENSE @@ -0,0 +1,29 @@ +BSD 3-Clause License + +Copyright (c) 2018, The Authors. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..f09637d --- /dev/null +++ b/README.md @@ -0,0 +1,69 @@ +# Starter project for C++ with cmake + +![Start Now!!](doc/_static/logo.png "ASAP Logo") + +[![Linux Build (Ubuntu latest)](https://github.com/abdes/asap/actions/workflows/linux-build.yml/badge.svg?branch=develop)](https://github.com/abdes/asap/actions/workflows/linux-build.yml) +[![Windows Build (latest)](https://github.com/abdes/asap/actions/workflows/windows-build.yml/badge.svg?branch=develop)](https://github.com/abdes/asap/actions/workflows/windows-build.yml) + +## [Project Documentation](https://abdes.github.io/asap/) + +## Overview + +- `CMake` as the build system with or without presets +- cross-platform portability on Linux, OS X and Windows +- multiple compilers: clang, g++ and MSVC +- modular structure with each module self-contained in a subdirectory within the project +- `CMake` build helpers to facilitate declaration of library, exe, test modules, for the + end-to-end lifecycle including doc generation, test, packaging etc... +- common facilities (common module) for platform specifics, assertions support, logging +- unit testing with Google Test +- code coverage with clang or g++ +- zero-touch valgrind, clang-tidy, clang-format, google sanitizers, etc. +- development can be done locally or in a dev container with vscode. + +## Getting the code + +```bash +git clone --recurse-submodules -j4 https://github.com/abdes/asap.git +``` + +NOTES: + +- -j4 requests git to parallelize cloning of repos. Needs a relatively recent version of git. If + that is not available, simply do not use this option. + +## Requirements + +Make sure you have a C++ compiler with C++-17 capabilities at least. Gnu, Clang and MSVC all can do +that with a recent version. + +## Enabling husky/commitlint/standard-version + +Only one time after the project is cloned, do the following: + +```bash +npx husky install +npm install -g @commitlint/cli @commitlint/config-conventional +npm install -g standard-version +``` + +## Building + +```bash +mkdir _build && cd _build && cmake .. && cmake --build . +``` + +or just use one of the predefined `CMake` presets. Detailed instructions are in the project +documentation, and many useful commands are listed [here](https://abdes.github.io/asap/master/html/01-getting-started/useful-commands.html). + + +```cmake +# Project options +option(BUILD_SHARED_LIBS "Build shared instead of static libraries." ON) +option(ASAP_BUILD_TESTS "Build tests." OFF) +option(ASAP_BUILD_EXAMPLES "Build examples." OFF) +option(ASAP_WITH_GOOGLE_ASAN "Instrument code with address sanitizer" OFF) +option(ASAP_WITH_GOOGLE_UBSAN "Instrument code with undefined behavior sanitizer" OFF) +option(ASAP_WITH_GOOGLE_TSAN "Instrument code with thread sanitizer" OFF) +option(ASAP_WITH_VALGRIND "Builds targets with valgrind profilers added" OFF) +``` diff --git a/cmake/AsapTargets.cmake b/cmake/AsapTargets.cmake new file mode 100644 index 0000000..0fabc6f --- /dev/null +++ b/cmake/AsapTargets.cmake @@ -0,0 +1,158 @@ +# ===-----------------------------------------------------------------------===# +# Distributed under the 3-Clause BSD License. See accompanying file LICENSE or +# copy at https://opensource.org/licenses/BSD-3-Clause). +# SPDX-License-Identifier: BSD-3-Clause +# ===-----------------------------------------------------------------------===# + +include(CMakePackageConfigHelpers) +include(common/SwiftTargets) +include(CompileDefinitions) + +# ------------------------------------------------------------------------------ +# Meta information about the this module +# ------------------------------------------------------------------------------ + +macro(asap_declare_module) + set(options) + set(oneValueArgs + MODULE_NAME + DESCRIPTION + GITHUB_REPO + AUTHOR_MAINTAINER + VERSION_MAJOR + VERSION_MINOR + VERSION_PATCH) + set(multiValueArgs) + + cmake_parse_arguments(x "${options}" "${oneValueArgs}" "${multiValueArgs}" + ${ARGN}) + + if(NOT DEFINED x_MODULE_NAME) + message(FATAL_ERROR "Module name is required.") + return() + endif() + if(NOT + (DEFINED x_VERSION_MAJOR + AND DEFINED x_VERSION_MINOR + AND DEFINED x_VERSION_PATCH)) + message( + FATAL_ERROR + "PLease specify all of VERSION_MAJOR VERSION_MINOR VERSION_PATCH for the module." + ) + return() + endif() + + # cmake-format: off + set(META_MODULE_NAME "${x_MODULE_NAME}") + set(META_MODULE_DESCRIPTION "${x_DESCRIPTION}") + set(META_MODULE_GITHUB_REPO "${x_GITHUB_REPO}") + set(META_MODULE_AUTHOR_MAINTAINER "${x_AUTHOR_MAINTAINER}") + set(META_MODULE_VERSION_MAJOR "${x_VERSION_MAJOR}") + set(META_MODULE_VERSION_MINOR "${x_VERSION_MINOR}") + set(META_MODULE_VERSION_PATCH "${x_VERSION_PATCH}") + set(META_MODULE_VERSION "${META_MODULE_VERSION_MAJOR}.${META_MODULE_VERSION_MINOR}.${META_MODULE_VERSION_PATCH}") + set(META_MODULE_NAME_VERSION "${META_MODULE_PROJECT_NAME} v${META_MODULE_VERSION}") + # cmake-format: on + message( + "=> [module: ${META_PROJECT_NAME}/${META_MODULE_NAME} ${META_MODULE_VERSION}]" + ) + +endmacro() + +# ------------------------------------------------------------------------------ +# CMake/pkgconfig config files +# ------------------------------------------------------------------------------ + +function(_module_cmake_config_files) + # generate the config file that includes the exports + configure_package_config_file( + ${CMAKE_CURRENT_SOURCE_DIR}/config.cmake.in + "${CMAKE_CURRENT_BINARY_DIR}/${MODULE_TARGET_NAME}Config.cmake" + INSTALL_DESTINATION "${ASAP_INSTALL_CMAKE}/${META_MODULE_NAME}" + PATH_VARS META_MODULE_VERSION MODULE_TARGET_NAME) + + # generate the version file for the config file + write_basic_package_version_file( + "${CMAKE_CURRENT_BINARY_DIR}/${MODULE_TARGET_NAME}ConfigVersion.cmake" + VERSION "${META_MODULE_VERSION}" + COMPATIBILITY AnyNewerVersion) +endfunction() + +function(_module_pkgconfig_files) + set(MODULE_PKGCONFIG_FILE ${MODULE_TARGET_NAME}.pc) + get_target_property(TARGET_DEBUG_POSTFIX ${MODULE_TARGET_NAME} DEBUG_POSTFIX) + set(MODULE_LINK_LIBS "-l${MODULE_TARGET_NAME}${TARGET_DEBUG_POSTFIX}") + configure_file(config.pc.in + ${CMAKE_CURRENT_BINARY_DIR}/${MODULE_PKGCONFIG_FILE} @ONLY) + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${MODULE_PKGCONFIG_FILE} + DESTINATION ${ASAP_INSTALL_PKGCONFIG}) +endfunction() + +function(asap_create_module_config_files) + _module_cmake_config_files() + _module_pkgconfig_files() +endfunction() + +# ------------------------------------------------------------------------------ +# Target creation helpers +# ------------------------------------------------------------------------------ + +function(asap_add_library target) + swift_add_library("${target}" ${ARGN}) + + # We can refer to this target either with its standalone target name or with a + # project scoped name (::) which we will alias to the target + # name. + add_library("${META_PROJECT_NAME}::${META_MODULE_NAME}" ALIAS ${target}) + get_target_property(type ${target} TYPE) + if (NOT ${type} STREQUAL "INTERFACE_LIBRARY") + # Set some common private compiler defines + asap_set_compile_definitions(${target}) + # Generate export headers for the library + asap_generate_export_headers(${target} ${META_MODULE_NAME}) + endif() + + set_target_properties( + ${target} + PROPERTIES FOLDER "Libraries" + VERSION ${META_MODULE_VERSION} + SOVERSION ${META_MODULE_VERSION_MAJOR} + DEBUG_POSTFIX "d" + CXX_VISIBILITY_PRESET hidden + VISIBILITY_INLINES_HIDDEN YES + ) +endfunction() + +function(asap_add_executable target) + swift_add_executable("${target}" ${ARGN}) + asap_set_compile_definitions(${target}) + set_target_properties(${target} PROPERTIES FOLDER "Executables") +endfunction() + +function(asap_add_tool target) + swift_add_tool("${target}" ${ARGN}) + asap_set_compile_definitions(${target}) + set_target_properties(${target} PROPERTIES FOLDER "Tools") +endfunction() + +function(asap_add_tool_library target) + swift_add_tool_library("${target}" ${ARGN}) + asap_set_compile_definitions(${target}) + set_target_properties( + ${target} + PROPERTIES FOLDER "Tool Libraries" + VERSION ${META_MODULE_VERSION} + SOVERSION ${META_MODULE_VERSION_MAJOR} + DEBUG_POSTFIX "d") +endfunction() + +function(asap_add_test_library target) + swift_add_test_library("${target}" ${ARGN}) + asap_set_compile_definitions(${target}) + set_target_properties( + ${target} + PROPERTIES FOLDER "Test Libraries" + VERSION ${META_MODULE_VERSION} + SOVERSION ${META_MODULE_VERSION_MAJOR} + DEBUG_POSTFIX "d") +endfunction() diff --git a/cmake/BuildHelpers.cmake b/cmake/BuildHelpers.cmake new file mode 100644 index 0000000..7f87e5a --- /dev/null +++ b/cmake/BuildHelpers.cmake @@ -0,0 +1,49 @@ +# ===-----------------------------------------------------------------------===# +# Distributed under the 3-Clause BSD License. See accompanying file LICENSE or +# copy at https://opensource.org/licenses/BSD-3-Clause). +# SPDX-License-Identifier: BSD-3-Clause +# ===-----------------------------------------------------------------------===# + +# ------------------------------------------------------------------------------ +# Build Helpers to simplify target creation. +# ------------------------------------------------------------------------------ + +function(asap_compile_definitions target) + # + # Compile definitions + # + # ones we use for every single target + target_compile_definitions( + ${target} + PRIVATE $<$: + ASAP_CONTRACT_DEFAULT + > + $<$: + ASAP_CONTRACT_OFF + > + $<$: + ASAP_CONTRACT_AUDIT + > + $<$: + NOMINMAX + WIN32_LEAN_AND_MEAN=1 + _WIN32_WINNT=0x0600 + >) +endfunction() + +include(GenerateTemplateExportHeader) +function(asap_generate_export_headers target include_dir) + # Set API export file and macro + string(MAKE_C_IDENTIFIER ${target} TEMPLATE_TARGET_ID) + string(TOUPPER ${TEMPLATE_TARGET_ID} TEMPLATE_TARGET_ID) + set(export_file "include/${include_dir}/${target}_export.h") + set(template_export_file "include/${include_dir}/${target}_api.h") + set(TEMPLATE_TARGET "${target}") + set(TEMPLATE_INCLUDE_DIR "${include_dir}") + + # Create API export headers + generate_export_header(${target} EXPORT_FILE_NAME ${export_file} + EXPORT_MACRO_NAME ${TEMPLATE_TARGET_ID}_API) + generate_template_export_header(${target} ${TEMPLATE_TARGET_ID} + ${template_export_file}) +endfunction() diff --git a/cmake/ClangFormat.cmake b/cmake/ClangFormat.cmake new file mode 100644 index 0000000..1bffb95 --- /dev/null +++ b/cmake/ClangFormat.cmake @@ -0,0 +1,27 @@ +# ===-----------------------------------------------------------------------===# +# Distributed under the 3-Clause BSD License. See accompanying file LICENSE or +# copy at https://opensource.org/licenses/BSD-3-Clause). +# SPDX-License-Identifier: BSD-3-Clause +# ===-----------------------------------------------------------------------===# + +# +# Clang-format targets work only when clang-format executable can be found. +# +find_program(CLANG_FORMAT NAMES clang-format clang-format-14) +if("${CLANG_FORMAT}" STREQUAL "CLANG_FORMAT-NOTFOUND") + + message(STATUS "Could not find appropriate clang-format, targets disabled") + + function(asap_setup_clang_format) + # empty + endfunction() + +else() + + include(common/ClangFormat) + + function(asap_setup_clang_format) + swift_setup_clang_format(${ARGV}) + endfunction() + +endif() diff --git a/cmake/ClangTidy.cmake b/cmake/ClangTidy.cmake new file mode 100644 index 0000000..6b861df --- /dev/null +++ b/cmake/ClangTidy.cmake @@ -0,0 +1,32 @@ +# ===-----------------------------------------------------------------------===# +# Distributed under the 3-Clause BSD License. See accompanying file LICENSE or +# copy at https://opensource.org/licenses/BSD-3-Clause). +# SPDX-License-Identifier: BSD-3-Clause +# ===-----------------------------------------------------------------------===# + +# +# clang-tidy targets work only when clang-tidy executable can be found and the +# compiler being used is clang (otherwise gcc/others options may cause errors +# for clang-tidy) +# +find_program(CLANG_TIDY NAMES clang-tidy clang-tidy-14) +if(NOT "${CLANG_TIDY}" STREQUAL "CLANG_TIDY-NOTFOUND") + if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + include(common/ClangTidy) + + function(asap_create_clang_tidy_targets) + swift_create_clang_tidy_targets(DONT_GENERATE_CLANG_TIDY_CONFIG ${ARGV}) + endfunction() + return() + + else() + message( + STATUS "Not using clang as a compiler, disabling clang-tidy targets") + endif() +else() + message(STATUS "Could not find appropriate clang-tidy, targets disabled") +endif() + +function(asap_create_clang_tidy_targets) + # empty +endfunction() diff --git a/cmake/CodeCoverage.cmake b/cmake/CodeCoverage.cmake new file mode 100644 index 0000000..97af84d --- /dev/null +++ b/cmake/CodeCoverage.cmake @@ -0,0 +1,35 @@ +# ===-----------------------------------------------------------------------===# +# Distributed under the 3-Clause BSD License. See accompanying file LICENSE or +# copy at https://opensource.org/licenses/BSD-3-Clause). +# SPDX-License-Identifier: BSD-3-Clause +# ===-----------------------------------------------------------------------===# + +# +# Code coverage only works with clang/gcc. +# +if("${CMAKE_C_COMPILER_ID}" MATCHES "(Apple)?[Cc]lang|GNU") + + include(common/CodeCoverage) + + function(asap_add_code_coverage) + add_code_coverage(${ARGV}) + endfunction() + + function(asap_add_code_coverage_all_targets) + add_code_coverage_all_targets(${ARGV}) + endfunction() + +else() + function(target_code_coverage) + # empty + endfunction() + + function(asap_add_code_coverage) + # empty + endfunction() + + function(asap_add_code_coverage_all_targets) + # empty + endfunction() + +endif() diff --git a/cmake/CompileDefinitions.cmake b/cmake/CompileDefinitions.cmake new file mode 100644 index 0000000..1b1e3bd --- /dev/null +++ b/cmake/CompileDefinitions.cmake @@ -0,0 +1,55 @@ +# ===-----------------------------------------------------------------------===# +# Distributed under the 3-Clause BSD License. See accompanying file LICENSE or +# copy at https://opensource.org/licenses/BSD-3-Clause). +# SPDX-License-Identifier: BSD-3-Clause +# ===-----------------------------------------------------------------------===# + +include_guard(GLOBAL) + +# ------------------------------------------------------------------------------ +# Set a common set of compile definitions +# ------------------------------------------------------------------------------ + +function(asap_set_compile_definitions target) + set(argOption) + set(argSingle "") + set(argMulti "ADD" "REMOVE") + + unset(x_WARNING) + unset(x_ADD) + unset(x_REMOVE) + + cmake_parse_arguments(x "${argOption}" "${argSingle}" "${argMulti}" ${ARGN}) + + set(all_flags) + + list(APPEND all_flags "ASAP_CONTRACT_${OPTION_CONTRACT_MODE}") + + # Provide a way to distinguish between debug and release builds via + # preprocessor define + list(APPEND all_flags "$<$:ASAP_IS_DEBUG_BUILD>") + + if(MSVC) + list(APPEND all_flags "NOMINMAX" "WIN32_LEAN_AND_MEAN=1" + "_WIN32_WINNT=0x0600") + # Disabling warning for not using "secure-but-not-standard" STL algos + list(APPEND all_flags "_CRT_SECURE_NO_WARNINGS" "_SCL_SECURE_NO_WARNINGS") + endif() + + if(x_REMOVE) + foreach(flag ${x_REMOVE}) + list(FIND all_flags ${flag} found) + if(found EQUAL -1) + message( + FATAL_ERROR + "Compiler flag '${flag}' specified for removal is not part of the set of common + compiler flags") + endif() + endforeach() + list(REMOVE_ITEM all_flags ${x_REMOVE}) + endif() + + list(APPEND all_flags ${x_ADD}) + target_compile_definitions(${target} PRIVATE ${all_flags}) + +endfunction() diff --git a/cmake/CompileOptions.cmake b/cmake/CompileOptions.cmake new file mode 100644 index 0000000..1ac8321 --- /dev/null +++ b/cmake/CompileOptions.cmake @@ -0,0 +1,234 @@ +# ===-----------------------------------------------------------------------===# +# Distributed under the 3-Clause BSD License. See accompanying file LICENSE or +# copy at https://opensource.org/licenses/BSD-3-Clause). +# SPDX-License-Identifier: BSD-3-Clause +# ===-----------------------------------------------------------------------===# + +# ------------------------------------------------------------------------------ +# Set a common set of compiler options and warning flags +# ------------------------------------------------------------------------------ + +# +# Call swift_set_compile_options() for any target to set the Swift default set +# of compiler options. This includes +# ~~~ +# - exceptions disabled +# - rtti disabled +# - strict aliasing disabled +# - Warnings as errors (-Werror) +# - Extensive set of enabled warnings +# ~~~ +# +# Exceptions and/or RTTI can be selectively enabled for a target be passing +# EXCEPTIONS and/or RTTI as a parameter, eg +# +# ~~~ +# swift_set_compile_options(sample-target EXCEPTIONS RTTI) +# ~~~ +# +# will enable exceptions and rtti for sample-target only +# +# Warning flags can be removed from the default set by passing REMOVE followed +# by a list of warning flags, eg +# +# ~~~ +# swift_set_compile_options(sample-target REMOVE -Wconversion) +# ~~~ +# +# will prevent -Wconversion from being passed to the compiler for sample-target +# only +# +# Similarly extra options can be given by passing ADD followed by a list of +# warning flags (or other compiler options), eg +# +# ~~~ +# swift_set_compile_options(sample-target ADD -Wformat=2) +# ~~~ +# +# will pass -Wformat=2 to the compiler for sample-target only +# +# By default -Werror is set, but this can be prevented by passing WARNING as a +# parameter, eg +# +# ~~~ +# swift_set_compile_options(sample-target WARNING) +# ~~~ +# +# will disable warnings-as-errors for sample-target only +# +# All flags will be checked for suitability with the in-use compilers before +# being selected. This is important since Swift code tends to be compiled with a +# wide variety of compilers which may not support the same set of flags and +# options. Therefore, it should be preferred to use this function to set +# compiler flags and options rather than target_compile_options() +# +# NOTE: user's can call on EXTRA_FLAGS to augment the default list of flags +# before flags are removed with REMOVE and subsequently added with ADD. +# + +include(CheckCCompilerFlag) +include(CheckCXXCompilerFlag) + +function(swift_set_compile_options) + set(argOption "WARNING" "NO_EXCEPTIONS" "EXCEPTIONS" "NO_RTTI" "RTTI") + set(argSingle "") + set(argMulti "ADD" "REMOVE" "EXTRA_FLAGS") + + unset(x_WARNING) + unset(x_ADD) + unset(x_REMOVE) + unset(x_EXTRA_FLAGS) + + cmake_parse_arguments(x "${argOption}" "${argSingle}" "${argMulti}" ${ARGN}) + set(targets ${x_UNPARSED_ARGUMENTS}) + + if(x_RTTI AND x_NO_RTTI) + message(FATAL_ERROR "RTTI and NO_RTTI can't be used together") + endif() + + if(x_EXCEPTIONS AND x_NO_EXCEPTIONS) + message(FATAL_ERROR "EXCEPTIONS and NO_EXCEPTIONS can't be used together") + endif() + + foreach(flag ${x_ADD} ${x_REMOVE}) + if(${flag} STREQUAL "-Werror") + message( + FATAL_ERROR + "Do not specify -Werror directly, use WARNING to disable -Werror") + endif() + if(${flag} STREQUAL "-Wno-error") + message( + FATAL_ERROR + "Do not specify -Wno-error directly, use WARNING to disable -Werror") + endif() + endforeach() + + if(DEFINED SWIFT_COMPILER_WARNING_ARE_ERROR) + if(SWIFT_COMPILER_WARNING_ARE_ERROR) + if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + set(all_flags /WX) + else() + set(all_flags -Werror -Wno-error=deprecated-declarations) + endif() + else() + set(all_flags -Wno-error) + endif() + else() + if(NOT x_WARNING) + if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + set(all_flags /WX) + else() + set(all_flags -Werror -Wno-error=deprecated-declarations) + endif() + endif() + endif() + + if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + # using Clang + list( + APPEND + all_flags + -Weverything + -Wno-c++98-compat + -Wno-c++98-compat-pedantic + -Wno-c++98-c++11-compat-pedantic + -Wno-padded + -Wno-documentation-unknown-command + -Wno-switch-enum + -Wno-unused-macros + -Wno-disabled-macro-expansion) + elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + # using GCC + list( + APPEND + all_flags + -Wall + -Wextra + -Wcast-align + -Wcast-qual + -Wctor-dtor-privacy + -Wdisabled-optimization + -Wformat=2 + -Winit-self + -Wmissing-declarations + -Wmissing-include-dirs + -Wold-style-cast + -Woverloaded-virtual + -Wredundant-decls + -Wshadow + -Wsign-conversion + -Wsign-promo + -Wundef + -Werror + -Wno-unused) + if(NOT DEFINED CMAKE_CXX_CLANG_TIDY) + list(APPEND all_flags -Wlogical-op -Wstrict-null-sentinel) + endif() + elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + # using Visual Studio C++ + list(APPEND all_flags /EHsc /MP /W4) + endif() + + if(x_REMOVE) + foreach(flag ${x_REMOVE}) + list(FIND all_flags ${flag} found) + if(found EQUAL -1) + message( + FATAL_ERROR + "Compiler flag '${flag}' specified for removal is not part of the set of common compiler flags" + ) + endif() + endforeach() + list(REMOVE_ITEM all_flags ${x_REMOVE}) + endif() + + list(APPEND all_flags ${x_ADD}) + + unset(final_flags) + + get_property(enabled_languages GLOBAL PROPERTY ENABLED_LANGUAGES) + list(FIND enabled_languages "C" c_enabled) + list(FIND enabled_languages "CXX" cxx_enabled) + + foreach(flag ${all_flags}) + string(TOUPPER ${flag} sanitised_flag) + string(REPLACE "+" "X" sanitised_flag ${sanitised_flag}) + string(REGEX REPLACE "[^A-Za-z_0-9]" "_" sanitised_flag ${sanitised_flag}) + + set(c_supported HAVE_C_FLAG_${sanitised_flag}) + string(REGEX REPLACE "_+" "_" c_supported ${c_supported}) + set(cxx_supported HAVE_CXX_FLAG_${sanitised_flag}) + string(REGEX REPLACE "_+" "_" cxx_supported ${cxx_supported}) + + if(${c_enabled} GREATER -1) + if(MSVC) + check_c_compiler_flag("/WX ${flag}" ${c_supported}) + else() + check_c_compiler_flag("-Werror ${flag}" ${c_supported}) + endif() + if(${${c_supported}}) + list(APPEND final_flags $<$:${flag}>) + endif() + endif() + + if(${cxx_enabled} GREATER -1) + if(MSVC) + check_cxx_compiler_flag("/WX ${flag}" ${cxx_supported}) + else() + check_cxx_compiler_flag("-Werror ${flag}" ${cxx_supported}) + endif() + if(${${cxx_supported}}) + list(APPEND final_flags $<$:${flag}>) + endif() + endif() + endforeach() + + foreach(target ${targets}) + target_compile_options(${target} PRIVATE ${final_flags}) + endforeach() + +endfunction() + +function(asap_set_compile_options) + swift_set_compile_options(${ARGV}) +endfunction() diff --git a/cmake/ComponentInstall.cmake b/cmake/ComponentInstall.cmake new file mode 100644 index 0000000..6a70495 --- /dev/null +++ b/cmake/ComponentInstall.cmake @@ -0,0 +1,11 @@ +# ===-----------------------------------------------------------------------===# +# Distributed under the 3-Clause BSD License. See accompanying file LICENSE or +# copy at https://opensource.org/licenses/BSD-3-Clause). +# SPDX-License-Identifier: BSD-3-Clause +# ===-----------------------------------------------------------------------===# + +# Execute cmake_install.cmake wrapper that allows to pass both DESTDIR and +# COMPONENT environment variable + +execute_process(COMMAND ${CMAKE_COMMAND} -DCOMPONENT=$ENV{COMPONENT} -P + cmake_install.cmake) diff --git a/cmake/DoxGeneration.cmake b/cmake/DoxGeneration.cmake new file mode 100644 index 0000000..3bd15a1 --- /dev/null +++ b/cmake/DoxGeneration.cmake @@ -0,0 +1,158 @@ +# ===-----------------------------------------------------------------------===# +# Distributed under the 3-Clause BSD License. See accompanying file LICENSE or +# copy at https://opensource.org/licenses/BSD-3-Clause). +# SPDX-License-Identifier: BSD-3-Clause +# ===-----------------------------------------------------------------------===# + +# ------------------------------------------------------------------------------ +# API Documentation +# ------------------------------------------------------------------------------ + +# To avoid indiscriminately generating documentation for all modules in the +# project, including third party modules and stuff for which we don't want +# documentation to be generated, we provide here the basic tools to add doxygen +# capabilities to a module. +# +# To use in a specific CmakeLists.txt add the following: +# +# ~~~ +# asap_with_doxygen( +# ${target} +# "\"\"" +# "\"<Desscription>\"" +# "<List of directories to include>") +# ~~~ +# +# Use 'make dox' to generate documentation. (not done by default) +# + +include(FindDoxygen) + +if(DOXYGEN_FOUND) + message(STATUS "Doxygen package was found.") + + function(_configure_doxyfile MODULE_NAME VERSION TITLE BRIEF INPUT_PATH) + if(EXISTS "${CMAKE_SOURCE_DIR}/doxygen/Doxyfile.in") + set(DOXY_OUTPUT_DIR "${MODULE_NAME}") + set(DOXY_MODULE_DIR "${CMAKE_CURRENT_SOURCE_DIR}") + set(DOXY_MODULE_NAME "${MODULE_NAME}") + set(DOXY_MODULE_VERSION "${VERSION}") + set(DOXY_TITLE "${TITLE}") + set(DOXY_BRIEF "${BRIEF}") + set(DOXY_INPUT_PATH "${INPUT_PATH}") + + set(DOXY_EXAMPLE_PATH) + if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/test") + list(APPEND DOXY_EXAMPLE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/test") + endif() + if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/examples") + list(APPEND DOXY_EXAMPLE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/examples") + endif() + string(REPLACE ";" ", " DOXY_EXAMPLE_PATH "${DOXY_EXAMPLE_PATH}") + + if(NOT EXISTS "${DOXYGEN_BUILD_DIR}/${DOXY_OUTPUT_DIR}") + file(MAKE_DIRECTORY "${DOXYGEN_BUILD_DIR}/${DOXY_OUTPUT_DIR}") + endif() + configure_file("${CMAKE_SOURCE_DIR}/doxygen/Doxyfile.in" + "${DOXYGEN_BUILD_DIR}/${MODULE_NAME}_Doxyfile" @ONLY) + else() + message( + STATUS + "WARNING: The '${CMAKE_SOURCE_DIR}/doxygen/Doxyfile.in' file does not exist!" + ) + endif() + endfunction() + + function(_add_doxygen_target MODULE_NAME) + add_custom_target( + ${MODULE_NAME}_dox + COMMAND ${CMAKE_COMMAND} -E remove -f "${MODULE_NAME}_Doxyfile.out" + COMMAND ${CMAKE_COMMAND} -E copy "${MODULE_NAME}_Doxyfile" + "${MODULE_NAME}_Doxyfile.out" + COMMAND ${DOXYGEN_EXECUTABLE} "${MODULE_NAME}_Doxyfile.out" + COMMAND ${CMAKE_COMMAND} -E remove -f "${MODULE_NAME}_Doxyfile.out" + WORKING_DIRECTORY "${DOXYGEN_BUILD_DIR}" + COMMENT "Generating doxygen documentation for \"${MODULE_NAME}\"" + VERBATIM) + set_target_properties(${MODULE_NAME}_dox PROPERTIES EXCLUDE_FROM_ALL TRUE) + add_dependencies(dox ${MODULE_NAME}_dox) + endfunction() + + function(asap_with_doxygen) + set(options) + set(oneValueArgs MODULE_NAME VERSION TITLE BRIEF INPUT_PATH) + set(multiValueArgs) + + cmake_parse_arguments(x "${options}" "${oneValueArgs}" "${multiValueArgs}" + ${ARGN}) + + if(NOT DEFINED x_MODULE_NAME) + message(FATAL_ERROR "Module name is required.") + return() + endif() + if(NOT DEFINED x_VERSION) + message(FATAL_ERROR "Version string (M.m.p) is required.") + return() + endif() + if(NOT DEFINED x_TITLE) + set(x_TITLE "Module `${x_MODULE_NAME}`") + endif() + if(NOT DEFINED x_BRIEF) + set(x_BRIEF "Documentation for module `${x_MODULE_NAME}`") + endif() + if(NOT DEFINED x_INPUT_PATH) + message(FATAL_ERROR "Input path (list of directories) is required.") + return() + endif() + + # Substitute variables in the doxygen config file for the target + _configure_doxyfile(${x_MODULE_NAME} ${x_VERSION} ${x_TITLE} ${x_BRIEF} + ${x_INPUT_PATH}) + # Add the target as a dependency to the master dox target + _add_doxygen_target(${x_MODULE_NAME}) + endfunction() + + # We'll make a special script to collect all doxygen warnings from submodules + # and print them at the end of the doxygen run. This mwill make it easier to + # detect if there were doxygen warnings in the project and eventually fail the + # build in a CI environment for example. + + set(COLLECT_WARNINGS_SCRIPT "${DOXYGEN_BUILD_DIR}/collect_warnings.cmake") + # cmake-format: off + file(WRITE "${COLLECT_WARNINGS_SCRIPT}" " \ + # Collect warnings from submodules into the consolidated warnings file\n \ + file(WRITE \"${DOXYGEN_BUILD_DIR}/${MODULE_NAME}/doxygen_warnings.txt\" \"\")\n \ + file(GLOB_RECURSE DOX_MODULES \"module_warnings.txt\")\n \ + foreach(MODULE \${DOX_MODULES})\n \ + message(\" collecting doxygen warnings from \${MODULE}\")\n \ + file(READ \"\${MODULE}\" MODULE_WARNINGS)\n \ + file(APPEND \"${DOXYGEN_BUILD_DIR}/doxygen_warnings.txt\" \"\${MODULE_WARNINGS}\")\n \ + endforeach(MODULE)\n \ + file(READ \"${DOXYGEN_BUILD_DIR}/doxygen_warnings.txt\" ALL_WARNINGS)\n \ + string(COMPARE NOTEQUAL \"\${ALL_WARNINGS}\" \"\" DOXYGEN_HAD_WARNINGS)\n \ + if(DOXYGEN_HAD_WARNINGS)\n \ + # Print the warnings\n \ + message(\"\${ALL_WARNINGS}\")\n \ + endif(DOXYGEN_HAD_WARNINGS)\n") + # cmake-format: on + + # The master doxygen target + add_custom_target(dox) + # We don't want it to be rebuilt everytime we build all. Need to explicitly + # request it to be built. + set_target_properties(dox PROPERTIES EXCLUDE_FROM_ALL TRUE) + # Custom command to collect warnings and print them + add_custom_command( + TARGET dox + POST_BUILD + COMMAND ${CMAKE_COMMAND} -P "${COLLECT_WARNINGS_SCRIPT}" + WORKING_DIRECTORY "${DOXYGEN_BUILD_DIR}" + COMMENT "Running post-build command for dox") + +else() + message(STATUS "WARNING: Doxygen package is not available on this system!") + + function(asap_with_doxygen) + + endfunction() +endif() diff --git a/cmake/FindSphinx.cmake b/cmake/FindSphinx.cmake new file mode 100644 index 0000000..ad1ebe8 --- /dev/null +++ b/cmake/FindSphinx.cmake @@ -0,0 +1,22 @@ +# ===-----------------------------------------------------------------------===# +# Distributed under the 3-Clause BSD License. See accompanying file LICENSE or +# copy at https://opensource.org/licenses/BSD-3-Clause). +# SPDX-License-Identifier: BSD-3-Clause +# ===-----------------------------------------------------------------------===# + +# * This module looks for Sphinx Find the Sphinx documentation generator +# +# This modules defines SPHINX_EXECUTABLE SPHINX_FOUND + +find_program( + SPHINX_EXECUTABLE + NAMES sphinx-build + HINTS $ENV{SPHINX_DIR} + PATH_SUFFIXES bin + DOC "Sphinx documentation generator") + +include(FindPackageHandleStandardArgs) + +find_package_handle_standard_args(Sphinx DEFAULT_MSG SPHINX_EXECUTABLE) + +mark_as_advanced(SPHINX_EXECUTABLE) diff --git a/cmake/FindValgrind.cmake b/cmake/FindValgrind.cmake new file mode 100644 index 0000000..f3af27c --- /dev/null +++ b/cmake/FindValgrind.cmake @@ -0,0 +1,7 @@ +# ===-----------------------------------------------------------------------===# +# Distributed under the 3-Clause BSD License. See accompanying file LICENSE or +# copy at https://opensource.org/licenses/BSD-3-Clause). +# SPDX-License-Identifier: BSD-3-Clause +# ===-----------------------------------------------------------------------===# + +include(common/FindValgrind) diff --git a/cmake/GenerateTemplateExportHeader.cmake b/cmake/GenerateTemplateExportHeader.cmake new file mode 100644 index 0000000..ad244f6 --- /dev/null +++ b/cmake/GenerateTemplateExportHeader.cmake @@ -0,0 +1,25 @@ +# ===-----------------------------------------------------------------------===# +# Distributed under the 3-Clause BSD License. See accompanying file LICENSE or +# copy at https://opensource.org/licenses/BSD-3-Clause). +# SPDX-License-Identifier: BSD-3-Clause +# ===-----------------------------------------------------------------------===# + +# ------------------------------------------------------------------------------ +# Generate export header for template classes/functions. +# ------------------------------------------------------------------------------ + +# Creates an export header similar to generate_export_header, but for templates. +# The main difference is that for MSVC, templates must not get exported. When +# the file ${export_file} is included in source code, the macro +# ${target_id}_TEMPLATE_API may get used to define public visibility for +# templates on GCC and Clang platforms. +# +function(generate_template_export_header target target_id export_file) + if("${CMAKE_CXX_COMPILER_ID}" MATCHES "MSVC") + configure_file(${PROJECT_SOURCE_DIR}/templates/template_msvc_api.h.in + ${CMAKE_CURRENT_BINARY_DIR}/${export_file}) + else() + configure_file(${PROJECT_SOURCE_DIR}/templates/template_api.h.in + ${CMAKE_CURRENT_BINARY_DIR}/${export_file}) + endif() +endfunction() diff --git a/cmake/GetGitRevisionDescription.cmake b/cmake/GetGitRevisionDescription.cmake new file mode 100644 index 0000000..58914c8 --- /dev/null +++ b/cmake/GetGitRevisionDescription.cmake @@ -0,0 +1,128 @@ +# * Returns a version string from Git +# +# These functions force a re-configure on each git commit so that you can trust +# the values of the variables in your build system. +# +# get_git_head_revision(<refspecvar> <hashvar> [<additional arguments to git +# describe> ...]) +# +# Returns the refspec and sha hash of the current head revision +# +# git_describe(<var> [<additional arguments to git describe> ...]) +# +# Returns the results of git describe on the source tree, and adjusting the +# output so that it tests false if an error occurs. +# +# git_get_exact_tag(<var> [<additional arguments to git describe> ...]) +# +# Returns the results of git describe --exact-match on the source tree, and +# adjusting the output so that it tests false if there was no exact matching +# tag. +# +# Requires CMake 2.6 or newer (uses the 'function' command) +# +# Original Author: 2009-2010 Ryan Pavlik <rpavlik@iastate.edu> +# <abiryan@ryand.net> http://academic.cleardefinition.com Iowa State University +# HCI Graduate Program/VRAC +# +# Copyright Iowa State University 2009-2010. Distributed under the Boost +# Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy +# at http://www.boost.org/LICENSE_1_0.txt) + +if(__get_git_revision_description) + return() +endif() +set(__get_git_revision_description YES) + +# We must run the following at "include" time, not at function call time, to +# find the path to this module rather than the path to a calling list file +get_filename_component(_gitdescmoddir ${CMAKE_CURRENT_LIST_FILE} PATH) + +function(get_git_head_revision _refspecvar _hashvar) + set(GIT_PARENT_DIR "${CMAKE_CURRENT_SOURCE_DIR}") + set(GIT_DIR "${GIT_PARENT_DIR}/.git") + while(NOT EXISTS "${GIT_DIR}") # .git dir not found, search parent directories + set(GIT_PREVIOUS_PARENT "${GIT_PARENT_DIR}") + get_filename_component(GIT_PARENT_DIR ${GIT_PARENT_DIR} PATH) + if(GIT_PARENT_DIR STREQUAL GIT_PREVIOUS_PARENT) + # We have reached the root directory, we are not in git + set(${_refspecvar} + "GITDIR-NOTFOUND" + PARENT_SCOPE) + set(${_hashvar} + "GITDIR-NOTFOUND" + PARENT_SCOPE) + return() + endif() + set(GIT_DIR "${GIT_PARENT_DIR}/.git") + endwhile() + # check if this is a submodule + if(NOT IS_DIRECTORY ${GIT_DIR}) + file(READ ${GIT_DIR} submodule) + string(REGEX REPLACE "gitdir: (.*)\n$" "\\1" GIT_DIR_RELATIVE ${submodule}) + get_filename_component(SUBMODULE_DIR ${GIT_DIR} PATH) + get_filename_component(GIT_DIR ${SUBMODULE_DIR}/${GIT_DIR_RELATIVE} + ABSOLUTE) + endif() + set(GIT_DATA "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/git-data") + if(NOT EXISTS "${GIT_DATA}") + file(MAKE_DIRECTORY "${GIT_DATA}") + endif() + + if(NOT EXISTS "${GIT_DIR}/HEAD") + return() + endif() + set(HEAD_FILE "${GIT_DATA}/HEAD") + configure_file("${GIT_DIR}/HEAD" "${HEAD_FILE}" COPYONLY) + + configure_file("${_gitdescmoddir}/GetGitRevisionDescription.cmake.in" + "${GIT_DATA}/grabRef.cmake" @ONLY) + include("${GIT_DATA}/grabRef.cmake") + + set(${_refspecvar} + "${HEAD_REF}" + PARENT_SCOPE) + set(${_hashvar} + "${HEAD_HASH}" + PARENT_SCOPE) +endfunction() + +function(git_describe _var) + if(NOT GIT_FOUND) + find_package(Git QUIET) + endif() + get_git_head_revision(refspec hash) + if(NOT GIT_FOUND) + set(${_var} + "GIT-NOTFOUND" + PARENT_SCOPE) + return() + endif() + if(NOT hash) + set(${_var} + "HEAD-HASH-NOTFOUND" + PARENT_SCOPE) + return() + endif() + + execute_process( + COMMAND "${GIT_EXECUTABLE}" describe ${hash} ${ARGN} + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + RESULT_VARIABLE res + OUTPUT_VARIABLE out + ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) + if(NOT res EQUAL 0) + set(out "${out}-${res}-NOTFOUND") + endif() + + set(${_var} + "${out}" + PARENT_SCOPE) +endfunction() + +function(git_get_exact_tag _var) + git_describe(out --exact-match ${ARGN}) + set(${_var} + "${out}" + PARENT_SCOPE) +endfunction() diff --git a/cmake/GetGitRevisionDescription.cmake.in b/cmake/GetGitRevisionDescription.cmake.in new file mode 100644 index 0000000..e15aa60 --- /dev/null +++ b/cmake/GetGitRevisionDescription.cmake.in @@ -0,0 +1,46 @@ +# +# Internal file for GetGitRevisionDescription.cmake +# +# Requires CMake 2.6 or newer (uses the 'function' command) +# +# Original Author: +# 2009-2010 Ryan Pavlik <rpavlik@iastate.edu> <abiryan@ryand.net> +# http://academic.cleardefinition.com +# Iowa State University HCI Graduate Program/VRAC +# +# Copyright Iowa State University 2009-2010. +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) + +set(HEAD_HASH) +set(HEAD_REF) + +if (NOT EXISTS "@HEAD_FILE@") + return() +endif() + +file(READ "@HEAD_FILE@" HEAD_CONTENTS LIMIT 1024) + +string(STRIP "${HEAD_CONTENTS}" HEAD_CONTENTS) +if(HEAD_CONTENTS MATCHES "ref") + # named branch + string(REPLACE "ref: " "" HEAD_REF "${HEAD_CONTENTS}") + if(EXISTS "@GIT_DIR@/${HEAD_REF}") + configure_file("@GIT_DIR@/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY) + elseif(EXISTS "@GIT_DIR@/packed-refs") + configure_file("@GIT_DIR@/packed-refs" "@GIT_DATA@/packed-refs" COPYONLY) + file(READ "@GIT_DATA@/packed-refs" PACKED_REFS) + if(${PACKED_REFS} MATCHES "([0-9a-z]*) ${HEAD_REF}") + set(HEAD_HASH "${CMAKE_MATCH_1}") + endif() + endif() +else() + # detached HEAD + configure_file("@GIT_DIR@/HEAD" "@GIT_DATA@/head-ref" COPYONLY) +endif() + +if(NOT HEAD_HASH AND EXISTS "@GIT_DATA@/head-ref") + file(READ "@GIT_DATA@/head-ref" HEAD_HASH LIMIT 1024) + string(STRIP "${HEAD_HASH}" HEAD_HASH) +endif() diff --git a/cmake/GoogleSanitizers.cmake b/cmake/GoogleSanitizers.cmake new file mode 100644 index 0000000..f1af465 --- /dev/null +++ b/cmake/GoogleSanitizers.cmake @@ -0,0 +1,194 @@ +# ===-----------------------------------------------------------------------===# +# Distributed under the 3-Clause BSD License. See accompanying file LICENSE or +# copy at https://opensource.org/licenses/BSD-3-Clause). +# SPDX-License-Identifier: BSD-3-Clause +# ===-----------------------------------------------------------------------===# + +# ------------------------------------------------------------------------------ +# Google Sanitizers https://github.com/google/sanitizers +# +# This module simplifies the use of Google Sanitizers (Address, Undefined +# Behavior and Thread) in the build. The Memory sanitizer is not supported +# because it requires all code, including third party and system libraries to be +# built with it. +# +# A target can be instrumented with any of the sanitizers by calling the +# corresponding function with the target name: +# ~~~ +# address sanitizer : asap_add_google_asan(target) +# undefined behavior sanitizer : asap_add_google_ubsan(target) +# thread sanitizer : asap_add_google_tsan(target) +# ~~~ +# NOTES: +# +# ASAN and UBSAN can be run together, but TSAN must be run separately. +# +# Not all compilers support all sanitizers. It is the responsibility of the +# caller to call these functions when the selected compiler does support the +# requested sanitizer. Failure to do so will not abort the CMake generation, but +# will print a message when the compiler does not support the requested +# sanitizer. +# +# ------------------------------------------------------------------------------ + +# ---- Internal function to check for compiler support of the sanitizer flags -- + +include(CMakePushCheckState) +include(CheckCXXSourceCompiles) +include(CMakeCheckCompilerFlagCommonPatterns) + +function(_check_sanitizer_flags _flag _var) + cmake_push_check_state(RESET) + + # Add to compiler + set(CMAKE_REQUIRED_FLAGS "${_flag}") + # Add to linker + if(CMAKE_VERSION VERSION_LESS "3.14") + set(CMAKE_REQUIRED_LIBRARIES "${_flag}") + else() + set(CMAKE_REQUIRED_LINK_OPTIONS "${_flag}") + endif() + + # Normalize locale during test compilation. + set(_locale_vars LC_ALL LC_MESSAGES LANG) + foreach(v IN LISTS _locale_vars) + set(_locale_vars_saved_${v} "$ENV{${v}}") + set(ENV{${v}} C) + endforeach() + check_compiler_flag_common_patterns(_common_patterns) + check_cxx_source_compiles("int main() { return 0; }" ${_var} + ${_common_patterns}) + foreach(v IN LISTS _locale_vars) + set(ENV{${v}} ${_locale_vars_saved_${v}}) + endforeach() + set(${_var} + "${${_var}}" + PARENT_SCOPE) + + cmake_pop_check_state() +endfunction() + +macro(_report_error sanitizer sanitizer_lib) + message( + STATUS + "\ +WARNING: ${sanitizer} Sanitizer was requested but is not working!\n\ +-- => You may want to do the following:\n\ +-- 1- Check that your compiler(${CMAKE_CXX_COMPILER}) does support it,\n\ +-- 2- Make sure ${sanitizer_lib} is installed.") +endmacro() + +# ---- Address Sanitizer +# --------------------------------------------------------------------------- + +function(asap_add_google_asan target) + # In order to use AddressSanitizer you will need to compile and link your + # program using clang with the -fsanitize=address switch. To get a reasonable + # performance add -O1 or higher. To get nicer stack traces in error messages + # add -fno-omit-frame-pointer. + set(SANITIZER_FLAGS_ASAN "-fsanitize=address" "-fno-omit-frame-pointer") + + if(NOT DEFINED COMPILER_SUPPORTS_ASAN) + # We'll use these flags to detect if the compiler supports ASan or not + message(STATUS "Checking Address sanitizer...") + _check_sanitizer_flags("${SANITIZER_FLAGS_ASAN}" COMPILER_SUPPORTS_ASAN) + if(NOT COMPILER_SUPPORTS_ASAN) + _report_error("Address" "libasan") + endif() + endif() + + if(COMPILER_SUPPORTS_ASAN) + # Only create this internal target once + if(NOT TARGET internal_asan) + add_library(internal_asan INTERFACE IMPORTED) + set_target_properties( + internal_asan + PROPERTIES INTERFACE_COMPILE_OPTIONS "${SANITIZER_FLAGS_ASAN}" + INTERFACE_LINK_OPTIONS "${SANITIZER_FLAGS_ASAN}") + endif() + target_link_libraries(${target} PRIVATE internal_asan) + endif() +endfunction() + +# ---- Undefined Behavior Sanitizer +# ---------------------------------------------------------------- + +function(asap_add_google_ubsan target) + # To use UBSan, compile and link your program with -fsanitize=undefined. To + # get nicer output for file names, we'll only keep the last 3 components of + # the path. + set(SANITIZER_FLAGS_UBSAN "-fsanitize=undefined" + "-fsanitize-undefined-strip-path-components=-2") + + if(NOT DEFINED COMPILER_SUPPORTS_UBSAN) + # We'll use these flags to detect if the compiler supports ASan or not + message(STATUS "Checking Undefined Behavior sanitizer...") + _check_sanitizer_flags("${SANITIZER_FLAGS_UBSAN}" COMPILER_SUPPORTS_UBSAN) + if(NOT COMPILER_SUPPORTS_UBSAN) + _report_error("Undefined Behavior" "libubsan") + endif() + endif() + + if(COMPILER_SUPPORTS_UBSAN) + # Only create this internal target once + if(NOT TARGET internal_ubsan) + add_library(internal_ubsan INTERFACE IMPORTED) + set_target_properties( + internal_ubsan + PROPERTIES INTERFACE_COMPILE_OPTIONS "${SANITIZER_FLAGS_UBSAN}" + INTERFACE_LINK_OPTIONS "${SANITIZER_FLAGS_UBSAN}") + endif() + target_link_libraries(${target} PRIVATE internal_ubsan) + endif() +endfunction() + +# ---- Thread Sanitizer +# ---------------------------------------------------------------------------- + +function(asap_add_google_tsan target) + # To use TSan, compile and link your program with -fsanitize=thread. + set(SANITIZER_FLAGS_TSAN "-fsanitize=thread") + + if(NOT DEFINED COMPILER_SUPPORTS_TSAN) + # We'll use these flags to detect if the compiler supports ASan or not + message(STATUS "Checking Thread sanitizer...") + _check_sanitizer_flags("${SANITIZER_FLAGS_TSAN}" COMPILER_SUPPORTS_TSAN) + if(NOT COMPILER_SUPPORTS_TSAN) + _report_error("Thread" "libtsan") + endif() + endif() + + if(COMPILER_SUPPORTS_TSAN) + # Only create this internal target once + if(NOT TARGET internal_tsan) + add_library(internal_tsan INTERFACE IMPORTED) + set_target_properties( + internal_tsan + PROPERTIES INTERFACE_COMPILE_OPTIONS "${SANITIZER_FLAGS_TSAN}" + INTERFACE_LINK_OPTIONS "${SANITIZER_FLAGS_TSAN}") + endif() + target_link_libraries(${target} PRIVATE internal_tsan) + endif() +endfunction() + +# ---- Helper to activate all sanitizers +# ----------------------------------------------------------- + +function(asap_add_sanitizers target) + if(ASAP_WITH_GOOGLE_ASAN) + asap_add_google_asan(${target}) + endif() + if(ASAP_WITH_GOOGLE_UBSAN) + asap_add_google_ubsan(${target}) + endif() + if(ASAP_WITH_GOOGLE_TSAN) + asap_add_google_tsan(${target}) + endif() + + # Add -O2 to get some reasonable performance during the test + if(ASAP_WITH_GOOGLE_ASAN + OR ASAP_WITH_GOOGLE_UBSAN + OR ASAP_WITH_GOOGLE_TSAN) + target_compile_options(${target} PRIVATE -O2) + endif() +endfunction() diff --git a/cmake/LanguageStandards.cmake b/cmake/LanguageStandards.cmake new file mode 100644 index 0000000..05943bf --- /dev/null +++ b/cmake/LanguageStandards.cmake @@ -0,0 +1,11 @@ +# ===-----------------------------------------------------------------------===# +# Distributed under the 3-Clause BSD License. See accompanying file LICENSE or +# copy at https://opensource.org/licenses/BSD-3-Clause). +# SPDX-License-Identifier: BSD-3-Clause +# ===-----------------------------------------------------------------------===# + +include(common/LanguageStandards) + +function(asap_set_language_standards) + swift_set_language_standards(${ARGV}) +endfunction() diff --git a/cmake/ListTargets.cmake b/cmake/ListTargets.cmake new file mode 100644 index 0000000..6be3d3c --- /dev/null +++ b/cmake/ListTargets.cmake @@ -0,0 +1,19 @@ +# ===-----------------------------------------------------------------------===# +# Distributed under the 3-Clause BSD License. See accompanying file LICENSE or +# copy at https://opensource.org/licenses/BSD-3-Clause). +# SPDX-License-Identifier: BSD-3-Clause +# ===-----------------------------------------------------------------------===# + +include(common/ListTargets) + +function(asap_get_all_targets result dir) + get_all_targets(${result} ${dir} ${ARGM}) +endfunction() + +function(asap_list_targets out_var) + swift_list_targets(${out_var}, ${ARGN}) +endfunction() + +function(asap_list_compilable_targets out_var) + swift_list_compilable_targets(${out_var}, ${ARGN}) +endfunction() diff --git a/cmake/SphinxGeneration.cmake b/cmake/SphinxGeneration.cmake new file mode 100644 index 0000000..88975c6 --- /dev/null +++ b/cmake/SphinxGeneration.cmake @@ -0,0 +1,82 @@ +# ===-----------------------------------------------------------------------===# +# Distributed under the 3-Clause BSD License. See accompanying file LICENSE or +# copy at https://opensource.org/licenses/BSD-3-Clause). +# SPDX-License-Identifier: BSD-3-Clause +# ===-----------------------------------------------------------------------===# + +# ------------------------------------------------------------------------------ +# Documentation with Sphinx/Breathe/Exhale +# ------------------------------------------------------------------------------ + +# To avoid indiscriminately generating documentation for all modules in the +# project, including third party modules and stuff for which we don't want +# documentation to be generated, we provide here the basic tools to add +# breathe/exhale capabilities to a module. Additionally, exhale can only process +# one module at a time, so we need to have the generation run for each module. +# +# Doxygen is run separately NOT by exhale. Sphinx targets must be run after +# doxygen. +# +# To use in a submodule or in the master module add the following: +# +# ~~~ +# asap_with_sphinx(${target}) +# ~~~ +# +# Use 'make sphinx' to generate documentation. (not done by default) +# + +include(FindSphinx) + +if(SPHINX_FOUND) + message(STATUS "System has sphinx.") + + # Add a master sphinx target to collect all submodules + add_custom_target(sphinx) + set_target_properties(sphinx PROPERTIES EXCLUDE_FROM_ALL TRUE) + + # The macro to add a submodule as a sphinx target. + macro(asap_with_sphinx TARGET_NAME) + # Setup work directory for the target module + set(SPHINX_TARGET_WORKDIR "${SPHINX_BUILD_DIR}/${TARGET_NAME}") + if(NOT EXISTS "${SPHINX_TARGET_WORKDIR}") + file(MAKE_DIRECTORY "${SPHINX_TARGET_WORKDIR}") + endif() + # Do @ substitutions in the sphinx config file for the module + configure_file("${CMAKE_CURRENT_SOURCE_DIR}/doc/conf.py.in" + "${CMAKE_CURRENT_SOURCE_DIR}/doc/conf.py" @ONLY) + + # Add a target for building the sphinx documentation of the module + add_custom_target( + ${TARGET_NAME}_sphinx + COMMAND + ${SPHINX_EXECUTABLE} -q -b html -c "${EXHALE_TARGET_WORKDIR}" -d + "${SPHINX_TARGET_WORKDIR}/_doctrees" "${CMAKE_CURRENT_SOURCE_DIR}/doc" + "${SPHINX_TARGET_WORKDIR}/html" + WORKING_DIRECTORY "${SPHINX_TARGET_WORKDIR}" + VERBATIM + COMMENT "Generating `sphinx` documentation for `${TARGET_NAME}`") + set_target_properties(${TARGET_NAME}_sphinx PROPERTIES EXCLUDE_FROM_ALL + TRUE) + # Finally add the module sphinx target as a dependency for the overall + # sphinx target + add_dependencies(sphinx ${TARGET_NAME}_sphinx) + + # Install sphinx docs + install( + DIRECTORY ${SPHINX_BUILD_DIR}/${TARGET_NAME} + DESTINATION ${ASAP_INSTALL_DOC} + COMPONENT ${TARGET_NAME}_docs) + + endmacro() + # We only build documentation through explicit invocation of the sphinx target + # as it is pretty heavy and requires doxygen to be run before it is invoked. + set_target_properties(sphinx PROPERTIES EXCLUDE_FROM_ALL TRUE) + +else(SPHINX_FOUND) + message(STATUS "WARNING: sphinx is not available on this system!") + + macro(asap_with_sphinx TARGET_NAME) + + endmacro() +endif(SPHINX_FOUND) diff --git a/cmake/TestTargets.cmake b/cmake/TestTargets.cmake new file mode 100644 index 0000000..017225c --- /dev/null +++ b/cmake/TestTargets.cmake @@ -0,0 +1,25 @@ +# ===-----------------------------------------------------------------------===# +# Distributed under the 3-Clause BSD License. See accompanying file LICENSE or +# copy at https://opensource.org/licenses/BSD-3-Clause). +# SPDX-License-Identifier: BSD-3-Clause +# ===-----------------------------------------------------------------------===# + +include(common/TestTargets) + +macro(asap_add_test target) + swift_add_test("${target}" ${ARGN}) + + asap_set_compile_options(${target} WARNING) + asap_set_compile_definitions(${target}) + if(TARGET gtest AND BUILD_SHARED_LIBS) + target_compile_definitions(${target} PRIVATE GTEST_LINKED_AS_SHARED_LIBRARY) + if(MSVC) + target_compile_options(${target} PRIVATE /wd4251) + endif() + endif() + set_target_properties(${target} PROPERTIES FOLDER "Unit Tests") +endmacro() + +macro(asap_add_test_runner target) + swift_add_test_runner("${target}" ${ARGN}) +endmacro() diff --git a/cmake/Valgrind.cmake b/cmake/Valgrind.cmake new file mode 100644 index 0000000..be95610 --- /dev/null +++ b/cmake/Valgrind.cmake @@ -0,0 +1,22 @@ +# ===-----------------------------------------------------------------------===# +# Distributed under the 3-Clause BSD License. See accompanying file LICENSE or +# copy at https://opensource.org/licenses/BSD-3-Clause). +# SPDX-License-Identifier: BSD-3-Clause +# ===-----------------------------------------------------------------------===# + +include(common/Valgrind) + +mark_as_advanced(${PROJECT_NAME}_ENABLE_PROFILING) +set(${PROJECT_NAME}_ENABLE_PROFILING ${ASAP_WITH_VALGRIND}) + +function(asap_add_valgrind_memcheck target) + swift_add_valgrind_memcheck(${target}, ${ARGN}) +endfunction() + +function(asap_add_valgrind_callgrind target) + swift_add_valgrind_callgrind(${target}, ${ARGN}) +endfunction() + +function(asap_add_valgrind_massif target) + swift_add_valgrind_massif(${target}, ${ARGN}) +endfunction() diff --git a/cmake/common b/cmake/common new file mode 160000 index 0000000..bf653ff --- /dev/null +++ b/cmake/common @@ -0,0 +1 @@ +Subproject commit bf653ff033a88c3f8e3672f5d7472eaffb412ea1 diff --git a/cmake/scripts/standard-version-updater.js b/cmake/scripts/standard-version-updater.js new file mode 100644 index 0000000..a34e2b4 --- /dev/null +++ b/cmake/scripts/standard-version-updater.js @@ -0,0 +1,96 @@ +//===----------------------------------------------------------------------===// +// Distributed under the 3-Clause BSD License. See accompanying file LICENSE or +// copy at https://opensource.org/licenses/BSD-3-Clause). +// SPDX-License-Identifier: BSD-3-Clause +//===----------------------------------------------------------------------===// + +// ----------------------------------------------------------------------------- +// Detect the dominant newline character of a string, from 'detect-newline' +// https://github.com/sindresorhus/detect-newline +// +// MIT License +// ----------------------------------------------------------------------------- +function detectNewline(string) { + if (typeof string !== 'string') { + throw new TypeError('Expected a string'); + } + + const newlines = string.match(/(?:\r?\n)/g) || []; + + if (newlines.length === 0) { + return; + } + + const crlf = newlines.filter(newline => newline === '\r\n').length; + const lf = newlines.length - crlf; + + return crlf > lf ? '\r\n' : '\n'; +} + +function detectNewlineGraceful(string) { + return (typeof string === 'string' && detectNewline(string)) || '\n'; +} +// ----------------------------------------------------------------------------- + +// ----------------------------------------------------------------------------- +// Version reader/updated for standard-version that uses the mETA information in +// the CmakeLists.txt file +// ----------------------------------------------------------------------------- + +const major_rex = /set\(META_VERSION_MAJOR\s+\"(\d+)\"\)/; +const minor_rex = /set\(META_VERSION_MINOR\s+\"(\d+)\"\)/; +const patch_rex = /set\(META_VERSION_PATCH\s+\"(\d+)\"\)/; + +module.exports.readVersion = function (contents) { + var major = null, minor = null, patch = null; + + const lines = contents.split(/\r?\n/); + for (let index in lines) { + let line = lines[index]; + var match = null; + if (major == null) { + var match = major_rex.exec(line); + if (match != null) { + major = match[1]; + } + } + if (match == null && minor == null) { + var match = minor_rex.exec(line); + if (match != null) { + minor = match[1]; + } + } + if (match == null && patch == null) { + var match = patch_rex.exec(line); + if (match != null) { + patch = match[1]; + } + } + if (major != null && minor != null && patch != null) break; + }; + + if (major == null) + console.error("Your CmakeLists.txt is missing META_VERSION_MAJOR variable!"); + if (minor == null) + console.error("Your CmakeLists.txt is missing META_VERSION_MINOR variable!"); + if (patch == null) + console.error("Your CmakeLists.txt is missing META_VERSION_PATCH variable!"); + + return major + "." + minor + "." + patch; +} + +module.exports.writeVersion = function (contents, version) { + var [major, minor, patch] = version.split("."); + var newContents = []; + + const lines = contents.split(/\r?\n/); + lines.forEach(line => { + var newLine = line.replace(major_rex, "set(META_VERSION_MAJOR \"" + major + "\")") + .replace(minor_rex, "set(META_VERSION_MINOR \"" + minor + "\")") + .replace(patch_rex, "set(META_VERSION_PATCH \"" + patch + "\")"); + newContents.push(newLine); + }); + + let newline = detectNewlineGraceful(contents) + return newContents.join(newline); +} diff --git a/common/.gitignore b/common/.gitignore new file mode 100644 index 0000000..3f650c5 --- /dev/null +++ b/common/.gitignore @@ -0,0 +1,2 @@ +# Generated files inside source tree +doc/conf.py diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt new file mode 100644 index 0000000..4318968 --- /dev/null +++ b/common/CMakeLists.txt @@ -0,0 +1,146 @@ +# ===-----------------------------------------------------------------------===# +# Distributed under the 3-Clause BSD License. See accompanying file LICENSE or +# copy at https://opensource.org/licenses/BSD-3-Clause). +# SPDX-License-Identifier: BSD-3-Clause +# ===-----------------------------------------------------------------------===# + +# ------------------------------------------------------------------------------ +# Meta information about the this module +# ------------------------------------------------------------------------------ + +asap_declare_module( + MODULE_NAME + "common" + DESCRIPTION + "Common functionality for asap based projects" + GITHUB_REPO + "https://github.com/abdes/asap" + AUTHOR_MAINTAINER + "Abdessattar Sassi" + VERSION_MAJOR + "1" + VERSION_MINOR + "2" + VERSION_PATCH + "0") + +# ------------------------------------------------------------------------------ +# Code generation +# ------------------------------------------------------------------------------ + +# Generate config-header +configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/include/${META_MODULE_NAME}/config.h.in + ${CMAKE_CURRENT_BINARY_DIR}/include/${META_MODULE_NAME}/config.h) + +# ============================================================================== +# Build instructions +# ============================================================================== + +# ------------------------------------------------------------------------------ +# Main module target +# ------------------------------------------------------------------------------ + +# Hardcode `asap` in the module name as we do not want this prefix to change +# with the forked project name. +set(MODULE_TARGET_NAME "asap_${META_MODULE_NAME}") + +asap_add_library(${MODULE_TARGET_NAME} INTERFACE WARNING) + +target_link_libraries(${MODULE_TARGET_NAME} INTERFACE Microsoft.GSL::GSL) + +target_include_directories( + ${MODULE_TARGET_NAME} + INTERFACE $<INSTALL_INTERFACE:include> + $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include> + $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/include>) + +target_compile_features(${MODULE_TARGET_NAME} INTERFACE cxx_std_17) + +# common comes directly from asap and is usually kept as is. It is more +# convenient to simply use it as asap::common +add_library(asap::${META_MODULE_NAME} ALIAS ${MODULE_TARGET_NAME}) + +# Add support for (optional) code quality tools +asap_add_sanitizers(${MODULE_TARGET_NAME}) + +# Generate module config files for cmake and pkgconfig +asap_create_module_config_files() + +# ------------------------------------------------------------------------------ +# Tests +# ------------------------------------------------------------------------------ + +if(ASAP_BUILD_TESTS) + add_subdirectory(test) +endif() + +# ------------------------------------------------------------------------------ +# API Documentation +# ------------------------------------------------------------------------------ + +asap_with_doxygen( + MODULE_NAME + ${MODULE_TARGET_NAME} + VERSION + ${META_MODULE_VERSION} + TITLE + "\"Common Module\"" + BRIEF + "\"Provides common basic building blocks such as portability, assertions, etc.\"" + INPUT_PATH + "${CMAKE_CURRENT_SOURCE_DIR}/src ${CMAKE_CURRENT_SOURCE_DIR}/include") + +asap_with_sphinx(${MODULE_TARGET_NAME}) + +# ============================================================================== +# Deployment instructions +# ============================================================================== + +set(TARGETS_EXPORT_NAME "${MODULE_TARGET_NAME}Targets") +set(runtime "${MODULE_TARGET_NAME}_runtime") +set(dev "${MODULE_TARGET_NAME}_dev") + +# Library +install( + TARGETS ${MODULE_TARGET_NAME} + EXPORT "${TARGETS_EXPORT_NAME}" + COMPONENT dev + RUNTIME DESTINATION ${ASAP_INSTALL_BIN} COMPONENT ${runtime} + LIBRARY DESTINATION ${ASAP_INSTALL_SHARED} COMPONENT ${runtime} + ARCHIVE DESTINATION ${ASAP_INSTALL_LIB} COMPONENT ${dev}) + +# Header files +install( + DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/${META_MODULE_NAME} + DESTINATION ${ASAP_INSTALL_INCLUDE} + COMPONENT ${dev} + FILES_MATCHING + PATTERN "*.h") + +# Contract library Header files +install( + DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/contract + DESTINATION ${ASAP_INSTALL_INCLUDE} + COMPONENT ${dev} + FILES_MATCHING + PATTERN "*.h") + +# Generated header files +install( + DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/include/${META_MODULE_NAME} + DESTINATION ${ASAP_INSTALL_INCLUDE} + COMPONENT ${dev}) + +# Target config +install( + EXPORT ${TARGETS_EXPORT_NAME} + NAMESPACE ${META_PROJECT_NAME}:: + DESTINATION ${ASAP_INSTALL_CMAKE}/${META_MODULE_NAME} + COMPONENT ${dev}) + +# Package configuration files +install( + FILES ${CMAKE_CURRENT_BINARY_DIR}/${MODULE_TARGET_NAME}Config.cmake + ${CMAKE_CURRENT_BINARY_DIR}/${MODULE_TARGET_NAME}ConfigVersion.cmake + DESTINATION ${ASAP_INSTALL_CMAKE}/${META_MODULE_NAME}) diff --git a/common/README.md b/common/README.md new file mode 100644 index 0000000..0cdf2d3 --- /dev/null +++ b/common/README.md @@ -0,0 +1,30 @@ +# Common submodule for the asap project + +See the asap project on [GitHub](https://github.com/abdes/asap) for a functional +minimal starter project that uses this submodule. + +This submodule is not intended to be used alone. Instead, refer to the asap +project for the recommended container to fully leverage this submodule and add +other libraries/executables/etc... to the top level project. + +Functionality offered by this submodule includes: + - cmake build helpers for the end-to-end lifecycle in 'cmake' subdirectory + - assertions + - logging using spdlog + - unit testing using Catch2 + - documentation using restructured text with + [sphinx](http://www.sphinx-doc.org/en/master/) + - API documentation using [doxygen](http://www.doxygen.org), translated to + reST using [breathe](https://breathe.readthedocs.io/en/latest/) + +## Getting the code + +Refer to the asap project. + +## Building + +Refer to the asap project. + +## Using + +Refer to the documentation generated from doxygen or doxygen+sphinx. diff --git a/common/config.cmake.in b/common/config.cmake.in new file mode 100644 index 0000000..745e77d --- /dev/null +++ b/common/config.cmake.in @@ -0,0 +1,7 @@ +set(@MODULE_TARGET_NAME@_VERSION @META_MODULE_VERSION@) + +@PACKAGE_INIT@ + +include("${CMAKE_CURRENT_LIST_DIR}/@TARGETS_EXPORT_NAME@.cmake") + +check_required_components(@MODULE_TARGET_NAME@) diff --git a/common/config.pc.in b/common/config.pc.in new file mode 100644 index 0000000..7bbd13b --- /dev/null +++ b/common/config.pc.in @@ -0,0 +1,11 @@ +prefix=@ASAP_INSTALL_PREFIX_FULL_PATH@ +libdir=${prefix}/@ASAP_INSTALL_LIB@ +includedir=${prefix}/@ASAP_INSTALL_INCLUDE@ + +Name: @MODULE_TARGET_NAME@ +URL: @META_MODULE_GITHUB_REPO@ +Description: @META_MODULE_DESCRIPTION@ +Version: @META_MODULE_VERSION@ +Requires: Microsoft.GSL +Cflags: -I${includedir} +Libs: -L${libdir} @MODULE_LINK_LIBS@ diff --git a/common/doc/_static/favicon.ico b/common/doc/_static/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..e2da7c884008a26f52634b2c5e2b3ade92a4705a GIT binary patch literal 178091 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 Xz};O&0NL{Rp92(S)!tW0e+u~z;o@TU literal 0 HcmV?d00001 diff --git a/common/doc/_static/logo.png b/common/doc/_static/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..4ca2442366da0413a602034a62330c08407e9e4f GIT binary patch literal 34593 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 literal 0 HcmV?d00001 diff --git a/common/doc/api.rst b/common/doc/api.rst new file mode 100644 index 0000000..cda2427 --- /dev/null +++ b/common/doc/api.rst @@ -0,0 +1,22 @@ +.. Structure conventions + # with overline, for parts + * with overline, for chapters + = for sections + - for subsections + ^ for sub-subsections + " for paragraphs + +************* +API reference +************* + +.. toctree:: + :maxdepth: 2 + :hidden: + + Platform detection <api/platform> + Compiler detection <api/compilers> + Mask/Flags manipulation <api/flags> + Contract checking API <api/contract> + Overload pattern <api/overload> + Mixin library <api/mixin> diff --git a/common/doc/api/compilers.rst b/common/doc/api/compilers.rst new file mode 100644 index 0000000..2694bb0 --- /dev/null +++ b/common/doc/api/compilers.rst @@ -0,0 +1,115 @@ +.. Structure conventions + # with overline, for parts + * with overline, for chapters + = for sections + - for subsections + ^ for sub-subsections + " for paragraphs + +****************** +Compiler detection +****************** + +The `common` module provides a set of macros that help in dealing with the +various compilers that may be used to build the software. These macros come in 4 +categories: + +- `Identification`_ +- `Version checking`_ +- `Feature testing`_ +- `Diagnostics`_ +- `Optimization hints`_ + +Identification +============== + +These macros are defined only if the compiler in question is in use (e.g., +ASAP_MSVC_VERSION is only defined when using the MSVC compiler). When they are +defined, they are defined to the version of the compiler currently in use, +encoded into a single integer. + +This provides a uniform way to check the if a specific compiler is in use, as +well as determine the version. + +Note that the GCC and GNUC macros are different. Many compilers masquerade as +GCC (by defining __GNUC__, __GNUC_MINOR__, and __GNUC_PATCHLEVEL__, but often do +not implement all the features that the version of GCC they pretend to be +supports. To work around this, the ASAP_GCC_VERSION macro is only defined for +GCC, whereas ASAP_GNUC_VERSION will be defined whenever a compiler defines +__GNUC__. + +.. doxygendefine:: ASAP_CLANG_VERSION + +.. doxygendefine:: ASAP_MSVC_VERSION + +.. doxygendefine:: ASAP_GNUC_VERSION + +.. doxygendefine:: ASAP_GCC_VERSION + +Version checking +================ + +For each compiler for which there is a ASAP_*_VERSION macro, there is a +ASAP_*_VERSION_CHECK(major,minor,revision) macro. These macros are always +defined, and will return true (1) if the current compiler corresponds to the +macro name, and the compiler version is greater than or equal to the provided +values. + +This provides a convenient way to check for features based on the version number. + +.. doxygendefine:: ASAP_CLANG_VERSION_CHECK + +.. doxygendefine:: ASAP_MSVC_VERSION_CHECK + +.. doxygendefine:: ASAP_GNUC_VERSION_CHECK + +.. doxygendefine:: ASAP_GCC_VERSION_CHECK + +Feature testing +=============== + +`Clang`` introduced an easier way to detect different features which doesn't +rely on knowledge of the version number it first appeared in. Not only is this +the preferred method for checking for features in `clang`, it's pretty much the +only way! + +Many clang-based compilers implement these macros, including Apple. +Additionally, some non-clang-based compilers support a subset of the macros. + +Attributes +---------- + +.. doxygendefine:: ASAP_HAS_ATTRIBUTE + +.. doxygendefine:: ASAP_HAS_CPP_ATTRIBUTE + + +Builtin functions +----------------- + +.. doxygendefine:: ASAP_HAS_BUILTIN + +Features +-------- + +.. doxygendefine:: ASAP_HAS_FEATURE + +Diagnostics +=========== + +.. doxygendefine:: ASAP_PRAGMA + +.. doxygendefine:: ASAP_DIAGNOSTIC_PUSH + +.. doxygendefine:: ASAP_DIAGNOSTIC_POP + +.. doxygendefine:: ASAP_HAS_WARNING + +Optimization hints +================== + +.. doxygendefine:: ASAP_ASSUME + +.. doxygendefine:: ASAP_UNREACHABLE + +.. doxygendefine:: ASAP_UNREACHABLE_RETURN diff --git a/common/doc/api/contract.rst b/common/doc/api/contract.rst new file mode 100644 index 0000000..7cf2df5 --- /dev/null +++ b/common/doc/api/contract.rst @@ -0,0 +1,110 @@ +.. Structure conventions + # with overline, for parts + * with overline, for chapters + = for sections + - for subsections + ^ for sub-subsections + " for paragraphs + +***************** +Contract checking +***************** + +Contracts are usually expressed in the form of preconditions, post-conditions +and assertions. Some programming languages have built-in support for contracts. +So far, the closest in C++ is assertions. But assertions are not expressive and +are not consistent. This simple API is intended at offering some rudimentary +support of contract programming in the form of preconditions, post-conditions +and assertions with three levels of contract enforcement at build time: `off`, +`default` and `audit`, while waiting for it to become part of the C++ language +in a near future. + +Alternative libraries and references +==================================== + +- `Contract Checking in C++: A (long-term) Road Map + <http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1332r0.txt>`_. + +- `libcontract <https://github.com/alexeiz/contract>`_ Library to support + contract programming in C++11. + +- `Boost.Contract + <https://www.boost.org/doc/libs/1_78_0/libs/contract/doc/html/index.html>`_ + implements contract programming (a.k.a., Design by Contract or DbC) [1] for + the C++ programming language.. + +- `N3753 <http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3753.pdf>`_, + "Centralized Defensive-Programming Support for Narrow Contracts" proposed for + C++14 Standard. + +- `N1962 <http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n1962.html>`_, + "Proposal to add Contract Programming to C++ (revision 4)" (not adopted for + C++11 Standard). + +Contract checking macros +======================== + +There are three contract checking modes that can be set at build time by +defining the appropriate symbol. Each mode has a specific behavior for the +contract checking at runtime as described in the table below: + +.. list-table:: + :header-rows: 1 + :widths: 15 30 55 + + * - Build mode + - Symbol to be defined + - Behavior + + * - OFF + - ASAP_CONTRACT_OFF + - None of the conditions are enforced. + + * - DEFAULT + - ASAP_CONTRACT_DEFAULT + - Default mode conditions are enforced and execution will abort if the + contract is not honored. Audit mode conditions are ignored. + + * - AUDIT + - ASAP_CONTRACT_AUDIT + - All conditions are enforced and execution will abort if the contract is + not honored. + +Default mode macros +------------------- + +.. doxygendefine:: ASAP_EXPECT + +.. doxygendefine:: ASAP_ENSURE + +.. doxygendefine:: ASAP_ASSERT + +Audit mode macros +------------------- + +.. doxygendefine:: ASAP_EXPECT_AUDIT + +.. doxygendefine:: ASAP_ENSURE_AUDIT + +.. doxygendefine:: ASAP_ASSERT_AUDIT + +Violation Handler +================= + +There is a single violation handler in the system. Its implementation, however, +can be switched at runtime to install a custom handler. + +.. doxygenfunction:: GetViolationHandler + +.. doxygenclass:: asap::contract::ViolationHandler + :members: + +Unit testing contracts +====================== + +Testing contracts is tricky as violations often result in the program execution +being abruptly terminated rendering it quite difficult to test without death +test support by the testing framework. + +To simplify this situation, you can use the `contracts` helper library +which provides special macros for testing contracts without death tests. diff --git a/common/doc/api/flags.rst b/common/doc/api/flags.rst new file mode 100644 index 0000000..7a3ae40 --- /dev/null +++ b/common/doc/api/flags.rst @@ -0,0 +1,36 @@ +.. Structure conventions + # with overline, for parts + * with overline, for chapters + = for sections + - for subsections + ^ for sub-subsections + " for paragraphs + +******************************** +Masks/Flags manipulation helpers +******************************** + +The header <common/flag_ops.h> defines a number of helper template functions +to set, clear, flip, and test flags in a bitset mask. All these functions +can only be instantiated with an arithmetic type (e.g. int) or an enum type. + + * :ref:`FlagSet <FlagSet>` + * :ref:`FlagClear <FlagClear>` + * :ref:`FlagFlip <FlagFlip>` + * :ref:`FlagTest <FlagTest>` + +.. _FlagSet: + +.. doxygenfunction:: asap::FlagSet + +.. _FlagClear: + +.. doxygenfunction:: asap::FlagClear + +.. _FlagFlip: + +.. doxygenfunction:: asap::FlagFlip + +.. _FlagTest: + +.. doxygenfunction:: asap::FlagTest diff --git a/common/doc/api/mixin.rst b/common/doc/api/mixin.rst new file mode 100644 index 0000000..31fc166 --- /dev/null +++ b/common/doc/api/mixin.rst @@ -0,0 +1,53 @@ +.. Structure conventions + # with overline, for parts + * with overline, for chapters + = for sections + - for subsections + ^ for sub-subsections + " for paragraphs + +********************* +Mixin support library +********************* + +.. doxygenfile:: mixin.h + :sections: detaileddescription + +The mixin class +=============== + +.. doxygenstruct:: asap::mixin::Mixin + :members: + +Interfaces and mixins +===================== + +When it comes to mixing interfaces in a composite class, we have two options: + +1. Implement the interface in a mixin that gets mixed in. + +2. Mix a mixin that provides the interface and implement it in the composite + class. + +Mixin implements interface +-------------------------- + +A mixin must derive from its only template parameter but can also derive from +any other base class. This property can be exploited to implement an interface +inside a mixin. This is particularly useful when the entire interface makes +sense to be implemented in a single place. + +Mixin provides interface +------------------------ + +Often, the interface API is too big to be implemented in a single mixin. In such +case, we can provide the interface to the composite class via a mixin and +implement its API in any suitable way. The challenge with this is the fact that +we require a mixin to have a single template parameter. To solve this challenge +we use a helper that can curry multiple template parameters and a special mixin +`Provides` that uses the curry helper to provide an interface class to the +composite. + +.. doxygenstruct:: asap::mixin::Curry + +.. doxygenstruct:: asap::mixins::Provides diff --git a/common/doc/api/overload.rst b/common/doc/api/overload.rst new file mode 100644 index 0000000..576d98f --- /dev/null +++ b/common/doc/api/overload.rst @@ -0,0 +1,26 @@ +.. Structure conventions + # with overline, for parts + * with overline, for chapters + = for sections + - for subsections + ^ for sub-subsections + " for paragraphs + +******************** +The overload pattern +******************** + +The header <common/overload.h> implements the useful overload pattern, which +allows to use separate lambdas "in=place" for visiting a variant. Traditionally +lambdas can not be implicitly overloaded. + +In C++14 you could derive from lambdas and build similar helper types, but only +with C++17 you can significantly reduce boilerplate code and limit potential +errors. With C++20 we’ll get even shorter syntax as CTAD (*Class Template +Argument Deduction*) will work with aggregates. + +You can read more in the proposal for overload `P0051 +<https://wg21.link/P0051>`_ (it was not accepted for C++20, but it’s worth to +see discussions and concepts behind it). + +.. doxygenstruct:: asap::Overload diff --git a/common/doc/api/platform.rst b/common/doc/api/platform.rst new file mode 100644 index 0000000..b13c218 --- /dev/null +++ b/common/doc/api/platform.rst @@ -0,0 +1,97 @@ +.. Structure conventions + # with overline, for parts + * with overline, for chapters + = for sections + - for subsections + ^ for sub-subsections + " for paragraphs + +********************************************* +Platform and environment preprocessor symbols +********************************************* + +The *common* module provides a number of preprocessor symbols that can be used +to detect the current platform and features available on it. These symbols are +present in the following files: + + **platform.h** + *operating system detection symbols* + **config.h** + *features used in the common module and which can be enabled/disabled* + +Operating System detection +========================== + +The following symbols are defined/undefined based on the operating system on +which the module is being compiled. + + * ASAP_WINDOWS : OS is Windows + * ASAP_WINDOWS_CYGWIN : OS is Windows, using cygwin + * ASAP_WINDOWS_MINGW : OS is Windows, using MinGW 32 or 64bit + * ASAP_UNIX : OS is a unix variant + + * ASAP_APPLE : OS is any of Apple variants (OS X, iOS, iOS simulator) + + * ASAP_APPLE_IOS_SIMULATOR : OS is Apple iOS is Xcode simulator + * ASAP_APPLE_IOS : OS is Apple iOS + * ASAP_APPLE_OSX : OS is Apple OS X + + * ASAP_BSD : OS is a BSD variant (FreeBSD, NetBSD, OpenBSD, DrangonFly, + BSDI) + * ASAP_CYGWIN : OS is CygWin not running on Windows + * ASAP_LINUX : OS is a Linux variant + + * ASAP_GNU_LINUX : OS is GNU/Linux + * ASAP_ANDROID : OS is the Android variant of Linux + + * ASAP_SUN : OS is an Oracle/SUN variant + * ASAP_SUN_SOLARIS : OS is the Oracle/SUN Solaris + * ASAP_SUN_SUNOS : OS is the Oracle/SUN SunOS + * ASAP_HPUX : OS is HP-UX + * ASAP_AIX : OS is IBM AIX + +An example fo how to use these symbols is as following: + +.. code-block:: c++ + + #include <common/platform.h> + + #if defined(ASAP_WINDOWS) + // do some windows specific things + #elif defined(ASAP_LINUX) + // do some Linux specific things + #else + // do generic things for the rest of the platforms + #endif + +.. _feature-symbols: + +Feature symbols +=============== + +Special features implemented in this module can be enabled/disabled using +preprocessor symbols as described in the table below: + +========================== ==================================================== +Symbol Description +========================== ==================================================== +ASAP_USE_ASSERTS If defined with value 1, it indicates that assertion + macros should be defined to fail when assertions are + evaluation to false. Otherwise, the macros will be + defined to no-op. + + Default is 1. + +ASAP_USE_SYSTEM_ASSERTS If defined with value 1, system assert() will be + used instead of the custom macros. + + Default is 1. + +ASAP_USE_EXECINFO If defined with value 1, the platform has execinfo.h + and the assertion macros will be able to display + rich call stacks on failures (see `GNU libc backtrace <https://www.gnu.org/software/libc/manual/html_node/Backtraces.html>`_). + + Default is platform specific. + +========================== ==================================================== + diff --git a/common/doc/conf.py.in b/common/doc/conf.py.in new file mode 100644 index 0000000..bb3a729 --- /dev/null +++ b/common/doc/conf.py.in @@ -0,0 +1,131 @@ +# -*- coding: utf-8 -*- +# +# Configuration file for the Sphinx documentation builder. +# +# This file does only contain a selection of the most common options. For a +# full list see the documentation: +# http://www.sphinx-doc.org/en/master/config + +# -- Path setup -------------------------------------------------------------- + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +# import os +# import sys +# sys.path.insert(0, os.path.abspath('.')) + + +# -- Project information ----------------------------------------------------- + +project = u'asap/@META_MODULE_NAME@' +copyright = u'2018, The Authors' + +# The short X.Y version +version = u'@META_MODULE_VERSION@' + +rst_prolog = """ +.. |version| replace:: {0} +""".format(version) + + +# -- General configuration --------------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. +# +# needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + 'sphinx.ext.intersphinx', + 'sphinx.ext.todo', + 'sphinx.ext.coverage', + 'sphinx.ext.viewcode', + 'sphinx-prompt', + 'sphinx_copybutton', + 'breathe', +] + +# Setup the breathe extension +breathe_projects = { + "asap_@META_MODULE_NAME@": "@DOXYGEN_BUILD_DIR@/asap_@META_MODULE_NAME@/xml" +} +breathe_default_project = "asap_@META_MODULE_NAME@" + +# Tell sphinx what the primary language being documented is. +primary_domain = 'cpp' + +# Tell sphinx what the pygments highlight language should be. +highlight_language = 'cpp' + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, so +# a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ["_static"] + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. This pattern also affects +# html_static_path and html_extra_path. +exclude_patterns = ["_build", "Thumbs.db", ".DS_Store", ".sphinx"] + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +# +# source_suffix = ['.rst', '.md'] +source_suffix = '.rst' + +# The master toctree document. +master_doc = 'index' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +language = None + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + + +# -- Options for HTML output ------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = 'sphinx_book_theme' + +html_title = u'Common' +html_logo = "_static/logo.png" +html_favicon = "_static/favicon.ico" + +# If this is not None, a ‘Last updated on:’ timestamp is inserted at every page +# bottom, using the given strftime() format. The empty string is equivalent to +# '%b %d, %Y' (or a locale-dependent equivalent). +html_last_updated_fmt = None + +html_theme_options = { + "path_to_docs": "docs", + "repository_url": '@META_GITHUB_REPO@', + "use_repository_button": True, + "use_issues_button": True, + "use_edit_page_button": False, + "use_download_button": True, + "use_fullscreen_button": True, + # We don't want the default navigation bar footer to be displayed on every + # page. Mention of the book theme will be added in the home page. + "extra_navbar": "" +} + +# -- Extension configuration ------------------------------------------------- + +# -- Options for todo extension ---------------------------------------------- + +# If true, `todo` and `todoList` produce output, else they produce nothing. +todo_include_todos = True diff --git a/common/doc/index.rst b/common/doc/index.rst new file mode 100644 index 0000000..7ba0927 --- /dev/null +++ b/common/doc/index.rst @@ -0,0 +1,25 @@ +########################### +ASAP common's documentation +########################### + +.. toctree:: + :maxdepth: 2 + :hidden: + + API <api> + License <license> + Version <version> + +Welcome! This is the documentation for the *common* module, part of the *asap* +project. This is a core module on which the other modules depend to get access +to low level and frequently needed features such as platform/environment +detection, assertions and C++ language features not consistently provided by +all compilers on all platforms. + +Parts of the documentation +========================== + +:doc:`API reference <api>` +------------------------------ +*check this out to see the documentation of classes, macros, etc. offered by +this module* diff --git a/common/doc/license.rst b/common/doc/license.rst new file mode 100644 index 0000000..6a2eab4 --- /dev/null +++ b/common/doc/license.rst @@ -0,0 +1,14 @@ +.. Structure conventions + # with overline, for parts + * with overline, for chapters + = for sections + - for subsections + ^ for sub-subsections + " for paragraphs + +******* +License +******* + +.. include:: ../../LICENSE + diff --git a/common/doc/version.rst b/common/doc/version.rst new file mode 100644 index 0000000..bd1c930 --- /dev/null +++ b/common/doc/version.rst @@ -0,0 +1,14 @@ +.. Structure conventions + # with overline, for parts + * with overline, for chapters + = for sections + - for subsections + ^ for sub-subsections + " for paragraphs + +******* +Version +******* + +|version| + diff --git a/common/include/common/compilers.h b/common/include/common/compilers.h new file mode 100644 index 0000000..e06c7e2 --- /dev/null +++ b/common/include/common/compilers.h @@ -0,0 +1,647 @@ +//===----------------------------------------------------------------------===// +// Distributed under the 3-Clause BSD License. See accompanying file LICENSE or +// copy at https://opensource.org/licenses/BSD-3-Clause). +// SPDX-License-Identifier: BSD-3-Clause +//===----------------------------------------------------------------------===// + +/*! + * \file + * + * \brief Compiler detection, diagnostics and feature helpers. + */ + +#pragma once + +// ------------------------------------------------------------------------------------------------- +// Compiler detection +// ------------------------------------------------------------------------------------------------- + +#if !defined(DOXYGEN_DOCUMENTATION_BUILD) + +#if defined(ASAP_VERSION_ENCODE_INTERNAL_) +#undef ASAP_VERSION_ENCODE_INTERNAL_ +#endif +#define ASAP_VERSION_ENCODE_INTERNAL_(major, minor, revision) \ + (((major)*1000000) + ((minor)*1000) + (revision)) + +#if defined(ASAP_CLANG_VERSION) +#undef ASAP_CLANG_VERSION +#endif +#if defined(__clang__) +#define ASAP_CLANG_VERSION \ + ASAP_VERSION_ENCODE_INTERNAL_( \ + __clang_major__, __clang_minor__, __clang_patchlevel__) +#endif + +#else // DOXYGEN_DOCUMENTATION_BUILD +/*! + * \brief Clang compiler detection macro. + * + * This macro is only defined if the compiler in use is a Clang compiler + * (including Apple Clang). + * + * Example + * ``` + * #if defined(ASAP_CLANG_VERSION) + * // Do things for clang + * #endif + * ``` + * + * \see ASAP_CLANG_VERSION_CHECK + */ +#define ASAP_CLANG_VERSION 0 +#endif // DOXYGEN_DOCUMENTATION_BUILD + +#if defined(ASAP_CLANG_VERSION_CHECK) +#undef ASAP_CLANG_VERSION_CHECK +#endif +#if defined(ASAP_CLANG_VERSION) +#define ASAP_CLANG_VERSION_CHECK(major, minor, patch) \ + (ASAP_CLANG_VERSION >= ASAP_VERSION_ENCODE_INTERNAL_(major, minor, patch)) +#else +#define ASAP_CLANG_VERSION_CHECK(major, minor, patch) (0) +#endif +/*! + * \def ASAP_CLANG_VERSION_CHECK + * + * \brief Clang compiler version check macro. + * + * This macro is always defined, and provides a convenient way to check for + * features based on the version number. + * + * \note In most cases for clang, you shouldn't test its version number, you + * should use the feature checking macros + * (https://clang.llvm.org/docs/LanguageExtensions.html#feature-checking-macros). + * + * Example + * ``` + * #if ASAP_CLANG_VERSION_CHECK(14,0,0) + * // Do a thing that only clang-14+ supports + * #endif + * ``` + * + * \return true (1) if the current compiler corresponds to the macro name, and + * the compiler version is greater than or equal to the provided values. + * + * \see ASAP_CLANG_VERSION + */ + +#if !defined(DOXYGEN_DOCUMENTATION_BUILD) + +#if defined(ASAP_MSVC_VERSION) +#undef ASAP_MSVC_VERSION +#endif +#if defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 140000000) && !defined(__ICL) +#define ASAP_MSVC_VERSION \ + ASAP_VERSION_ENCODE_INTERNAL_(_MSC_FULL_VER / 10000000, \ + (_MSC_FULL_VER % 10000000) / 100000, (_MSC_FULL_VER % 100000) / 100) +#elif defined(_MSC_FULL_VER) && !defined(__ICL) +#define ASAP_MSVC_VERSION \ + ASAP_VERSION_ENCODE_INTERNAL_(_MSC_FULL_VER / 1000000, \ + (_MSC_FULL_VER % 1000000) / 10000, (_MSC_FULL_VER % 10000) / 10) +#elif defined(_MSC_VER) && !defined(__ICL) +#define ASAP_MSVC_VERSION \ + ASAP_VERSION_ENCODE_INTERNAL_(_MSC_VER / 100, _MSC_VER % 100, 0) +#endif + +#else // DOXYGEN_DOCUMENTATION_BUILD +/*! + * \brief MSVC compiler detection macro. + * + * This macro is only defined if the compiler in use is Microsoft Visual Studio + * C++ compiler. + * + * Example + * ``` + * #if defined(ASAP_MSVC_VERSION) + * // Do things for MSVC + * #endif + * ``` + * + * \see ASAP_MSVC_VERSION_CHECK + */ +#define ASAP_MSVC_VERSION 0 +#endif // DOXYGEN_DOCUMENTATION_BUILD + +#if defined(ASAP_MSVC_VERSION_CHECK) +#undef ASAP_MSVC_VERSION_CHECK +#endif +#if !defined(ASAP_MSVC_VERSION) +#define ASAP_MSVC_VERSION_CHECK(major, minor, patch) (0) +#elif defined(_MSC_VER) && (_MSC_VER >= 1400) +#define ASAP_MSVC_VERSION_CHECK(major, minor, patch) \ + (_MSC_FULL_VER >= ((major * 10000000) + (minor * 100000) + (patch))) +#elif defined(_MSC_VER) && (_MSC_VER >= 1200) +#define ASAP_MSVC_VERSION_CHECK(major, minor, patch) \ + (_MSC_FULL_VER >= ((major * 1000000) + (minor * 10000) + (patch))) +#else +#define ASAP_MSVC_VERSION_CHECK(major, minor, patch) \ + (_MSC_VER >= ((major * 100) + (minor))) +#endif +/*! + * \def ASAP_MSVC_VERSION_CHECK + * \brief MSVC compiler version check macro. + * + * This macro is always defined, and provides a convenient way to check for + * features based on the version number. + * + * Example + * ``` + * #if ASAP_MSVC_VERSION_CHECK(16,0,0) + * // Do a thing that only MSVC 16+ supports + * #endif + * ``` + * + * \return true (1) if the current compiler corresponds to the macro name, and + * the compiler version is greater than or equal to the provided values. + * + * \see ASAP_MSVC_VERSION + */ + +#if !defined(DOXYGEN_DOCUMENTATION_BUILD) + +/* + * Note that the GNUC and GCC macros are different. Many compilers masquerade as + * GCC (by defining + * __GNUC__, __GNUC_MINOR__, and __GNUC_PATCHLEVEL__, but often do not implement + * all the features of the version of GCC they pretend to support. + * + * To work around this, the ASAP_GCC_VERSION macro is only defined for GCC, + * whereas ASAP_GNUC_VERSION will be defined whenever a compiler defines + * __GCC__. + */ + +#if defined(ASAP_GNUC_VERSION) +#undef ASAP_GNUC_VERSION +#endif +#if defined(__GNUC__) && defined(__GNUC_PATCHLEVEL__) +#define ASAP_GNUC_VERSION \ + ASAP_VERSION_ENCODE_INTERNAL_(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) +#elif defined(__GNUC__) +#define ASAP_GNUC_VERSION \ + ASAP_VERSION_ENCODE_INTERNAL_(__GNUC__, __GNUC_MINOR__, 0) +#endif + +#else // DOXYGEN_DOCUMENTATION_BUILD +/*! + * \brief GNU like compiler detection macro. + * + * This macro is only defined if the compiler in use defines `__GNUC__`, which + * may indicate that it is the real GCC compiler or a compiler masquerading GCC. + * + * Example + * ``` + * #if defined(ASAP_GNUC_VERSION) + * // Do things that work for GNU like compilers + * #endif + * ``` + * + * \see ASAP_GNUC_VERSION_CHECK + */ +#define ASAP_GNUC_VERSION 0 +#endif // DOXYGEN_DOCUMENTATION_BUILD + +#if defined(ASAP_GNUC_VERSION_CHECK) +#undef ASAP_GNUC_VERSION_CHECK +#endif +#if defined(ASAP_GNUC_VERSION) +#define ASAP_GNUC_VERSION_CHECK(major, minor, patch) \ + (ASAP_GNUC_VERSION >= ASAP_VERSION_ENCODE_INTERNAL_(major, minor, patch)) +#else +#define ASAP_GNUC_VERSION_CHECK(major, minor, patch) (0) +#endif +/*! + * \def ASAP_GNUC_VERSION_CHECK + * \brief GNU like compiler version check macro. + * + * This macro is always defined, and provides a convenient way to check for + * features based on the version number. + * + * Example + * ``` + * #if ASAP_GNUC_VERSION_CHECK(9,0,0) + * // Do a thing that only GNU-like compiler 9+ supports + * #endif + * ``` + * + * \return true (1) if the current compiler corresponds to the macro name, and + * the compiler version is greater than or equal to the provided values. + * + * \see ASAP_GNUC_VERSION + */ + +#if !defined(DOXYGEN_DOCUMENTATION_BUILD) + +#if defined(ASAP_GCC_VERSION) +#undef ASAP_GCC_VERSION +#endif +#if defined(ASAP_GNUC_VERSION) && !defined(__clang__) +#define ASAP_GCC_VERSION ASAP_GNUC_VERSION +#endif + +#else // DOXYGEN_DOCUMENTATION_BUILD +/*! + * \brief GCC compiler detection macro. + * + * This macro is only defined if the compiler in use is GNU C/C++ compiler. Any + * other compilers that masquerade as `__GNUC__` but define another known + * compiler symbol are excluded. + * + * Example + * ``` + * #if defined(ASAP_GCC_VERSION) + * // Do things for GCC/G++ + * #endif + * ``` + * + * \see ASAP_GCC_VERSION_CHECK + */ +#define ASAP_GCC_VERSION 0 +#endif // DOXYGEN_DOCUMENTATION_BUILD + +#if defined(ASAP_GCC_VERSION_CHECK) +#undef ASAP_GCC_VERSION_CHECK +#endif +#if defined(ASAP_GCC_VERSION) +#define ASAP_GCC_VERSION_CHECK(major, minor, patch) \ + (ASAP_GCC_VERSION >= ASAP_VERSION_ENCODE_INTERNAL_(major, minor, patch)) +#else +#define ASAP_GCC_VERSION_CHECK(major, minor, patch) (0) +#endif +/*! + * \def ASAP_GCC_VERSION_CHECK + * \brief GCC/G++ compiler version check macro. + * + * This macro is always defined, and provides a convenient way to check for + * features based on the version number. + * + * Example + * ``` + * #if ASAP_GCC_VERSION_CHECK(11,0,0) + * // Do a thing that only GCC 11+ supports + * #endif + * ``` + * + * \return true (1) if the current compiler corresponds to the macro name, and + * the compiler version is greater than or equal to the provided values. + * + * \see ASAP_GCC_VERSION + */ + +// ------------------------------------------------------------------------------------------------- +// Compiler attribute check +// ------------------------------------------------------------------------------------------------- + +#if defined(ASAP_HAS_ATTRIBUTE) +#undef ASAP_HAS_ATTRIBUTE +#endif +#if defined(__has_attribute) +#define ASAP_HAS_ATTRIBUTE(attribute) __has_attribute(attribute) +#else +#define ASAP_HAS_ATTRIBUTE(attribute) (0) +#endif +/*! + * \def ASAP_HAS_ATTRIBUTE + * \brief Checks for the presence of an attribute named by `attribute`. + * + * Example + * ``` + * #if ASAP_HAS_ATTRIBUTE(deprecated) // Check for an attribute + * # define DEPRECATED(msg) [[deprecated(msg)]] + * #else + * # define DEPRECATED(msg) + * #endif + * + * DEPRECATED("foo() has been deprecated") void foo(); + * ``` + * + * \return non-zero value if the attribute is supported by the compiler. + */ + +#if defined(ASAP_HAS_CPP_ATTRIBUTE) +#undef ASAP_HAS_CPP_ATTRIBUTE +#endif +#if defined(__has_cpp_attribute) && defined(__cplusplus) +#define ASAP_HAS_CPP_ATTRIBUTE(attribute) __has_cpp_attribute(attribute) +#else +#define ASAP_HAS_CPP_ATTRIBUTE(attribute) (0) +#endif +/*! + * \def ASAP_HAS_CPP_ATTRIBUTE + * \brief Checks for the presence of a C++ compiler attribute named by + * `attribute`. + * + * Example + * ``` + * #if ASAP_HAS_CPP_ATTRIBUTE(nodiscard) + * [[nodiscard]] + * #endif + * int foo(int i) { return i * i; } + * ``` + * + * \return non-zero value if the attribute is supported by the compiler. + */ + +// ------------------------------------------------------------------------------------------------- +// Compiler builtin check +// ------------------------------------------------------------------------------------------------- + +#if defined(ASAP_HAS_BUILTIN) +#undef ASAP_HAS_BUILTIN +#endif +#if defined(__has_builtin) +#define ASAP_HAS_BUILTIN(builtin) __has_builtin(builtin) +#else +#define ASAP_HAS_BUILTIN(builtin) (0) +#endif +/*! + * \def ASAP_HAS_BUILTIN + * \brief Checks for the presence of a compiler builtin function named by + * `builtin`. + * + * Example + * ``` + * #if ASAP_HAS_BUILTIN(__builtin_trap) + * __builtin_trap(); + * #else + * abort(); + * #endif + * ``` + * + * \return non-zero value if the builtin function is supported by the compiler. + */ + +// ------------------------------------------------------------------------------------------------- +// Compiler feature check +// ------------------------------------------------------------------------------------------------- + +#if defined(ASAP_HAS_FEATURE) +#undef ASAP_HAS_FEATURE +#endif +#if defined(__has_feature) +#define ASAP_HAS_FEATURE(feature) __has_feature(feature) +#else +#define ASAP_HAS_FEATURE(feature) (0) +#endif +/*! + * \def ASAP_HAS_FEATURE + * \brief Checks for the presence of a compiler feature named by `feature`. + * + * Example + * ``` + * #if ASAP_HAS_FEATURE(attribute_overloadable) || ASAP_HAS_FEATURE(blocks) + * ... + * #endif + * ``` + * + * \return non-zero value if the feature is supported by the compiler. + */ + +// ------------------------------------------------------------------------------------------------- +// Pragma +// ------------------------------------------------------------------------------------------------- + +#if (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \ + defined(__clang__) || ASAP_GCC_VERSION_CHECK(3, 0, 0) +#define ASAP_PRAGMA(value) _Pragma(#value) +#elif ASAP_MSVC_VERSION_CHECK(15, 0, 0) +#define ASAP_PRAGMA(value) __pragma(value) +#else +#define ASAP_PRAGMA(value) +#endif +/*! + * \def ASAP_PRAGMA + * + * \brief Produce a `pragma` directive for the compiler. + * + * Pragma directives specify machine-specific or operating system-specific + * compiler features. There are different ways compilers support the + * specification of pragmas and this macro will simply abstract these ways in a + * single generic way. + */ + +// ------------------------------------------------------------------------------------------------- +// Compiler diagnostics +// ------------------------------------------------------------------------------------------------- + +#if defined(ASAP_DIAGNOSTIC_PUSH) +#undef ASAP_DIAGNOSTIC_PUSH +#endif +#if defined(ASAP_DIAGNOSTIC_POP) +#undef ASAP_DIAGNOSTIC_POP +#endif +#if defined(__clang__) +#define ASAP_DIAGNOSTIC_PUSH _Pragma("clang diagnostic push") +#define ASAP_DIAGNOSTIC_POP _Pragma("clang diagnostic pop") +#elif ASAP_GCC_VERSION_CHECK(4, 6, 0) +#define ASAP_DIAGNOSTIC_PUSH _Pragma("GCC diagnostic push") +#define ASAP_DIAGNOSTIC_POP _Pragma("GCC diagnostic pop") +#elif ASAP_MSVC_VERSION_CHECK(15, 0, 0) +#define ASAP_DIAGNOSTIC_PUSH __pragma(warning(push)) +#define ASAP_DIAGNOSTIC_POP __pragma(warning(pop)) +#else +#define ASAP_DIAGNOSTIC_PUSH +#define ASAP_DIAGNOSTIC_POP +#endif +/*! + * \def ASAP_DIAGNOSTIC_PUSH + * + * \brief Remember the current state of the compiler's diagnostics. + * + * Example + * ``` + * ASAP_DIAGNOSTIC_PUSH + * #if defined(__clang__) && ASAP_HAS_WARNING("-Wunused-const-variable") + * #pragma clang diagnostic ignored "-Wunused-const-variable" + * #endif + * const char *const FOO = "foo"; + * ASAP_DIAGNOSTIC_POP + * ``` + * + * \see ASAP_DIAGNOSTIC_POP + */ + +/*! + * \def ASAP_DIAGNOSTIC_POP + * + * \brief Return the state of the compiler's diagnostics to the value from the + * last push. + * + * Example + * ``` + * ASAP_DIAGNOSTIC_PUSH + * #if defined(__clang__) && ASAP_HAS_WARNING("-Wunused-const-variable") + * #pragma clang diagnostic ignored "-Wunused-const-variable" + * #endif + * const char *const FOO = "foo"; + * ASAP_DIAGNOSTIC_POP + * ``` + * + * \see ASAP_DIAGNOSTIC_PUSH + */ + +#if defined(ASAP_HAS_WARNING) +#undef ASAP_HAS_WARNING +#endif +#if defined(__has_warning) +#define ASAP_HAS_WARNING(warning) __has_warning(warning) +#else +#define ASAP_HAS_WARNING(warning) (0) +#endif +/*! + * \def ASAP_HAS_WARNING + * \brief Checks for the presence of a compiler warning named by `warning`. + * + * Example + * ``` + * ASAP_DIAGNOSTIC_PUSH + * #if defined(__clang__) && ASAP_HAS_WARNING("-Wunused-const-variable") + * #pragma clang diagnostic ignored "-Wunused-const-variable" + * #endif + * const char *const FOO = "foo"; + * ASAP_DIAGNOSTIC_POP + * ``` + * + * \return non-zero value if the feature is supported by the compiler. + */ + +// ------------------------------------------------------------------------------------------------- +// assume, unreachable, unreachable return +// ------------------------------------------------------------------------------------------------- + +#if defined(ASAP_UNREACHABLE) +#undef ASAP_UNREACHABLE +#endif +#if defined(ASAP_UNREACHABLE_RETURN) +#undef ASAP_UNREACHABLE_RETURN +#endif +#if defined(ASAP_ASSUME) +#undef ASAP_ASSUME +#endif +#if ASAP_MSVC_VERSION_CHECK(13, 10, 0) +#define ASAP_ASSUME(expr) __assume(expr) +#elif ASAP_HAS_BUILTIN(__builtin_assume) +#define ASAP_ASSUME(expr) __builtin_assume(expr) +#endif +#if ASAP_HAS_BUILTIN(__builtin_unreachable) || ASAP_GCC_VERSION_CHECK(4, 5, 0) +#define ASAP_UNREACHABLE() __builtin_unreachable() +#elif defined(ASAP_ASSUME) +#define ASAP_UNREACHABLE() ASAP_ASSUME(0) +#endif +#if !defined(ASAP_ASSUME) +#if defined(ASAP_UNREACHABLE) +#define ASAP_ASSUME(expr) \ + ASAP_STATIC_CAST(void, ((expr) ? 1 : (ASAP_UNREACHABLE(), 1))) +#else +#define ASAP_ASSUME(expr) ASAP_STATIC_CAST(void, expr) +#endif +#endif +#if defined(ASAP_UNREACHABLE) +#define ASAP_UNREACHABLE_RETURN(value) ASAP_UNREACHABLE() +#else +#define ASAP_UNREACHABLE_RETURN(value) return (value) +#endif +#if !defined(ASAP_UNREACHABLE) +#define ASAP_UNREACHABLE() ASAP_ASSUME(0) +#endif + +/*! + * \def ASAP_UNREACHABLE + * + * \brief Inform the compiler/analyzer that the code should never be reached + * (even with invalid input). + * + * Example + * ``` + * switch (foo) { + * case BAR: + * do_something(); + * break; + * case BAZ: + * do_something_else(); + * break; + * default: + * ASAP_UNREACHABLE(); + * break; + * } + * ``` + * \see ASAP_UNREACHABLE_RETURN + */ + +/*! + * \def ASAP_UNREACHABLE_RETURN + * + * \brief Inform the compiler/analyzer that the code should never be reached or, + * for compilers which don't provide a way to provide such information, return a + * value. + * + * Example + * ``` + * static int handle_code(enum Foo code) { + * switch (code) { + * case FOO_BAR: + * case FOO_BAZ: + * case FOO_QUX: + * return 0; + * } + * + * ASAP_UNREACHABLE_RETURN(0); + * } + * ``` + * \see ASAP_UNREACHABLE + */ + +/*! + * \def ASAP_ASSUME + * + * \brief Inform the compiler/analyzer that the provided expression should + * always evaluate to a non-false value. + * + * Note that the compiler is free to assume that the expression never evaluates + * to true and therefore can elide code paths where it does evaluate to true. + * + * Example + * ``` + * unsigned sum_small(unsigned data[], size_t count) { + * __builtin_assume(count <= 4); + * unsigned sum = 0; + * for (size_t i = 0; i < count; ++i) { + * sum += data[i]; + * } + * return sum; + * } + * ``` + */ + +// ------------------------------------------------------------------------------------------------- +// fallthrough +// ------------------------------------------------------------------------------------------------- + +#if defined(ASAP_FALL_THROUGH) +#undef ASAP_FALL_THROUGH +#endif +#if ASAP_HAS_ATTRIBUTE(fallthrough) || ASAP_GCC_VERSION_CHECK(7, 0, 0) +#define ASAP_FALL_THROUGH __attribute__((__fallthrough__)) +#else +#define ASAP_FALL_THROUGH +#endif + +/*! + * \def ASAP_FALL_THROUGH + * + * \brief Explicitly tell the compiler to fall through a case in the switch + * statement. Without this, some compilers may think you accidentally omitted a + * "break;" and emit a diagnostic. + * + * Example + * ``` + * switch (foo) { + * case FOO: + * handle_foo(); + * ASAP_FALL_THROUGH; + * case BAR: + * handle_bar(); + * break; + * } + * ``` + */ diff --git a/common/include/common/config.h.in b/common/include/common/config.h.in new file mode 100644 index 0000000..3868a01 --- /dev/null +++ b/common/include/common/config.h.in @@ -0,0 +1,20 @@ +//===----------------------------------------------------------------------===// +// Distributed under the 3-Clause BSD License. See accompanying file LICENSE or +// copy at https://opensource.org/licenses/BSD-3-Clause). +// SPDX-License-Identifier: BSD-3-Clause +//===----------------------------------------------------------------------===// + +/*! + * \file + * + * \brief Contains useful compiler definitions produced during project configure + * time. + */ + +#pragma once + +// POSIX 200112L compliance +#cmakedefine ASAP_POSIX +#if defined(ASAP_POSIX) +#define ASAP_POSIX_LEVEL 200112L +#endif diff --git a/common/include/common/flag_ops.h b/common/include/common/flag_ops.h new file mode 100644 index 0000000..ffea899 --- /dev/null +++ b/common/include/common/flag_ops.h @@ -0,0 +1,78 @@ +//===----------------------------------------------------------------------===// +// Distributed under the 3-Clause BSD License.See accompanying file LICENSE or +// copy at https://opensource.org/licenses/BSD-3-Clause). +// SPDX-License-Identifier: BSD-3-Clause +//===----------------------------------------------------------------------===// + +/*! + * \file + * + * \brief C++ template API for bit flags manipulation. + */ + +#pragma once + +#include <type_traits> + +namespace asap { + +/*! + * @brief Set bits in a mask based on the bits set in flags. + * + * @param[in,out] mask the bitset mask to be changed. + * @param[in] flags the flags to set in the mask. May contain one or more + * bits to set. + */ +template <typename T, + typename std::enable_if<std::disjunction<std::is_arithmetic<T>, + std::is_enum<T>>::value>::type * = nullptr> +void FlagSet(T &mask, T flags) { + mask |= flags; +} + +/*! + * @brief Clear bits in a mask based on the bits set in flags. + * + * @param[in,out] mask the bitset mask to be changed. + * @param[in] flags the flags to clear in the mask. May contain one or more + * bits to clear. + */ +template <typename T, + typename std::enable_if<std::disjunction<std::is_arithmetic<T>, + std::is_enum<T>>::value>::type * = nullptr> +void FlagClear(T &mask, T flags) { + mask &= ~flags; +} + +/*! + * @brief Flip bits ('0' to '1' and '1' to '0') in a mask based on the bits set + * in flags. + * + * @param[in,out] mask the bitset mask to be changed. + * @param[in] flags the flags to flip in the mask. May contain one or more + * bits to set. + */ +template <typename T, + typename std::enable_if<std::disjunction<std::is_arithmetic<T>, + std::is_enum<T>>::value>::type * = nullptr> +void FlagFlip(T &mask, T flags) { + mask ^= flags; +} + +/*! + * @brief Check if the bits set in `flags` are also set in `mask`. + * + * @param[in] mask the bitset mask to be tested. + * @param[in] flags the flags to check in the mask. May contain one or more + * bits to set. + * + * @return \b true if the flags are set in mask; otherwise \b false; + */ +template <typename T, + typename std::enable_if<std::disjunction<std::is_arithmetic<T>, + std::is_enum<T>>::value>::type * = nullptr> +auto FlagTest(T mask, T flags) -> bool { + return (mask & flags) == flags; +} + +} // namespace asap diff --git a/common/include/common/mixin.h b/common/include/common/mixin.h new file mode 100644 index 0000000..0b78137 --- /dev/null +++ b/common/include/common/mixin.h @@ -0,0 +1,254 @@ +//===----------------------------------------------------------------------===// +// Distributed under the 3-Clause BSD License.See accompanying file LICENSE or +// copy at https://opensource.org/licenses/BSD-3-Clause). +// SPDX-License-Identifier: BSD-3-Clause +//===----------------------------------------------------------------------===// + +/*! + * \file + * + * \brief C++ lightweight mixin support library. + * + * The idea with mix-ins is to provide a bunch of primitive classes, where each + * of them models a basic orthogonal concept, and be able to stick them together + * to compose more complex classes with just the functionality you want -- sort + * of like Lego. The primitive classes themselves are meant to be used as + * building blocks. This is extensible since later on you can add other + * primitive classes to the collection without affecting the existing ones. + * + * A technique for doing this in C++ is using templates and inheritance. The + * basic idea here is you connect these building blocks together by providing + * them via the template parameter. You then chain them together, eg. via + * typedef, to form a new type containing the functionality you want. + * + * **What's a mixin** + * + * A mixin is a template struct/class that: + * - inherits publicly from its only template parameter (the CRTP pattern). It + * is allowed to also inherit from other things, + * - has a constructor that forwards arguments its does not consume to its + * base. + * + * \code + * template<Base> + * struct my_mixin : Base { // and any other additional bases + * + * // A mixin must forward the constructor parameters it does not consume + * // add the parameters to your mixin at the front, before `rest` + * template <typename...Args> + * my_mixin(Args&&... rest) : Base(std::forward<Args>(rest)...) {} + * + * // public interface methods + * + * // private state + * } + * \endcode + * + * Mixins can have state, which should be implemented with private data members. + * Access to the state information should be provided via the public interface. + * That way, mixins are self-contained, their interface is clear and conflicts + * with other mixins are minimized. + * + * *Composing mixins* + * Mixins are added to a class using chain inheritance, where each layer + * inherits from the layer before it. At the end, the resulting class (which is + * the one we'll be using) is the most derived class and provides the interfaces + * of all mixins used to build it. + * + * One problem remains though: just like that, mixins are restricted on which + * functionality they can use from other mixins. Interoperability between mixins + * works only in the direction of the inheritance chain. For example, if we are + * using a logging mixin with a persistence mixin, then the persistence mixin + * can use the logging interface as long as it is mixed-in after the logging + * mixin. But this is not really what we want as conceptually, mixins are a set + * and we want to be able to compose them in any order we want. + * + * In order to deal with this elegantly, we automatically add a top layer in the + * chain (not a mixin), that will track the 'most derived class' (the one with + * all the mixins interfaces) and offer access to a `self` that is the result of + * casting `this` to that **most derived class**. This layer is the initial base + * in the inheritance chain and is therefore visible from any other mixin. + * + * When defining a concrete class using mixins, you don't need to do anything to + * benefit from the above quality of life improvement. Simply declare mixins as + * usual and access their interfaces via `this->self()`. We need to have + * `this->` because `self()` is a *dependent* name (i.e. depends on the template + * parameter but is not declared within the template). + * + * \snippet mixin_top_test.cpp Mixin calls other mixin + * + * The order in which you use compose the concrete class using the above mixins + * is not important, thanks to the top layer and to the trick of providing + * access to the `most derived class` via `self()`. In the example below, the + * two classes, `Concrete` and `Concrete2`, are equivalent: + * + * \snippet mixin_top_test.cpp Composition order does not matter + */ + +#pragma once + +#include <type_traits> + +/// C++ mixin support library +namespace asap::mixin { + +/*! + * \brief Top layer in the mixin chain, providing access to the `most derived + * class`, thus giving visibility to all public interfaces of all mixins in the + * chain. + * + * This is not a mixin. It's simply the top layer in the mixin chain, with the + * bottom layer being the `most derived class`. This layer is automatically + * added to the chain in order to provide access to the `most derived class` + * from within any mixin in the chain. This trick is extremely powerful and + * allows any mixin to access any public interface in another mixin or in the + * base class. Simply use `this->self()` (note that `this->` here is required + * due to the fact that `self()` is a dependent name). + * + * **Example** + * + * \snippet mixin_top_test.cpp Mixin calls other mixin + * + * The above mixins can be put together, independently of their order, as + * following: + * + * \snippet mixin_top_test.cpp Composition order does not matter + */ +template <typename MostDerived> struct MixinTop { /* not a mixin */ + using self_type = MostDerived; + // clang-format off + auto self() & -> decltype(auto) { return static_cast<self_type &>(*this); } + auto self() && -> decltype(auto) { return static_cast<self_type &&>(*this); } + auto self() const & -> decltype(auto) { return static_cast<self_type const &>(*this); } + auto self() const && -> decltype(auto) { return static_cast<self_type const &&>(*this); } + // clang-format on +}; + +#if !defined(DOXYGEN_DOCUMENTATION_BUILD) +namespace detail { +/* + * Mixing multiple mixins with multiple inheritance (the traditional CRTP + * pattern), would require complicated casting from within a mixin to access + * another mixin and sometimes would create very large size classes. Chain + * inheritance, combined with the `MixinTop` trick solves these problems. + * + * The helpers in this implementation detail block are here to simplify the + * right-folding of the mixins template parameters and the automatic chaining of + * the `MixinTop` layer. This concretely results in an inheritance chain that + * starts with the `MixinTop` and ends with the Concrete class, adding an + * inheritance level for each template parameter (a Mixin) because every Mixin + * extends its only template parameter. + */ + +template <typename Concrete, template <class> class H, + template <class> class... Tail> +struct ChainInherit { + using result = typename ChainInherit<Concrete, Tail...>::type; + using type = H<result>; +}; +// base-case +template <typename Concrete, template <class> class H> +struct ChainInherit<Concrete, H> { + using type = H<Concrete>; +}; + +template <typename Concrete, template <class> class... Mixins> +using MixinImpl = typename ChainInherit<MixinTop<Concrete>, Mixins...>::type; + +} // namespace detail +#endif // !DOXYGEN_DOCUMENTATION_BUILD + +/*! + * \brief Mixes multiple mixins in an inheritance chain starting with a + * `MixinTop` and ending with the `Concrete` class, where each layer inherits + * from the previous one. + * + * Similar to the rule we set for mixins, where a mixin's constructor must + * forward arguments it does not use to its base class, we also require the + * mixed in class to do the same. + * + * @tparam Concrete The base class to which mixins will be added. + * + * @tparam Mixins The list of mixins to be added to the Concrete base class. + * Each mixin must respect the following requirements: inherits publicly from + * its **only template parameter** and has a constructor that forwards arguments + * its does not consume to its base. + */ +template <typename Concrete, template <class> class... Mixins> +struct Mixin : detail::MixinImpl<Concrete, Mixins...> { + /*! + * \brief Constructor that forwards all its arguments to the base class. + */ + template <typename... Rest> + constexpr explicit Mixin(Rest &&...rest) + : detail::MixinImpl<Concrete, Mixins...>( + static_cast<decltype(rest)>(rest)...) { + } + + // Explicitly implement the other builtins because we have defined one + // constructor above. + + Mixin() = default; + Mixin(Mixin const &) = default; + Mixin(Mixin &&) noexcept = default; + auto operator=(Mixin const &) -> Mixin & = default; + auto operator=(Mixin &&) noexcept -> Mixin & = default; + +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wsuggest-destructor-override" +#endif + ~Mixin() = default; +#if defined(__clang__) +#pragma clang diagnostic push +#endif +}; + +/*! + * \brief Allows currying of mixin classes with multiple template parameters. + * + * + * **Example** + * + * \snippet mixin_curry_test.cpp Curry mixin with multiple parameters + */ +template <template <class...> class Mixin, typename... Args> struct Curry { + template <typename Base> using mixin = Mixin<Args..., Base>; +}; + +} // namespace asap::mixin + +/// Utility mixins +namespace asap::mixins { + +/// Implementation detail used in the `Provides` mixin. +struct Deferred { + Deferred() = delete; +}; + +/** + * \brief A mixin that provides an interface to be mixed into the composite + * class. + * + * @tparam Interface - the abstract base class to add to the interface. The rest + * are implementation details. + * + * **Example** + * + * \snippet mixin_interfaces_test.cpp Virtual interface + * + * \snippet mixin_interfaces_test.cpp Mixin provides interface + */ +template <typename Interface, typename Base = Deferred> +struct Provides : Base, Interface { + template <typename Base_> + using mixin = + typename mixin::Curry<Provides, Interface>::template mixin<Base_>; + + template <typename... Args> + constexpr explicit Provides(Args &&...args) + : Base(static_cast<decltype(args)>(args)...) { + } +}; + +} // namespace asap::mixins diff --git a/common/include/common/overload.h b/common/include/common/overload.h new file mode 100644 index 0000000..0e9565b --- /dev/null +++ b/common/include/common/overload.h @@ -0,0 +1,34 @@ +//===----------------------------------------------------------------------===// +// Distributed under the 3-Clause BSD License.See accompanying file LICENSE or +// copy at https://opensource.org/licenses/BSD-3-Clause). +// SPDX-License-Identifier: BSD-3-Clause +//===----------------------------------------------------------------------===// + +/*! + * \file + * + * \brief C++ overload pattern to enable lambda overloads when visiting + * variants. + */ + +#pragma once + +namespace asap { + +/*! + * \brief The Overload pattern allows to explicitly 'overload' lambdas and is + * particularly useful for creating visitors, e.g. for std::variant. + * + * **Example** + * + * \snippet overload_test.cpp Example Overload with default + */ +template <typename... Ts> // (7) +struct Overload : Ts... { + using Ts::operator()...; +}; +// Deduction guide only needed for C++17. C++20 can automatically create the +// template parameters out of the constructor arguments. +template <class... Ts> Overload(Ts...) -> Overload<Ts...>; + +} // namespace asap diff --git a/common/include/common/platform.h b/common/include/common/platform.h new file mode 100644 index 0000000..5e362ba --- /dev/null +++ b/common/include/common/platform.h @@ -0,0 +1,84 @@ +//===----------------------------------------------------------------------===// +// Distributed under the 3-Clause BSD License. See accompanying file LICENSE or +// copy at https://opensource.org/licenses/BSD-3-Clause). +// SPDX-License-Identifier: BSD-3-Clause +//===----------------------------------------------------------------------===// + +/*! + * \file + * + * \brief Contains compiler definitions and macros for platform detection. + */ + +#pragma once + +// ------------------------------------------------------------------------------------------------- +// Operating System detection +// ------------------------------------------------------------------------------------------------- + +// WINDOWS +#if defined(_WIN32) // defined for 32-bit and 64-bit environments +#define ASAP_WINDOWS +#if defined(__CYGWIN__) // non-POSIX CygWin +#define ASAP_WINDOWS_CYGWIN +#endif +#if defined(__MINGW32__) || defined(__MINGW64__) +#define ASAP_WINDOWS_MINGW +#endif +// Not a Windows +// UNIX-style OS +// All UNIX-style OSes define some form of the unix symbol, except for Apple. +// GCC with CygWin also defines unix symbols even when building WIN32 apps and +// this is why UNIX detection is within the #elif of _WIN32 +#elif (defined(__unix__) || defined(__unix) || \ + (defined(__APPLE__) && defined(__MACH__))) +#define ASAP_UNIX // UNIX-style OS. +// Apple OSX, iOS, Darwin +#if defined(__APPLE__) && defined(__MACH__) +#define ASAP_APPLE // Apple OSX and iOS (Darwin) +#include <TargetConditionals.h> +#if TARGET_IPHONE_SIMULATOR == 1 +#define ASAP_APPLE_IOS_SIMULATOR // iOS in Xcode simulator +#elif TARGET_OS_IPHONE == 1 +#define ASAP_APPLE_IOS // iOS on iPhone, iPad, etc. +#elif TARGET_OS_MAC == 1 +#define ASAP_APPLE_OSX // OSX +#endif +#endif +// CygWin (not WIN32) +#if defined(__CYGWIN__) +#define ASAP_CYGWIN +#endif +// Any Linux based OS, including Gnu/Linux and Android +#if defined(__linux__) +#define ASAP_LINUX +#if defined(__gnu_linux__) // Specificaly Gnu/Linux +#define ASAP_GNU_LINUX +#endif +#if defined(__ANDROID__) // Android (which also defines __linux__) +#define ASAP_ANDROID +#endif +#endif +// Solaris and SunOS +#if defined(sun) || defined(__sun) +#define ASAP_SUN +#if defined(__SVR4) || defined(__svr4__) +#define ASAP_SUN_SOLARIS // Solaris +#else +#define ASAP_SUN_SUNOS // SunOS +#endif +#endif +// HP-UX +#if defined(__hpux) +#define ASAP_HPUX +#endif +// IBM AIX +#if defined(_AIX) +#define ASAP_AIX +#endif +// BSD (DragonFly BSD, FreeBSD, OpenBSD, NetBSD) +#include <sys/param.h> +#if defined(BSD) +#define ASAP_BSD +#endif +#endif diff --git a/common/test/CMakeLists.txt b/common/test/CMakeLists.txt new file mode 100644 index 0000000..7976b08 --- /dev/null +++ b/common/test/CMakeLists.txt @@ -0,0 +1,39 @@ +# ===-----------------------------------------------------------------------===# +# Distributed under the 3-Clause BSD License. See accompanying file LICENSE or +# copy at https://opensource.org/licenses/BSD-3-Clause). +# SPDX-License-Identifier: BSD-3-Clause +# ===-----------------------------------------------------------------------===# + +# ============================================================================== +# Build instructions +# ============================================================================== + +set(MAIN_TEST_TARGET_NAME ${MODULE_TARGET_NAME}_test) + +asap_add_test( + ${MAIN_TEST_TARGET_NAME} + UNIT_TEST + VALGRIND_MEMCHECK + SRCS + "flag_ops_test.cpp" + "overload_test.cpp" + "main.cpp" + LINK + asap::common + gtest_main + gmock_main + COMMENT + "ASAP common unit tests") + +# Target specific compiler features +target_compile_features(${MAIN_TEST_TARGET_NAME} PRIVATE cxx_constexpr) + +gtest_discover_tests(${MAIN_TEST_TARGET_NAME}) + +# Add support for (optional) code quality tools +asap_add_sanitizers(${MAIN_TEST_TARGET_NAME}) +swift_add_valgrind_massif(${MAIN_TEST_TARGET_NAME}) +swift_add_valgrind_callgrind(${MAIN_TEST_TARGET_NAME}) + +# Also build the tests programs in subdirectories +add_subdirectory(mixin) diff --git a/common/test/flag_ops_test.cpp b/common/test/flag_ops_test.cpp new file mode 100644 index 0000000..4e36a96 --- /dev/null +++ b/common/test/flag_ops_test.cpp @@ -0,0 +1,148 @@ +//===----------------------------------------------------------------------===// +// Distributed under the 3-Clause BSD License. See accompanying file LICENSE or +// copy at https://opensource.org/licenses/BSD-3-Clause). +// SPDX-License-Identifier: BSD-3-Clause +//===----------------------------------------------------------------------===// + +#include "common/flag_ops.h" + +#include <common/compilers.h> + +#include <gmock/gmock.h> +#include <gtest/gtest.h> + +#include <cstdint> + +// Disable compiler and linter warnings originating from the unit test framework +// and for which we cannot do anything. Additionally every TEST or TEST_X macro +// usage must be preceded by a '// NOLINTNEXTLINE'. +ASAP_DIAGNOSTIC_PUSH +#if defined(__clang__) && ASAP_HAS_WARNING("-Wused-but-marked-unused") +#pragma clang diagnostic ignored "-Wused-but-marked-unused" +#pragma clang diagnostic ignored "-Wglobal-constructors" +#pragma clang diagnostic ignored "-Wunused-member-function" +#endif +// NOLINTBEGIN(used-but-marked-unused) + +using ::testing::Eq; +using ::testing::IsTrue; +using ::testing::Ne; + +namespace asap { + +namespace { + +// NOLINTNEXTLINE +TEST(FlagOps, SetSingleBit) { + constexpr auto TEST_MASK = 0x100010U; + constexpr auto TEST_FLAGS = 0x1000U; + constexpr auto RESULT_MASK = 0x101010U; + + std::uint32_t mask = TEST_MASK; + std::uint32_t flag = TEST_FLAGS; + + FlagSet(mask, flag); + // bit corresponding to flag is set + EXPECT_THAT((mask & flag), Ne(0U)); + // other bits not touched + EXPECT_THAT(mask, Eq(RESULT_MASK)); +} + +// NOLINTNEXTLINE +TEST(FlagOps, SetMultipleBits) { + constexpr auto TEST_MASK = 0x100010U; + constexpr auto TEST_FLAGS = 0x1001U; + constexpr auto RESULT_MASK = 0x101011U; + + std::uint32_t mask = TEST_MASK; + std::uint32_t flag = TEST_FLAGS; + + FlagSet(mask, flag); + EXPECT_THAT(mask, Eq(RESULT_MASK)); +} + +// NOLINTNEXTLINE +TEST(FlagOps, ClearSingleBit) { + constexpr auto TEST_MASK = 0x100010U; + constexpr auto TEST_FLAGS = 0x10U; + constexpr auto RESULT_MASK = 0x100000U; + + std::uint32_t mask = TEST_MASK; + std::uint32_t flag = TEST_FLAGS; + + FlagClear(mask, flag); + // bit corresponding to flag is cleared + EXPECT_THAT((mask & flag), Eq(0U)); + // other bits not touched + EXPECT_THAT(mask, Eq(RESULT_MASK)); +} + +// NOLINTNEXTLINE +TEST(FlagOps, ClearMultipleBits) { + constexpr auto TEST_MASK = 0x10101010U; + constexpr auto TEST_FLAGS = 0x101000U; + constexpr auto RESULT_MASK = 0x10000010U; + + std::uint32_t mask = TEST_MASK; + std::uint32_t flag = TEST_FLAGS; + + FlagClear(mask, flag); + EXPECT_THAT((mask & flag), Eq(0U)); + EXPECT_THAT(mask, Eq(RESULT_MASK)); +} + +// NOLINTNEXTLINE +TEST(FlagOps, FlipSingleBit) { + constexpr auto INITIAL_MASK = 0x10101010U; + constexpr auto TEST_FLAGS = 0x101000U; + constexpr auto RESULT_MASK = 0x10000010U; + + std::uint32_t mask = INITIAL_MASK; + std::uint32_t flag = TEST_FLAGS; + + FlagFlip(mask, flag); + // bit corresponding to flag is cleared + EXPECT_THAT((mask & flag), Eq(0U)); + // other bits not touched + EXPECT_THAT(mask, Eq(RESULT_MASK)); + + FlagFlip(mask, flag); + // bit corresponding to flag is cleared + EXPECT_THAT((mask & flag), Ne(0U)); + // other bits not touched + EXPECT_THAT(mask, Eq(INITIAL_MASK)); +} + +// NOLINTNEXTLINE +TEST(FlagOps, FlipMultipleBits) { + constexpr auto INITIAL_MASK = 0x10101010U; + constexpr auto TEST_FLAGS = 0x11111111U; + constexpr auto RESULT_MASK = 0x01010101U; + + std::uint32_t mask = INITIAL_MASK; + std::uint32_t flag = TEST_FLAGS; + + FlagFlip(mask, flag); + EXPECT_THAT(mask, Eq(RESULT_MASK)); + FlagFlip(mask, flag); + EXPECT_THAT(mask, Eq(INITIAL_MASK)); +} + +// NOLINTNEXTLINE +TEST(FlagOps, TestMultipleBits) { + constexpr auto TEST_MASK = 0x100010U; + constexpr auto TEST_FLAGS = 0x10U; + + std::uint32_t mask = TEST_MASK; + std::uint32_t flag = TEST_FLAGS; + + EXPECT_THAT(FlagTest(mask, flag), IsTrue()); + EXPECT_THAT(FlagTest(mask, mask), IsTrue()); +} + +} // namespace + +} // namespace asap + +// NOLINTEND(used-but-marked-unused) +ASAP_DIAGNOSTIC_POP diff --git a/common/test/main.cpp b/common/test/main.cpp new file mode 100644 index 0000000..885f1bd --- /dev/null +++ b/common/test/main.cpp @@ -0,0 +1,13 @@ +//===----------------------------------------------------------------------===// +// Distributed under the 3-Clause BSD License. See accompanying file LICENSE or +// copy at https://opensource.org/licenses/BSD-3-Clause). +// SPDX-License-Identifier: BSD-3-Clause +//===----------------------------------------------------------------------===// + +#include <gmock/gmock.h> + +auto main(int argc, char *argv[]) -> int { + testing::InitGoogleTest(&argc, argv); + testing::InitGoogleMock(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/common/test/mixin/CMakeLists.txt b/common/test/mixin/CMakeLists.txt new file mode 100644 index 0000000..ecc1bdd --- /dev/null +++ b/common/test/mixin/CMakeLists.txt @@ -0,0 +1,36 @@ +# ===-----------------------------------------------------------------------===# +# Distributed under the 3-Clause BSD License. See accompanying file LICENSE or +# copy at https://opensource.org/licenses/BSD-3-Clause). +# SPDX-License-Identifier: BSD-3-Clause +# ===-----------------------------------------------------------------------===# + +# ============================================================================== +# Build instructions +# ============================================================================== + +set(target_name ${MODULE_TARGET_NAME}_mixin_test) + +asap_add_test( + ${target_name} + UNIT_TEST + VALGRIND_MEMCHECK + SRCS + # sources + "mixin_chained_init_test.cpp" + "mixin_curry_test.cpp" + "mixin_interfaces_test.cpp" + "mixin_top_test.cpp" + "../main.cpp" + LINK + asap::common + gtest + gmock + COMMENT + "Mixin support library unit tests") + +gtest_discover_tests(${target_name}) + +# Add support for (optional) code quality tools +asap_add_sanitizers(${target_name}) +swift_add_valgrind_massif(${target_name}) +swift_add_valgrind_callgrind(${target_name}) diff --git a/common/test/mixin/mixin_chained_init_test.cpp b/common/test/mixin/mixin_chained_init_test.cpp new file mode 100644 index 0000000..90198c1 --- /dev/null +++ b/common/test/mixin/mixin_chained_init_test.cpp @@ -0,0 +1,74 @@ +//===----------------------------------------------------------------------===// +// Distributed under the 3-Clause BSD License.See accompanying file LICENSE or +// copy at https://opensource.org/licenses/BSD-3-Clause). +// SPDX-License-Identifier: BSD-3-Clause +//===----------------------------------------------------------------------===// + +#include "common/mixin.h" + +#include <common/compilers.h> + +#include <gmock/gmock.h> +#include <gtest/gtest.h> + +#include <sstream> + +// Disable compiler and linter warnings originating from the unit test framework +// and for which we cannot do anything. Additionally, every TEST or TEST_X macro +// usage must be preceded by a '// NOLINTNEXTLINE'. +ASAP_DIAGNOSTIC_PUSH +#if defined(__clang__) && ASAP_HAS_WARNING("-Wused-but-marked-unused") +#pragma clang diagnostic ignored "-Wused-but-marked-unused" +#pragma clang diagnostic ignored "-Wglobal-constructors" +#pragma clang diagnostic ignored "-Wunused-member-function" +#endif +// NOLINTBEGIN(used-but-marked-unused) + +using testing::Eq; +using testing::IsTrue; + +namespace asap::mixin { + +namespace { +struct Argument1 { + int a1; +}; +struct Argument2 { + int a2; +}; + +template <typename Base> struct Fragment1 : Base { + template <typename... Args> + Fragment1(Argument1 arg1, int foo, Args &&...args) + : Base(std::forward<Args>(args)...), x_{arg1.a1}, y_{foo} { + } + int x_; + int y_; +}; + +template <typename Base> struct Fragment2 : Base { + template <typename... Args> + explicit Fragment2(Argument2 arg2, Args &&...args) + : Base(std::forward<Args>(args)...), z_{arg2.a2} { + } + int z_; +}; + +// NOLINTNEXTLINE +TEST(MixinChainedInit, MixinConstructorForwardsUnusedArgumentsToBase) { + struct Composite : Mixin<Composite, Fragment1, Fragment2> { + constexpr Composite(Argument1 arg1, int foo, Argument2 arg2) + : Mixin(arg1, foo, arg2) { + } + }; + + Composite composite{Argument1{1}, 2, Argument2{3}}; + EXPECT_THAT(composite.x_ + composite.y_ + composite.z_, Eq(1 + 2 + 3)); +} + +} // namespace + +} // namespace asap::mixin + +// NOLINTEND(used-but-marked-unused) +ASAP_DIAGNOSTIC_POP diff --git a/common/test/mixin/mixin_curry_test.cpp b/common/test/mixin/mixin_curry_test.cpp new file mode 100644 index 0000000..e5f8e69 --- /dev/null +++ b/common/test/mixin/mixin_curry_test.cpp @@ -0,0 +1,61 @@ +//===----------------------------------------------------------------------===// +// Distributed under the 3-Clause BSD License.See accompanying file LICENSE or +// copy at https://opensource.org/licenses/BSD-3-Clause). +// SPDX-License-Identifier: BSD-3-Clause +//===----------------------------------------------------------------------===// + +#include "common/mixin.h" + +#include <common/compilers.h> + +#include <gmock/gmock.h> +#include <gtest/gtest.h> + +// Disable compiler and linter warnings originating from the unit test framework +// and for which we cannot do anything. Additionally, every TEST or TEST_X macro +// usage must be preceded by a '// NOLINTNEXTLINE'. +ASAP_DIAGNOSTIC_PUSH +#if defined(__clang__) && ASAP_HAS_WARNING("-Wused-but-marked-unused") +#pragma clang diagnostic ignored "-Wused-but-marked-unused" +#pragma clang diagnostic ignored "-Wglobal-constructors" +#pragma clang diagnostic ignored "-Wunused-member-function" +#endif +// NOLINTBEGIN(used-but-marked-unused) + +namespace asap::mixin { + +namespace { + +//! [Curry mixin with multiple parameters] +template <typename Arg1, typename Arg2, typename Base> struct MyMixin : Base { + /// Forwarding constructor + template <typename... Args> + constexpr explicit MyMixin(Arg1 field_1, Arg2 field_2, Args &&...args) + : Base(static_cast<decltype(args)>(args)...), field_1_{field_1}, + field_2_{field_2} { + } + +private: + Arg1 field_1_; + Arg2 field_2_; +}; + +struct MyClass : Mixin<MyClass, mixin::Curry<MyMixin, int, float>::mixin> { + template <typename... Args> + constexpr explicit MyClass(Args &&...args) + : Mixin(static_cast<decltype(args)>(args)...) { + } +}; +//! [Curry mixin with multiple parameters] + +// NOLINTNEXTLINE +TEST(MixinCurry, MultipleTemplateParameters) { + [[maybe_unused]] MyClass composite{0, 0.0F}; +} + +} // namespace + +} // namespace asap::mixin + +// NOLINTEND(used-but-marked-unused) +ASAP_DIAGNOSTIC_POP diff --git a/common/test/mixin/mixin_interfaces_test.cpp b/common/test/mixin/mixin_interfaces_test.cpp new file mode 100644 index 0000000..7e1330a --- /dev/null +++ b/common/test/mixin/mixin_interfaces_test.cpp @@ -0,0 +1,85 @@ +//===----------------------------------------------------------------------===// +// Distributed under the 3-Clause BSD License.See accompanying file LICENSE or +// copy at https://opensource.org/licenses/BSD-3-Clause). +// SPDX-License-Identifier: BSD-3-Clause +//===----------------------------------------------------------------------===// + +#include "common/mixin.h" + +#include <common/compilers.h> + +#include <gmock/gmock.h> +#include <gtest/gtest.h> + +// Disable compiler and linter warnings originating from the unit test framework +// and for which we cannot do anything. Additionally, every TEST or TEST_X macro +// usage must be preceded by a '// NOLINTNEXTLINE'. +ASAP_DIAGNOSTIC_PUSH +#if defined(__clang__) +#pragma clang diagnostic ignored "-Wused-but-marked-unused" +#pragma clang diagnostic ignored "-Wglobal-constructors" +#pragma clang diagnostic ignored "-Wunused-member-function" +#endif +// NOLINTBEGIN(used-but-marked-unused) + +using testing::NotNull; + +namespace asap::mixin { + +namespace { +// NOLINTBEGIN(cppcoreguidelines-special-member-functions, +// hicpp-special-member-functions, cppcoreguidelines-virtual-class-destructor) +//! [Virtual interface] +struct Interface { + virtual ~Interface() = default; + virtual void foo() = 0; +}; +//! [Virtual interface] + +//! [Mixin implements interface] +template <typename Base> struct ImplementsInterface : Base, Interface { + template <typename... Args> + constexpr ImplementsInterface(Args &&...args) + : Base(static_cast<decltype(args)>(args)...) { + } + + void foo() override { + // Implement foo interface in the mixin + } +}; +//! [Mixin implements interface] + +// NOLINTEND(cppcoreguidelines-special-member-functions, +// hicpp-special-member-functions, cppcoreguidelines-virtual-class-destructor) + +// NOLINTNEXTLINE +TEST(MixinInterfaces, MixinCanImplementInterface) { + struct Composite : Mixin<Composite, ImplementsInterface> {}; + // This code compiles and defines the `foo()` method + Composite composite{}; + EXPECT_THAT(dynamic_cast<Interface *>(&composite), NotNull()); + composite.foo(); +} + +// NOLINTNEXTLINE +TEST(MixinInterfaces, MixinCanProvideInterface) { + //! [Mixin provides interface] + using asap::mixins::Provides; + struct Composite final : Mixin<Composite, Provides<Interface>::mixin> { + void foo() override { + // Implement foo interface in the composite + } + }; + //! [Mixin provides interface] + // This code compiles and defines the `foo()` method + Composite composite{}; + EXPECT_THAT(dynamic_cast<Interface *>(&composite), NotNull()); + composite.foo(); +} + +} // namespace + +} // namespace asap::mixin + +// NOLINTEND(used-but-marked-unused) +ASAP_DIAGNOSTIC_POP diff --git a/common/test/mixin/mixin_top_test.cpp b/common/test/mixin/mixin_top_test.cpp new file mode 100644 index 0000000..b14c100 --- /dev/null +++ b/common/test/mixin/mixin_top_test.cpp @@ -0,0 +1,81 @@ +//===----------------------------------------------------------------------===// +// Distributed under the 3-Clause BSD License.See accompanying file LICENSE or +// copy at https://opensource.org/licenses/BSD-3-Clause). +// SPDX-License-Identifier: BSD-3-Clause +//===----------------------------------------------------------------------===// + +#include "common/mixin.h" + +#include <common/compilers.h> + +#include <gmock/gmock.h> +#include <gtest/gtest.h> + +#include <sstream> + +// Disable compiler and linter warnings originating from the unit test framework +// and for which we cannot do anything. Additionally, every TEST or TEST_X macro +// usage must be preceded by a '// NOLINTNEXTLINE'. +ASAP_DIAGNOSTIC_PUSH +#if defined(__clang__) && ASAP_HAS_WARNING("-Wused-but-marked-unused") +#pragma clang diagnostic ignored "-Wused-but-marked-unused" +#pragma clang diagnostic ignored "-Wglobal-constructors" +#pragma clang diagnostic ignored "-Wunused-member-function" +#endif +// NOLINTBEGIN(used-but-marked-unused) + +using testing::Eq; +using testing::IsTrue; + +namespace asap::mixin { + +namespace { + +//! [Mixin calls other mixin] +template <typename Base> struct WithLogging : Base { + void Log(const std::string &message) const { + out << message; + } + auto LoggerOutput() -> std::string { + return out.str(); + } + +private: + mutable std::stringstream out; +}; + +template <typename Base> struct Persistence : Base { + void Store() const { + // self() is provided by Base, so we need to mark it dependent. + // this->self() is short and obvious. + this->self().Log("storing..."); + } +}; +//! [Mixin calls other mixin] + +// NOLINTNEXTLINE +TEST(MixinSelf, UseSelfToAccessOtherMixinInterfaces) { + struct Concrete : asap::mixin::Mixin<Concrete, Persistence, WithLogging> {}; + + Concrete concrete; + concrete.Store(); + EXPECT_THAT(concrete.LoggerOutput(), Eq("storing...")); +} + +// NOLINTNEXTLINE +TEST(MixinSelf, CompositionOrderDoesNotMatter) { + //! [Composition order does not matter] + struct Concrete1 : Mixin<Concrete1, Persistence, WithLogging> {}; + struct Concrete2 : Mixin<Concrete2, WithLogging, Persistence> {}; + //! [Composition order does not matter] + + [[maybe_unused]] Concrete1 concreate_1; + [[maybe_unused]] Concrete2 concrete_2; +} + +} // namespace + +} // namespace asap::mixin + +// NOLINTEND(used-but-marked-unused) +ASAP_DIAGNOSTIC_POP diff --git a/common/test/overload_test.cpp b/common/test/overload_test.cpp new file mode 100644 index 0000000..9e536ce --- /dev/null +++ b/common/test/overload_test.cpp @@ -0,0 +1,66 @@ +//===----------------------------------------------------------------------===// +// Distributed under the 3-Clause BSD License. See accompanying file LICENSE or +// copy at https://opensource.org/licenses/BSD-3-Clause). +// SPDX-License-Identifier: BSD-3-Clause +//===----------------------------------------------------------------------===// + +#include "common/overload.h" + +#include <common/compilers.h> + +#include <gmock/gmock-matchers.h> +#include <gmock/gmock.h> +#include <gtest/gtest.h> + +#include <variant> + +// Disable compiler and linter warnings originating from the unit test framework +// and for which we cannot do anything. Additionally every TEST or TEST_X macro +// usage must be preceded by a '// NOLINTNEXTLINE'. +ASAP_DIAGNOSTIC_PUSH +#if defined(__clang__) && ASAP_HAS_WARNING("-Wused-but-marked-unused") +#pragma clang diagnostic ignored "-Wused-but-marked-unused" +#pragma clang diagnostic ignored "-Wglobal-constructors" +#pragma clang diagnostic ignored "-Wunused-member-function" +#endif +// NOLINTBEGIN(used-but-marked-unused) + +using ::testing::Eq; +using ::testing::IsTrue; +using ::testing::Ne; + +namespace asap { + +namespace { + +// NOLINTNEXTLINE +TEST(Overload, ExampleWithDefault) { + //! [Example Overload with default] + const char char_value = 5; + const int int_value = 2017; + const unsigned int uint_value = 2017; + const float float_value = 3.5F; + const double double_value = 10.4; + + std::vector<std::variant<char, float, int, unsigned int, double>> variants = { + char_value, int_value, uint_value, float_value, double_value}; + + auto TypeOfIntegral = Overload{ + [&](char value) { EXPECT_THAT(value, Eq(char_value)); }, + [&](float value) { EXPECT_THAT(value, Eq(float_value)); }, + [&](double value) { EXPECT_THAT(value, Eq(double_value)); }, + [&](auto value) { EXPECT_THAT(static_cast<int>(value), Eq(int_value)); }, + }; + + for (auto value : variants) { + std::visit(TypeOfIntegral, value); + } + //! [Example Overload with default] +} + +} // namespace + +} // namespace asap + +// NOLINTEND(used-but-marked-unused) +ASAP_DIAGNOSTIC_POP diff --git a/contract/.gitignore b/contract/.gitignore new file mode 100644 index 0000000..3f650c5 --- /dev/null +++ b/contract/.gitignore @@ -0,0 +1,2 @@ +# Generated files inside source tree +doc/conf.py diff --git a/contract/CMakeLists.txt b/contract/CMakeLists.txt new file mode 100644 index 0000000..61ed479 --- /dev/null +++ b/contract/CMakeLists.txt @@ -0,0 +1,149 @@ +# ===-----------------------------------------------------------------------===# +# Distributed under the 3-Clause BSD License. See accompanying file LICENSE or +# copy at https://opensource.org/licenses/BSD-3-Clause). +# SPDX-License-Identifier: BSD-3-Clause +# ===-----------------------------------------------------------------------===# + +# ------------------------------------------------------------------------------ +# Meta information about the this module +# ------------------------------------------------------------------------------ + +asap_declare_module( + MODULE_NAME + "contract" + DESCRIPTION + "Contract checking API" + GITHUB_REPO + "https://github.com/abdes/asap" + AUTHOR_MAINTAINER + "Abdessattar Sassi" + VERSION_MAJOR + "1" + VERSION_MINOR + "0" + VERSION_PATCH + "0") + +# ============================================================================== +# Build instructions +# ============================================================================== + +# ------------------------------------------------------------------------------ +# Main module target +# ------------------------------------------------------------------------------ + +# Hardcode `asap` in the module name as we do not want this prefix to change +# with the forked project name. +set(MODULE_TARGET_NAME "asap_${META_MODULE_NAME}") + +asap_add_library( + ${MODULE_TARGET_NAME} + STATIC + SHARED + WARNING + SOURCES + "include/contract/contract.h" + "include/contract/ut/gtest.h" + "include/contract/ut/framework.h" + # Sources + "src/contract.cpp" + "src/contract_ut.cpp") + +target_link_libraries(${MODULE_TARGET_NAME} PUBLIC asap::common) + +target_include_directories( + ${MODULE_TARGET_NAME} + PUBLIC $<INSTALL_INTERFACE:include> + $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include> + $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/include> + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) + +target_compile_features(${MODULE_TARGET_NAME} PUBLIC cxx_std_17) + +# common comes directly from asap and is usually kept as is. It is more +# convenient to simply use it as asap::common +add_library(asap::${META_MODULE_NAME} ALIAS ${MODULE_TARGET_NAME}) + +# Add support for (optional) code quality tools +asap_add_sanitizers(${MODULE_TARGET_NAME}) + +# Generate module config files for cmake and pkgconfig +#asap_create_module_config_files() + +# ------------------------------------------------------------------------------ +# Tests +# ------------------------------------------------------------------------------ + +if(ASAP_BUILD_TESTS) + add_subdirectory(test) +endif() + +# ------------------------------------------------------------------------------ +# API Documentation +# ------------------------------------------------------------------------------ + +asap_with_doxygen( + MODULE_NAME + ${MODULE_TARGET_NAME} + VERSION + ${META_MODULE_VERSION} + TITLE + "\"Contract checking API\"" + BRIEF + "\"Provides macros and implementation for the contract checking API.\"" + INPUT_PATH + "${CMAKE_CURRENT_SOURCE_DIR}/src ${CMAKE_CURRENT_SOURCE_DIR}/include") + +asap_with_sphinx(${MODULE_TARGET_NAME}) + +# ============================================================================== +# Deployment instructions +# ============================================================================== + +set(TARGETS_EXPORT_NAME "${MODULE_TARGET_NAME}Targets") +set(runtime "${MODULE_TARGET_NAME}_runtime") +set(dev "${MODULE_TARGET_NAME}_dev") + +# Library +install( + TARGETS ${MODULE_TARGET_NAME} + EXPORT "${TARGETS_EXPORT_NAME}" + COMPONENT dev + RUNTIME DESTINATION ${ASAP_INSTALL_BIN} COMPONENT ${runtime} + LIBRARY DESTINATION ${ASAP_INSTALL_SHARED} COMPONENT ${runtime} + ARCHIVE DESTINATION ${ASAP_INSTALL_LIB} COMPONENT ${dev}) + +# Header files +install( + DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/${META_MODULE_NAME} + DESTINATION ${ASAP_INSTALL_INCLUDE} + COMPONENT ${dev} + FILES_MATCHING + PATTERN "*.h") + +# Contract library Header files +install( + DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/contract + DESTINATION ${ASAP_INSTALL_INCLUDE} + COMPONENT ${dev} + FILES_MATCHING + PATTERN "*.h") + +# Generated header files +install( + DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/include/${META_MODULE_NAME} + DESTINATION ${ASAP_INSTALL_INCLUDE} + COMPONENT ${dev}) + +# Target config +install( + EXPORT ${TARGETS_EXPORT_NAME} + NAMESPACE ${META_PROJECT_NAME}:: + DESTINATION ${ASAP_INSTALL_CMAKE}/${META_MODULE_NAME} + COMPONENT ${dev}) + +# Package configuration files +install( + FILES ${CMAKE_CURRENT_BINARY_DIR}/${MODULE_TARGET_NAME}Config.cmake + ${CMAKE_CURRENT_BINARY_DIR}/${MODULE_TARGET_NAME}ConfigVersion.cmake + DESTINATION ${ASAP_INSTALL_CMAKE}/${META_MODULE_NAME}) diff --git a/contract/README.md b/contract/README.md new file mode 100644 index 0000000..0cdf2d3 --- /dev/null +++ b/contract/README.md @@ -0,0 +1,30 @@ +# Common submodule for the asap project + +See the asap project on [GitHub](https://github.com/abdes/asap) for a functional +minimal starter project that uses this submodule. + +This submodule is not intended to be used alone. Instead, refer to the asap +project for the recommended container to fully leverage this submodule and add +other libraries/executables/etc... to the top level project. + +Functionality offered by this submodule includes: + - cmake build helpers for the end-to-end lifecycle in 'cmake' subdirectory + - assertions + - logging using spdlog + - unit testing using Catch2 + - documentation using restructured text with + [sphinx](http://www.sphinx-doc.org/en/master/) + - API documentation using [doxygen](http://www.doxygen.org), translated to + reST using [breathe](https://breathe.readthedocs.io/en/latest/) + +## Getting the code + +Refer to the asap project. + +## Building + +Refer to the asap project. + +## Using + +Refer to the documentation generated from doxygen or doxygen+sphinx. diff --git a/contract/config.cmake.in b/contract/config.cmake.in new file mode 100644 index 0000000..745e77d --- /dev/null +++ b/contract/config.cmake.in @@ -0,0 +1,7 @@ +set(@MODULE_TARGET_NAME@_VERSION @META_MODULE_VERSION@) + +@PACKAGE_INIT@ + +include("${CMAKE_CURRENT_LIST_DIR}/@TARGETS_EXPORT_NAME@.cmake") + +check_required_components(@MODULE_TARGET_NAME@) diff --git a/contract/config.pc.in b/contract/config.pc.in new file mode 100644 index 0000000..7bbd13b --- /dev/null +++ b/contract/config.pc.in @@ -0,0 +1,11 @@ +prefix=@ASAP_INSTALL_PREFIX_FULL_PATH@ +libdir=${prefix}/@ASAP_INSTALL_LIB@ +includedir=${prefix}/@ASAP_INSTALL_INCLUDE@ + +Name: @MODULE_TARGET_NAME@ +URL: @META_MODULE_GITHUB_REPO@ +Description: @META_MODULE_DESCRIPTION@ +Version: @META_MODULE_VERSION@ +Requires: Microsoft.GSL +Cflags: -I${includedir} +Libs: -L${libdir} @MODULE_LINK_LIBS@ diff --git a/contract/doc/_static/favicon.ico b/contract/doc/_static/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..e2da7c884008a26f52634b2c5e2b3ade92a4705a GIT binary patch literal 178091 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 Xz};O&0NL{Rp92(S)!tW0e+u~z;o@TU literal 0 HcmV?d00001 diff --git a/contract/doc/_static/logo.png b/contract/doc/_static/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..4ca2442366da0413a602034a62330c08407e9e4f GIT binary patch literal 34593 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 literal 0 HcmV?d00001 diff --git a/contract/doc/api.rst b/contract/doc/api.rst new file mode 100644 index 0000000..41d2de8 --- /dev/null +++ b/contract/doc/api.rst @@ -0,0 +1,17 @@ +.. Structure conventions + # with overline, for parts + * with overline, for chapters + = for sections + - for subsections + ^ for sub-subsections + " for paragraphs + +************* +API reference +************* + +.. toctree:: + :maxdepth: 2 + :hidden: + + Contract checking API <api/contract> diff --git a/contract/doc/api/contract.rst b/contract/doc/api/contract.rst new file mode 100644 index 0000000..873d384 --- /dev/null +++ b/contract/doc/api/contract.rst @@ -0,0 +1,151 @@ +.. Structure conventions +# with overline, for parts +* with overline, for chapters += for sections +- for subsections +^ for sub-subsections +" for paragraphs + +***************** +Contract checking +***************** + +Contracts are usually expressed in the form of preconditions, post-conditions +and assertions. Some programming languages have built-in support for contracts. +So far, the closest in C++ is assertions. But assertions are not expressive and +are not consistent. This simple API is intended at offering some rudimentary +support of contract programming in the form of preconditions, post-conditions +and assertions with three levels of contract enforcement at build time: `off`, +`default` and `audit`, while waiting for it to become part of the C++ language +in a near future. + +Alternative libraries and references +==================================== + +- `Contract Checking in C++: A (long-term) Road Map + <http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1332r0.txt>`_. + +- `libcontract <https://github.com/alexeiz/contract>`_ Library to support + contract programming in C++11. + +- `Boost.Contract + <https://www.boost.org/doc/libs/1_78_0/libs/contract/doc/html/index.html>`_ + implements contract programming (a.k.a., Design by Contract or DbC) [1] for + the C++ programming language.. + +- `N3753 <http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3753.pdf>`_, + "Centralized Defensive-Programming Support for Narrow Contracts" proposed for + C++14 Standard. + +- `N1962 <http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n1962.html>`_, + "Proposal to add Contract Programming to C++ (revision 4)" (not adopted for + C++11 Standard). + +Contract checking macros +======================== + +There are three contract checking modes that can be set at build time by +defining the appropriate symbol. Each mode has a specific behavior for the +contract checking at runtime as described in the table below: + +.. list-table:: + :header-rows: 1 + :widths: 15 30 55 + + * - Build mode + - Symbol to be defined + - Behavior + + * - OFF + - ASAP_CONTRACT_OFF + - None of the conditions are enforced. + + * - DEFAULT + - ASAP_CONTRACT_DEFAULT + - Default mode conditions are enforced and execution will abort if the + contract is not honored. Audit mode conditions are ignored. + + * - AUDIT + - ASAP_CONTRACT_AUDIT + - All conditions are enforced and execution will abort if the contract is + not honored. + +Default mode macros +------------------- + +.. doxygendefine:: ASAP_EXPECT + +.. doxygendefine:: ASAP_ENSURE + +.. doxygendefine:: ASAP_ASSERT + +Audit mode macros +------------------- + +.. doxygendefine:: ASAP_EXPECT_AUDIT + +.. doxygendefine:: ASAP_ENSURE_AUDIT + +.. doxygendefine:: ASAP_ASSERT_AUDIT + +Violation Handler +================= + +There is a single violation handler in the system. Its implementation, however, +can be switched at runtime to install a custom handler. + +.. doxygenfunction:: GetViolationHandler + +.. doxygenclass:: asap::contract::ViolationHandler + :members: + +Unit testing contracts +====================== + +Testing contracts is tricky as violations often result in the program execution +being abruptly terminated rendering it quite difficult to test without death +test support by the testing framework. + +To simplify this situation, you can use the `contracts` helper library +which provides special macros for testing contracts without death tests. + +.. doxygendefine:: CHECK_VIOLATES_CONTRACT + +.. doxygendefine:: EXPECT_VIOLATES_CONTRACT + +.. doxygendefine:: ASSERT_VIOLATES_CONTRACT + +Example +------- + +.. code-block:: c++ + + // Some function to be tested in some .cpp file + auto TestExpectDefault(const int *ptr) -> int { + ASAP_EXPECT(ptr); + return *ptr; + } + +.. code-block:: c++ + + #include "contract/ut/framework.h" + #include "contract/ut/gtest.h" + + #include <gtest/gtest.h> + + TEST(GoogleTestDeathMacros, DefaultModeExpectDeath) { + CHECK_VIOLATES_CONTRACT(testing::TestExpectDefault(nullptr)); + } + + auto main(int argc, char **argv) -> int { + asap::contract::PrepareForTesting(); + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); + } + +Limitations +----------- + +The mechanism that allows contract checks to be tested during unit tests is +implemented with setjmp and longjmp. It uses global variables to save the stack +environment during the setjmp/longjmp which is not thread safe. diff --git a/contract/doc/conf.py.in b/contract/doc/conf.py.in new file mode 100644 index 0000000..a2516ee --- /dev/null +++ b/contract/doc/conf.py.in @@ -0,0 +1,131 @@ +# -*- coding: utf-8 -*- +# +# Configuration file for the Sphinx documentation builder. +# +# This file does only contain a selection of the most common options. For a +# full list see the documentation: +# http://www.sphinx-doc.org/en/master/config + +# -- Path setup -------------------------------------------------------------- + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +# import os +# import sys +# sys.path.insert(0, os.path.abspath('.')) + + +# -- Project information ----------------------------------------------------- + +project = u'asap/@META_MODULE_NAME@' +copyright = u'2022, The Authors' + +# The short X.Y version +version = u'@META_MODULE_VERSION@' + +rst_prolog = """ +.. |version| replace:: {0} +""".format(version) + + +# -- General configuration --------------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. +# +# needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + 'sphinx.ext.intersphinx', + 'sphinx.ext.todo', + 'sphinx.ext.coverage', + 'sphinx.ext.viewcode', + 'sphinx-prompt', + 'sphinx_copybutton', + 'breathe', +] + +# Setup the breathe extension +breathe_projects = { + "asap_@META_MODULE_NAME@": "@DOXYGEN_BUILD_DIR@/asap_@META_MODULE_NAME@/xml" +} +breathe_default_project = "asap_@META_MODULE_NAME@" + +# Tell sphinx what the primary language being documented is. +primary_domain = 'cpp' + +# Tell sphinx what the pygments highlight language should be. +highlight_language = 'cpp' + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, so +# a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ["_static"] + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. This pattern also affects +# html_static_path and html_extra_path. +exclude_patterns = ["_build", "Thumbs.db", ".DS_Store", ".sphinx"] + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +# +# source_suffix = ['.rst', '.md'] +source_suffix = '.rst' + +# The master toctree document. +master_doc = 'index' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +language = None + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + + +# -- Options for HTML output ------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = 'sphinx_book_theme' + +html_title = u'Common' +html_logo = "_static/logo.png" +html_favicon = "_static/favicon.ico" + +# If this is not None, a ‘Last updated on:’ timestamp is inserted at every page +# bottom, using the given strftime() format. The empty string is equivalent to +# '%b %d, %Y' (or a locale-dependent equivalent). +html_last_updated_fmt = None + +html_theme_options = { + "path_to_docs": "docs", + "repository_url": '@META_GITHUB_REPO@', + "use_repository_button": True, + "use_issues_button": True, + "use_edit_page_button": False, + "use_download_button": True, + "use_fullscreen_button": True, + # We don't want the default navigation bar footer to be displayed on every + # page. Mention of the book theme will be added in the home page. + "extra_navbar": "" +} + +# -- Extension configuration ------------------------------------------------- + +# -- Options for todo extension ---------------------------------------------- + +# If true, `todo` and `todoList` produce output, else they produce nothing. +todo_include_todos = True diff --git a/contract/doc/index.rst b/contract/doc/index.rst new file mode 100644 index 0000000..7ba0927 --- /dev/null +++ b/contract/doc/index.rst @@ -0,0 +1,25 @@ +########################### +ASAP common's documentation +########################### + +.. toctree:: + :maxdepth: 2 + :hidden: + + API <api> + License <license> + Version <version> + +Welcome! This is the documentation for the *common* module, part of the *asap* +project. This is a core module on which the other modules depend to get access +to low level and frequently needed features such as platform/environment +detection, assertions and C++ language features not consistently provided by +all compilers on all platforms. + +Parts of the documentation +========================== + +:doc:`API reference <api>` +------------------------------ +*check this out to see the documentation of classes, macros, etc. offered by +this module* diff --git a/contract/doc/license.rst b/contract/doc/license.rst new file mode 100644 index 0000000..6a2eab4 --- /dev/null +++ b/contract/doc/license.rst @@ -0,0 +1,14 @@ +.. Structure conventions + # with overline, for parts + * with overline, for chapters + = for sections + - for subsections + ^ for sub-subsections + " for paragraphs + +******* +License +******* + +.. include:: ../../LICENSE + diff --git a/contract/doc/version.rst b/contract/doc/version.rst new file mode 100644 index 0000000..bd1c930 --- /dev/null +++ b/contract/doc/version.rst @@ -0,0 +1,14 @@ +.. Structure conventions + # with overline, for parts + * with overline, for chapters + = for sections + - for subsections + ^ for sub-subsections + " for paragraphs + +******* +Version +******* + +|version| + diff --git a/contract/include/contract/contract.h b/contract/include/contract/contract.h new file mode 100644 index 0000000..2c8e546 --- /dev/null +++ b/contract/include/contract/contract.h @@ -0,0 +1,334 @@ +//===----------------------------------------------------------------------===// +// Distributed under the 3-Clause BSD License. See accompanying file LICENSE or +// copy at https://opensource.org/licenses/BSD-3-Clause). +// SPDX-License-Identifier: BSD-3-Clause +//===----------------------------------------------------------------------===// + +// clang-format off +/*! + * \file + * + * \brief Types and and macros used for contract checking. + * + * Behavior of the contract checking macros can be changed by setting the + * contract checking build mode to one of the values: `OFF`, `DEFAULT` or + * `AUDIT` by defining the corresponding symbol as per the table below: + * + * Build Mode | Symbol to be defined | Behavior + * ---------- | --------------------- | -------- + * OFF | ASAP_CONTRACT_OFF | None of the conditions are enforced. + * DEFAULT | ASAP_CONTRACT_DEFAULT | Default mode conditions are enforced and execution will abort if the contract is not honored. Audit mode conditions are ignored. + * AUDIT | ASAP_CONTRACT_AUDIT | All conditions are enforced and execution will abort if the contract is not honored. + */ +// clang-format on + +#pragma once + +#include <contract/asap_contract_api.h> + +#include <cstdlib> +#include <functional> + +// ----------------------------------------------------------------------------- +// Types used to implement the contract checking macros/apis +// ----------------------------------------------------------------------------- + +/// Contract checking namespace. +namespace asap::contract { + +/// Encapsulates the information related to a contract violation. +struct ASAP_CONTRACT_API Violation { + /// The name of the source file in which the violation occurred. + const char *file; + /// The line number at which the violation occurred. + size_t line; + /// The function name inside which the contract violation occurred. + const char *function; + /// The type of the violation (`precondition`, `postcondition` or + /// `assertion`). + const char *type; + /// The expression that specifies the predicate of the contract. + const char *condition; +}; + +/*! + * \brief Interface for a violation handler which can switch its implementation + * at runtime. + * + * We expect to have a single violation handler instance in the system, although + * its implementation can be changed at runtime. Therefore, the interface + * clearly deletes the copy constructors and assignment operators. + * + * \see GetViolationHandler + */ +class ASAP_CONTRACT_API ViolationHandler { +public: + /// A wrapper using std::function around violation handler implementation + /// functions. + using WrapperType = std::function<void(const Violation *)>; + + /// Constructor. + ViolationHandler() = default; + + /// Destructor. + virtual ~ViolationHandler(); + + /// Copy constructor (deleted). + ViolationHandler(const ViolationHandler &) = delete; + + /// Move constructor (default) + ViolationHandler(ViolationHandler &&) = default; + + /// Copy assignment operator (deleted) + auto operator=(const ViolationHandler &) -> ViolationHandler & = delete; + + /// Move assignment operator (default) + auto operator=(ViolationHandler &&) -> ViolationHandler & = default; + + /*! + * \brief Handle a contract violation. + * + * This method is called when a contract violation occurs. The information + * related to the violation is provided by the `violation` parameter. + * + * \note This function *may* never return. + * + * \param violation violation details. Will never be null and is allocated on + * the stack. + */ + virtual void HandleViolation(const Violation *violation) = 0; + + /*! + * \brief Swap the existing violation handler implementation with the give + * one. + * + * \param other_handler at the call time this holds the new violation handler + * implementation function. Upon return, it will hold the old violation + * handler function. + */ + virtual void SwapHandler(WrapperType &other_handler) = 0; +}; + +/*! + * \brief Obtain the single instance of the violation handler. + * + * \note This function is very lightweight and can be called as often as + * required. It is preferred to exclusively call it every time the violation + * handler is needed instead of storing the returned reference. + * + * \return a reference to the violation handler. + */ +ASAP_CONTRACT_API auto GetViolationHandler() -> ViolationHandler &; + +} // namespace asap::contract + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) + +// ----------------------------------------------------------------------------- +// Public contract checking macros +// ----------------------------------------------------------------------------- + +// --- Default mode macros +// ----------------------------------------------------------------------------- + +/*! + * \brief Defines a precondition. + * + * A precondition describes the function's expectation of its arguments and/or + * the state of other objects upon entry into the function and is usually placed + * at the start of a function body. + * + * \note The prediction is ignored if the contract checking build mode is `OFF`. + * + * \param cond an expression, that specifies the predicate of the contract. + */ +#define ASAP_EXPECT(cond) \ + INTERNAL_ASAP_CONTRACT_CREATE_EXPECT_( \ + INTERNAL_ASAP_CONTRACT_MODE_DEFAULT_, cond) + +/*! + * Defines a postcondition. + * + * A postcondition is a condition that a function should ensure for the return + * value and/or the state of objects upon exit from the function and is usually + * placed immediately before returning control to the caller of the function. + * + * \note The postcondition is ignored if the contract checking build mode is + * `OFF`. + * + * \param cond an expression, that specifies the predicate of the contract. + */ +#define ASAP_ENSURE(cond) \ + INTERNAL_ASAP_CONTRACT_CREATE_ENSURE_( \ + INTERNAL_ASAP_CONTRACT_MODE_DEFAULT_, cond) + +/*! + * \brief Defines an assertion. + * + * An assertion is a condition that should be satisfied where it appears in a + * function body and can be placed anywhere within the body of a function. + * + * \note The assertion is ignored if the contract checking build mode is `OFF`. + * + * \param cond an expression, that specifies the predicate of the contract. + */ +#define ASAP_ASSERT(cond) \ + INTERNAL_ASAP_CONTRACT_CREATE_ASSERT_( \ + INTERNAL_ASAP_CONTRACT_MODE_DEFAULT_, cond) + +// --- Audit mode macros +// ----------------------------------------------------------------------------- + +/*! + * \brief Defines a precondition for the `AUDIT` contract checking mode. + * + * A precondition describes the function's expectation of its arguments and/or + * the state of other objects upon entry into the function and is usually placed + * at the start of a function body. + * + * \note The precondition is enforced only if the contract checking build mode + * is `AUDIT`. + * + * \param cond an expression, that specifies the predicate of the contract. + */ +#define ASAP_EXPECT_AUDIT(cond) \ + INTERNAL_ASAP_CONTRACT_CREATE_EXPECT_( \ + INTERNAL_ASAP_CONTRACT_MODE_AUDIT_, cond) + +/*! + * \brief Defines a postcondition for the `AUDIT` contract checking mode. + * + * A postcondition is a condition that a function should ensure for the return + * value and/or the state of objects upon exit from the function and is usually + * placed immediately before returning control to the caller of the function. + * + * \note The postcondition is enforced only if the contract checking build mode + * is `AUDIT`. + * + * \param cond an expression, that specifies the predicate of the contract. + */ +#define ASAP_ENSURE_AUDIT(cond) \ + INTERNAL_ASAP_CONTRACT_CREATE_ENSURE_( \ + INTERNAL_ASAP_CONTRACT_MODE_AUDIT_, cond) + +/*! + * \brief Defines an assertion for the `AUDIT` contract checking mode. + * + * An assertion is a condition that should be satisfied where it appears in a + * function body and can be placed anywhere within the body of a function. + * + * \note The assertion is enforced only if the contract checking build mode is + * `AUDIT`. + * + * \param cond an expression, that specifies the predicate of the contract. + */ +#define ASAP_ASSERT_AUDIT(cond) \ + INTERNAL_ASAP_CONTRACT_CREATE_ASSERT_( \ + INTERNAL_ASAP_CONTRACT_MODE_AUDIT_, cond) + +// ----------------------------------------------------------------------------- +// Internal macros +// ----------------------------------------------------------------------------- + +// Exclude internal macros from doxygen documentation +#if !defined(DOXYGEN_DOCUMENTATION_BUILD) + +/* Helper macros */ +#define INTERNAL_ASAP_CONTRACT_CREATE_EXPECT_(check, cond) \ + check(INTERNAL_ASAP_CONTRACT_TYPE_EXPECT_, cond) + +#define INTERNAL_ASAP_CONTRACT_CREATE_ENSURE_(check, cond) \ + check(INTERNAL_ASAP_CONTRACT_TYPE_ENSURE_, cond) + +#define INTERNAL_ASAP_CONTRACT_CREATE_ASSERT_(check, cond) \ + check(INTERNAL_ASAP_CONTRACT_TYPE_ASSERT_, cond) + +/* Contract names */ +#define INTERNAL_ASAP_CONTRACT_TYPE_EXPECT_ "precondition" +#define INTERNAL_ASAP_CONTRACT_TYPE_ENSURE_ "postcondition" +#define INTERNAL_ASAP_CONTRACT_TYPE_ASSERT_ "assertion" + +#if defined(__clang__) +#if __has_builtin(__builtin_unreachable) +#define INTERNAL_ASAP_CONTRACT_HAVE_BUILTIN_UNREACHABLE 1 +#endif +#elif defined(__GNUC__) +#if __GNUC__ >= 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5) +#define INTERNAL_ASAP_CONTRACT_HAVE_BUILTIN_UNREACHABLE 1 +#endif +#endif + +#if defined(_MSVC_VER) +#define INTERNAL_ASAP_CONTRACT_HAVE_BUILTIN_ASSUME 1 +#endif + +/* Contract modes */ +#define INTERNAL_ASAP_CONTRACT_IGNORE_(type, cond) (void)sizeof((cond) ? 1 : 0) + +#define INTERNAL_ASAP_CONTRACT_CHECK_NEVER_CONTINUE_(type, cond) \ + do { \ + if (!(cond)) { \ + INTERNAL_ASAP_CONTRACT_HANDLE_VIOLATION_(type, cond); \ + abort(); \ + } \ + } while (0) + +/* Create violation handler call */ +#define INTERNAL_ASAP_CONTRACT_HANDLE_VIOLATION_(type, cond) \ + do { \ + const struct asap::contract::Violation violation = { \ + __FILE__, __LINE__, static_cast<const char *>(__func__), type, #cond}; \ + asap::contract::GetViolationHandler().HandleViolation(&violation); \ + } while (0) + +/* Build configuration options */ +#if defined ASAP_CONTRACT_OFF +#if defined ASAP_CONTRACT_DEFAULT +#error At most one of ASAP_CONTRACT_OFF, ASAP_CONTRACT_DEFAULT, ASAP_CONTRACT_AUDIT must be specified +#endif +#define INTERNAL_ASAP_CONTRACT_BUILD_ INTERNAL_ASAP_CONTRACT_BUILD_MODE_OFF_ +#elif defined ASAP_CONTRACT_DEFAULT +#if defined ASAP_CONTRACT_AUDIT +#error At most one of ASAP_CONTRACT_OFF, ASAP_CONTRACT_DEFAULT, ASAP_CONTRACT_AUDIT must be specified +#endif +#define INTERNAL_ASAP_CONTRACT_BUILD_ INTERNAL_ASAP_CONTRACT_BUILD_MODE_DEFAULT_ +#elif defined ASAP_CONTRACT_AUDIT +#define INTERNAL_ASAP_CONTRACT_BUILD_ INTERNAL_ASAP_CONTRACT_BUILD_MODE_AUDIT_ +#else /* Default build */ +#define INTERNAL_ASAP_CONTRACT_BUILD_ INTERNAL_ASAP_CONTRACT_BUILD_MODE_DEFAULT_ +#endif + +#define INTERNAL_ASAP_CONTRACT_BUILD_MODE_OFF_ 0 +#define INTERNAL_ASAP_CONTRACT_BUILD_MODE_DEFAULT_ 1 +#define INTERNAL_ASAP_CONTRACT_BUILD_MODE_AUDIT_ 2 + +/* Standard build configurations */ +#if INTERNAL_ASAP_CONTRACT_BUILD_ == INTERNAL_ASAP_CONTRACT_BUILD_MODE_OFF_ +#define INTERNAL_ASAP_CONTRACT_MODE_DEFAULT_ INTERNAL_ASAP_CONTRACT_IGNORE_ +#define INTERNAL_ASAP_CONTRACT_MODE_AUDIT_ INTERNAL_ASAP_CONTRACT_IGNORE_ +#elif INTERNAL_ASAP_CONTRACT_BUILD_ == \ + INTERNAL_ASAP_CONTRACT_BUILD_MODE_DEFAULT_ +#define INTERNAL_ASAP_CONTRACT_MODE_DEFAULT_ \ + INTERNAL_ASAP_CONTRACT_CHECK_NEVER_CONTINUE_ +#define INTERNAL_ASAP_CONTRACT_MODE_AUDIT_ INTERNAL_ASAP_CONTRACT_IGNORE_ +#elif INTERNAL_ASAP_CONTRACT_BUILD_ == INTERNAL_ASAP_CONTRACT_BUILD_MODE_AUDIT_ +#define INTERNAL_ASAP_CONTRACT_MODE_DEFAULT_ \ + INTERNAL_ASAP_CONTRACT_CHECK_NEVER_CONTINUE_ +#define INTERNAL_ASAP_CONTRACT_MODE_AUDIT_ \ + INTERNAL_ASAP_CONTRACT_CHECK_NEVER_CONTINUE_ +#endif + +/* Options to override standard build configurations */ +#if defined ASAP_CONTRACT_MODE_DEFAULT +#undef INTERNAL_ASAP_CONTRACT_MODE_DEFAULT_ +#define INTERNAL_ASAP_CONTRACT_MODE_DEFAULT_ ASAP_CONTRACT_MODE_DEFAULT +#endif + +#if defined ASAP_CONTRACT_MODE_AUDIT +#undef INTERNAL_ASAP_CONTRACT_MODE_AUDIT_ +#define INTERNAL_ASAP_CONTRACT_MODE_AUDIT_ ASAP_CONTRACT_MODE_AUDIT +#endif + +#endif // DOXYGEN_DOCUMENTATION_BUILD + +// NOLINTEND(cppcoreguidelines-macro-usage) diff --git a/contract/include/contract/ut/framework.h b/contract/include/contract/ut/framework.h new file mode 100644 index 0000000..f6571de --- /dev/null +++ b/contract/include/contract/ut/framework.h @@ -0,0 +1,128 @@ +//===----------------------------------------------------------------------===// +// Distributed under the 3-Clause BSD License. See accompanying file LICENSE or +// copy at https://opensource.org/licenses/BSD-3-Clause). +// SPDX-License-Identifier: BSD-3-Clause +//===----------------------------------------------------------------------===// + +/*! + * \file + * + * \brief Declaration for the unit testing support for contract checking. + */ + +#pragma once + +#include <contract/asap_contract_api.h> + +#include <csetjmp> +#include <cstdlib> +#include <cstring> + +namespace asap::contract { + +/// Verbosity level of the test violation handler. +enum class Verbosity { + /// Do not print anything + QUIET = 0, + /// Print the same information as if the default handler were running. + VERBOSE +}; + +/*! + * \brief Set the verbosity level of the test violation handler. + * + * \param verbosity a value of Verbosity::QUIET will not produce any output, + * while a value of Verbosity::VERBOSE will produce the same output than the + * default handler. + */ +void ASAP_CONTRACT_API SetVerbosity(enum Verbosity verbosity); + +/*! + * \brief Prepare the violation handler for testing. + * + * This function replaces the default violation handler with a special purpose + * handler that allows to test the contract checking assertions without the need + * for sophisticated death test features from the unit testing framework. + * + * Here is an example of its usage with the Google Test framework: + * + * ``` + * auto main(int argc, char **argv) -> int { + * asap::contract::PrepareForTesting(); + * ::testing::InitGoogleTest(&argc, argv); + * return RUN_ALL_TESTS(); + * } + * ``` + * + * \see CHECK_VIOLATES_CONTRACT + * \see EXPECT_VIOLATES_CONTRACT + * \see ASSERT_VIOLATES_CONTRACT + */ +void ASAP_CONTRACT_API PrepareForTesting(); + +namespace details { +/// Push a contract check for nested violation checks. +void ASAP_CONTRACT_API ContractCheckPush(); +/// Pop a contract check for nested violation checks. +void ASAP_CONTRACT_API ContractCheckPop(); + +/// Stack environment saved/restored with the setjmp/longjmp used to handle +/// contract violations during testing. +// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) +extern ASAP_CONTRACT_API jmp_buf jmp_env; + +} // namespace details + +} // namespace asap::contract + +#if !defined(DOXYGEN_DOCUMENTATION_BUILD) + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) +#define ASAP_CONTRACT_UT_DO_CHECK_VIOLATION_(call, check, expected, msg) \ + do { \ + jmp_buf old_jmp_buf; \ + int violation_detected = 0; \ + asap::contract::details::ContractCheckPush(); \ + /* preserve any existing setjmp environment before the macro was called */ \ + memcpy(static_cast<void *>(old_jmp_buf), \ + static_cast<void *>(asap::contract::details::jmp_env), \ + sizeof(old_jmp_buf)); \ + /* Save stack environment for restore using the longjmp call in the test \ + * handler. */ \ + /* Returns 0 after saving the stack environment. If setjmp returns because \ + * of a longjmp, */ \ + /* it returns the value argument of longjmp, or if the value argument of \ + * longjmp is 0, */ \ + /* setjmp returns 1. There's no error return. */ \ + if (!setjmp(&asap::contract::details::jmp_env[0])) { \ + call; \ + /* This will never be reached if a violation occurs. The handler will \ + * call longjmp with a non-zero return value, causing the other branch \ + * after setjmp to be */ \ + violation_detected = 0; \ + } else { \ + /* This branch is executed when longjmp from the handler returns a \ + * non-zero value indicating a violation has occurred. */ \ + violation_detected = 1; \ + } \ + check(violation_detected == (expected), msg); \ + /* restore saved setjmp environment before the macro was called */ \ + memcpy(static_cast<void *>(asap::contract::details::jmp_env), \ + static_cast<void *>(old_jmp_buf), sizeof(old_jmp_buf)); \ + asap::contract::details::ContractCheckPop(); \ + } while (0) + +#if defined(_MSC_VER) +#define ASAP_CONTRACT_UT_CHECK_VIOLATION_(call, check) \ + __pragma(warning(push)) __pragma(warning(disable : 4611)) \ + ASAP_CONTRACT_UT_DO_CHECK_VIOLATION_(call, check, 1, \ + "'" #call ";' does not violate any contract") __pragma(warning(pop)) +#else +#define ASAP_CONTRACT_UT_CHECK_VIOLATION_(call, check) \ + ASAP_CONTRACT_UT_DO_CHECK_VIOLATION_( \ + call, check, 1, "'" #call ";' does not violate any contract") +#endif + +#endif // DOXYGEN_DOCUMENTATION_BUILD + +// NOLINTEND(cppcoreguidelines-macro-usage) diff --git a/contract/include/contract/ut/gtest.h b/contract/include/contract/ut/gtest.h new file mode 100644 index 0000000..4d4ddd4 --- /dev/null +++ b/contract/include/contract/ut/gtest.h @@ -0,0 +1,95 @@ +//===----------------------------------------------------------------------===// +// Distributed under the 3-Clause BSD License. See accompanying file LICENSE or +// copy at https://opensource.org/licenses/BSD-3-Clause). +// SPDX-License-Identifier: BSD-3-Clause +//===----------------------------------------------------------------------===// + +/*! + * \file + * + * \brief Macros used with Google Test unit testing framework to test contract + * checking assertions without the need for death tests. + * + * To use, include the following call in your test main: + * ``` + * auto main(int argc, char **argv) -> int { + * asap::contract::PrepareForTesting(); + * ::testing::InitGoogleTest(&argc, argv); + * return RUN_ALL_TESTS(); + * } + * ``` + */ + +#pragma once + +#include <contract/ut/framework.h> + +#include <gtest/gtest.h> + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) + +// ------------------------------------------------------------------------------------------------- +// Public test macros +// ------------------------------------------------------------------------------------------------- + +/*! + * \brief An alias for ASSERT_VIOLATES_CONTRACT(), checks that the statement + * produces a contract violation and if not, the test is immediately terminated. + * + * If the statement does not produce a violation contract, execution is aborted. + * + * \note The implementation of this macro is not thread safe. + * + * \param call the statement to test. + * + * \see ASSERT_VIOLATES_CONTRACT + * \see EXPECT_VIOLATES_CONTRACT + */ +#define CHECK_VIOLATES_CONTRACT(call) ASSERT_VIOLATES_CONTRACT(call) + +/*! + * \brief Assert that the statement produces a contract violation. If the + * statement does not produce a contract violation contract, the test is + * immediately terminated. + * + * Use ASSERT_VIOLATES_CONTRACT when the condition must hold - if it doesn't the + * test stops right there. Use this when the remainder of the test doesn't have + * semantic meaning without this condition holding. + * + * \note The implementation of this macro is not thread safe. + * + * \param call the statement to test. + * + * \see EXPECT_VIOLATES_CONTRACT + */ +#define ASSERT_VIOLATES_CONTRACT(call) \ + ASAP_CONTRACT_UT_CHECK_VIOLATION_(call, ASAP_INTERNAL_FATAL_CHECK) + +/*! + * \brief Expect the statement to produce a contract violation. If the statement + * does not produce a contract, execution still continues. + * + * Use EXPECT when the condition should hold, but in cases where it doesn't we + * can still get value out of continuing the test. The test will still + * ultimately fail at the end, though. + * + * \note The implementation of this macro is not thread safe. + * + * \param call the statement to test. + * + * \see ASSERT_VIOLATES_CONTRACT + */ +#define EXPECT_VIOLATES_CONTRACT(call) \ + ASAP_CONTRACT_UT_CHECK_VIOLATION_(call, ASAP_INTERNAL_NON_FATAL_CHECK) + +// ------------------------------------------------------------------------------------------------- +// Internal macros +// ------------------------------------------------------------------------------------------------- + +// Exclude internal macros from doxygen documentation +#if !defined(DOXYGEN_DOCUMENTATION_BUILD) +#define ASAP_INTERNAL_FATAL_CHECK(cond, msg) ASSERT_TRUE(cond) << (msg); +#define ASAP_INTERNAL_NON_FATAL_CHECK(cond, msg) EXPECT_TRUE(cond) << (msg); +#endif // DOXYGEN_DOCUMENTATION_BUILD + +// NOLINTEND(cppcoreguidelines-macro-usage) diff --git a/contract/src/contract.cpp b/contract/src/contract.cpp new file mode 100644 index 0000000..1ccec38 --- /dev/null +++ b/contract/src/contract.cpp @@ -0,0 +1,79 @@ +//===----------------------------------------------------------------------===// +// Distributed under the 3-Clause BSD License. See accompanying file LICENSE or +// copy at https://opensource.org/licenses/BSD-3-Clause). +// SPDX-License-Identifier: BSD-3-Clause +//===----------------------------------------------------------------------===// + +/*! + * \file + * + * \brief Implementation details for the contract checking API. + */ + +#include "contract/contract.h" + +#include <common/compilers.h> + +#include <cassert> +#include <csetjmp> +#include <iostream> + +namespace asap::contract { + +// ----------------------------------------------------------------------------- +// Unit Testing stuff +// ----------------------------------------------------------------------------- + +namespace { + +void PrintViolation(const Violation *violation) { + std::cerr << violation->file << ":" << violation->line << ": in " + << violation->function << ": " << violation->type << " '" + << violation->condition << "' violated" << std::endl; +} + +[[noreturn]] void DefaultViolationHandler(const Violation *violation) { + PrintViolation(violation); + abort(); +} + +} // namespace + +// Internal implementation of the singleton violation handler. +class ViolationHandler_impl : public ViolationHandler { +public: + static inline auto instance() -> ViolationHandler & { + ASAP_DIAGNOSTIC_PUSH +#if defined(ASAP_CLANG_VERSION) + ASAP_PRAGMA(clang diagnostic ignored "-Wexit-time-destructors") +#endif + static ViolationHandler_impl instance_; + ASAP_DIAGNOSTIC_POP + return instance_; + } + + void HandleViolation(const Violation *violation) override; + void SwapHandler(WrapperType &other_handler) override; + +private: + ViolationHandler_impl() = default; + + WrapperType handler = WrapperType{DefaultViolationHandler}; +}; + +void ViolationHandler_impl::HandleViolation(const Violation *violation) { + assert(violation != nullptr); // NOLINT + handler(violation); +} + +void ViolationHandler_impl::SwapHandler(WrapperType &other_handler) { + other_handler.swap(handler); +} + +auto GetViolationHandler() -> ViolationHandler & { + return ViolationHandler_impl::instance(); +} + +ViolationHandler::~ViolationHandler() = default; + +} // namespace asap::contract diff --git a/contract/src/contract_ut.cpp b/contract/src/contract_ut.cpp new file mode 100644 index 0000000..440aef5 --- /dev/null +++ b/contract/src/contract_ut.cpp @@ -0,0 +1,82 @@ +//===----------------------------------------------------------------------===// +// Distributed under the 3-Clause BSD License. See accompanying file LICENSE or +// copy at https://opensource.org/licenses/BSD-3-Clause). +// SPDX-License-Identifier: BSD-3-Clause +//===----------------------------------------------------------------------===// + +/*! + * \file + * + * \brief Implementation details for the contract checking API. + */ + +#include "contract/contract.h" +#include "contract/ut/framework.h" + +#include <csetjmp> +#include <iostream> + +namespace asap::contract { + +// ----------------------------------------------------------------------------- +// Unit Testing stuff +// ----------------------------------------------------------------------------- + +namespace { + +void PrintViolation(const Violation *violation) { + std::cerr << violation->file << ":" << violation->line << ": in " + << violation->function << ": " << violation->type << " '" + << violation->condition << "' violated" << std::endl; +} + +// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) +int contract_check_active = 0; + +// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) +Verbosity verbosity_level = Verbosity::QUIET; + +[[noreturn]] void TestViolationHandler(const Violation *violation) { + if (contract_check_active == 0) { + std::cerr << "Unexpected contract violation:" << std::endl; + PrintViolation(violation); + abort(); + } + + if (verbosity_level == Verbosity::VERBOSE) { + PrintViolation(violation); + } + + // Restore calling environment and jump back to setjmp. Return + // -1 so that setjmp will return false for conditional test. + // NOLINTNEXTLINE + longjmp(details::jmp_env, 1); +} + +} // namespace + +namespace details { + +// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) +jmp_buf jmp_env; + +void ContractCheckPush() { + contract_check_active++; +} + +void ContractCheckPop() { + contract_check_active--; +} + +} // namespace details + +void SetVerbosity(Verbosity verbosity) { + verbosity_level = verbosity; +} + +void PrepareForTesting() { + auto test_handler = ViolationHandler::WrapperType{TestViolationHandler}; + GetViolationHandler().SwapHandler(test_handler); +} + +} // namespace asap::contract diff --git a/contract/test/CMakeLists.txt b/contract/test/CMakeLists.txt new file mode 100644 index 0000000..d9664a0 --- /dev/null +++ b/contract/test/CMakeLists.txt @@ -0,0 +1,126 @@ +# ===-----------------------------------------------------------------------===# +# Distributed under the 3-Clause BSD License. See accompanying file LICENSE or +# copy at https://opensource.org/licenses/BSD-3-Clause). +# SPDX-License-Identifier: BSD-3-Clause +# ===-----------------------------------------------------------------------===# + +# ============================================================================== +# Build instructions +# ============================================================================== + +# If running with google sanitizers, skill all these targets as they are abort +# and segmentation fault tests that do not make sense to run under sanitizers +if(ASAP_WITH_GOOGLE_ASAN + OR ASAP_WITH_GOOGLE_UBSAN + OR ASAP_WITH_GOOGLE_TSAN) + return() +endif() + +function(_remove_contract_mode target) + get_target_property(defs ${target} COMPILE_DEFINITIONS) + list(REMOVE_ITEM defs ASAP_CONTRACT_DEFAULT ASAP_CONTRACT_AUDIT + ASAP_CONTRACT_OFF) + set_property(TARGET ${target} PROPERTY COMPILE_DEFINITIONS ${defs}) +endfunction() + +# ------------------------------------------------------------------------------ +# Default mode tests +# ------------------------------------------------------------------------------ + +set(target_name ${MODULE_TARGET_NAME}_contract_default_test) + +asap_add_test( + ${target_name} + UNIT_TEST + VALGRIND_MEMCHECK + SRCS + # headers + "test_helper.h" + # sources + "contracts_default_test.cpp" + "contract_handlers_test.cpp" + "contracts_honored_test.cpp" + "test_helper.cpp" + "main.cpp" + LINK + asap::common + asap::contract + gtest + gmock + COMMENT + "Contract unit tests in DEFAULT build mode") + +# Remove any existing contract mode compiler definition and force the mode to +# DEFAULT for this target. +_remove_contract_mode(${target_name}) +target_compile_definitions(${target_name} PRIVATE ASAP_CONTRACT_DEFAULT) + +gtest_discover_tests(${target_name}) + +# ------------------------------------------------------------------------------ +# OFF mode tests +# ------------------------------------------------------------------------------ + +set(target_name ${MODULE_TARGET_NAME}_contract_off_test) + +asap_add_test( + ${target_name} + UNIT_TEST + VALGRIND_MEMCHECK + SRCS + # headers + "test_helper.h" + # sources + "contracts_off_test.cpp" + "contracts_honored_test.cpp" + "test_helper.cpp" + "main.cpp" + LINK + asap::common + asap::contract + gtest + gmock + COMMENT + "Contract unit tests in OFF build mode") + +# Remove any existing contract mode compiler definition and force the mode to +# OFF for this target. +_remove_contract_mode(${target_name}) +target_compile_definitions(${target_name} PRIVATE ASAP_CONTRACT_OFF) + +gtest_discover_tests(${target_name}) + +# ------------------------------------------------------------------------------ +# Audit mode tests +# ------------------------------------------------------------------------------ + +set(target_name ${MODULE_TARGET_NAME}_contract_audit_test) + +asap_add_test( + ${target_name} + UNIT_TEST + VALGRIND_MEMCHECK + SRCS + # headers + "test_helper.h" + # sources + "contracts_audit_test.cpp" + "contracts_honored_test.cpp" + "test_helper.cpp" + "main.cpp" + LINK + asap::common + asap::contract + gtest + gmock + COMMENT + "Contract unit tests in AUDIT build mode") + +# Remove any existing contract mode compiler definition and force the mode to +# AUDIT for this target. +_remove_contract_mode(${target_name}) +target_compile_definitions(${target_name} PRIVATE ASAP_CONTRACT_AUDIT) + +gtest_discover_tests(${target_name}) + +add_subdirectory(ut) diff --git a/contract/test/contract_handlers_test.cpp b/contract/test/contract_handlers_test.cpp new file mode 100644 index 0000000..5adaec0 --- /dev/null +++ b/contract/test/contract_handlers_test.cpp @@ -0,0 +1,91 @@ +//===----------------------------------------------------------------------===// +// Distributed under the 3-Clause BSD License. See accompanying file LICENSE or +// copy at https://opensource.org/licenses/BSD-3-Clause). +// SPDX-License-Identifier: BSD-3-Clause +//===----------------------------------------------------------------------===// + +#include <contract/contract.h> + +#include <common/compilers.h> + +#include <gmock/gmock-matchers.h> +#include <gmock/gmock.h> +#include <gtest/gtest-death-test.h> +#include <gtest/gtest.h> + +// Disable compiler and linter warnings originating from the unit test framework +// and for which we cannot do anything. Additionally, every TEST or TEST_X macro +// usage must be preceded by a '// NOLINTNEXTLINE'. +ASAP_DIAGNOSTIC_PUSH +#if defined(__clang__) && ASAP_HAS_WARNING("-Wused-but-marked-unused") +#pragma clang diagnostic ignored "-Wused-but-marked-unused" +#pragma clang diagnostic ignored "-Wglobal-constructors" +#pragma clang diagnostic ignored "-Wunused-member-function" +#endif +// NOLINTBEGIN(used-but-marked-unused) + +using ::testing::IsFalse; +using ::testing::IsNull; +using ::testing::IsTrue; +using ::testing::NotNull; + +namespace asap::contract { +namespace { + +/// The signature of functions which can be used as violation handler +/// implementation. +using FunctionType = void (*)(const Violation *); + +// NOLINTNEXTLINE +TEST(DefaultHandler, IsDefined) { + ViolationHandler::WrapperType dummy_handler; + auto &default_handler = GetViolationHandler(); + + default_handler.SwapHandler(dummy_handler); + auto *handler_function = dummy_handler.target<FunctionType>(); + ASSERT_THAT(handler_function, NotNull()); + + // Restore the handler + default_handler.SwapHandler(dummy_handler); + handler_function = dummy_handler.target<FunctionType>(); + ASSERT_THAT(handler_function, IsNull()); +} + +// NOLINTNEXTLINE +TEST(DefaultHandlerDeathTest, AbortOnViolation) { + auto &default_handler = GetViolationHandler(); + + const struct asap::contract::Violation violation = { + __FILE__, __LINE__, "my_function", "precondition", "1 == 2"}; + + // We don't want to be too stringent on what is printed when a contract + // violation is being handled. We just require that the output contains the + // word violated. NOLINTNEXTLINE + ASSERT_DEATH(default_handler.HandleViolation(&violation), "violated"); +} + +// NOLINTNEXTLINE +TEST(CustomHandler, HandleViolationCallsRegisteredHandler) { + ::testing::MockFunction<void(const Violation *)> violation_handler_mock{}; + auto function_mock = violation_handler_mock.AsStdFunction(); + auto &default_handler = GetViolationHandler(); + default_handler.SwapHandler(function_mock); + auto *handler_function = function_mock.target<FunctionType>(); + ASSERT_THAT(handler_function, NotNull()); + + const asap::contract::Violation violation = { + __FILE__, __LINE__, "my_function", "precondition", "1 == 2"}; + + EXPECT_CALL(violation_handler_mock, Call(&violation)).Times(1); + default_handler.HandleViolation(&violation); + + default_handler.SwapHandler(function_mock); + handler_function = function_mock.target<FunctionType>(); + ASSERT_THAT(handler_function, IsNull()); +} + +} // namespace +} // namespace asap::contract + +// NOLINTEND(used-but-marked-unused) +ASAP_DIAGNOSTIC_POP diff --git a/contract/test/contracts_audit_test.cpp b/contract/test/contracts_audit_test.cpp new file mode 100644 index 0000000..cda4d3c --- /dev/null +++ b/contract/test/contracts_audit_test.cpp @@ -0,0 +1,98 @@ +//===----------------------------------------------------------------------===// +// Distributed under the 3-Clause BSD License. See accompanying file LICENSE or +// copy at https://opensource.org/licenses/BSD-3-Clause). +// SPDX-License-Identifier: BSD-3-Clause +//===----------------------------------------------------------------------===// + +#include "test_helper.h" + +#include "common/compilers.h" +#include "common/platform.h" + +#include <gtest/gtest.h> + +// Disable compiler and linter warnings originating from the unit test framework +// and for which we cannot do anything. Additionally, every TEST or TEST_X macro +// usage must be preceded by a '// NOLINTNEXTLINE'. +ASAP_DIAGNOSTIC_PUSH +#if defined(__clang__) && ASAP_HAS_WARNING("-Wused-but-marked-unused") +#pragma clang diagnostic ignored "-Wused-but-marked-unused" +#pragma clang diagnostic ignored "-Wglobal-constructors" +#pragma clang diagnostic ignored "-Wunused-member-function" +#endif +// NOLINTBEGIN(used-but-marked-unused) + +namespace asap::contract { +namespace { + +// NOLINTNEXTLINE +TEST(AuditModeContractViolations, ExpectDefaultWillAbort) { +#if !defined(ASAP_WINDOWS) + // NOLINTNEXTLINE + ASSERT_EXIT(testing::TestExpectDefault(nullptr), + ::testing::KilledBySignal(SIGABRT), ".*"); +#else + ASSERT_DEATH(testing::TestExpectDefault(nullptr), ""); +#endif +} + +// NOLINTNEXTLINE +TEST(AuditModeContractViolations, EnsureDefaultWillAbort) { +#if !defined(ASAP_WINDOWS) + // NOLINTNEXTLINE + ASSERT_EXIT(testing::TestEnsureDefault(nullptr), + ::testing::KilledBySignal(SIGABRT), ".*"); +#else + ASSERT_DEATH(testing::TestEnsureDefault(nullptr), ""); +#endif +} + +// NOLINTNEXTLINE +TEST(AuditModeContractViolations, AssertDefaultWillAbort) { +#if !defined(ASAP_WINDOWS) + // NOLINTNEXTLINE + ASSERT_EXIT(testing::TestAssertDefault(nullptr), + ::testing::KilledBySignal(SIGABRT), ".*"); +#else + ASSERT_DEATH(testing::TestAssertDefault(nullptr), ""); +#endif +} + +// NOLINTNEXTLINE +TEST(AuditModeContractViolations, ExpectAuditWillAbort) { +#if !defined(ASAP_WINDOWS) + // NOLINTNEXTLINE + ASSERT_EXIT(testing::TestExpectAudit(nullptr), + ::testing::KilledBySignal(SIGABRT), ".*"); +#else + ASSERT_DEATH(testing::TestExpectAudit(nullptr), ""); +#endif +} + +// NOLINTNEXTLINE +TEST(AuditModeContractViolations, EnsureAuditWillAbort) { +#if !defined(ASAP_WINDOWS) + // NOLINTNEXTLINE + ASSERT_EXIT(testing::TestEnsureAudit(nullptr), + ::testing::KilledBySignal(SIGABRT), ".*"); +#else + ASSERT_DEATH(testing::TestEnsureAudit(nullptr), ""); +#endif +} + +// NOLINTNEXTLINE +TEST(AuditModeContractViolations, AssertAuditWillAbort) { +#if !defined(ASAP_WINDOWS) + // NOLINTNEXTLINE + ASSERT_EXIT(testing::TestAssertAudit(nullptr), + ::testing::KilledBySignal(SIGABRT), ".*"); +#else + ASSERT_DEATH(testing::TestAssertAudit(nullptr), ""); +#endif +} + +} // namespace +} // namespace asap::contract + +// NOLINTEND(used-but-marked-unused) +ASAP_DIAGNOSTIC_POP diff --git a/contract/test/contracts_default_test.cpp b/contract/test/contracts_default_test.cpp new file mode 100644 index 0000000..dc2b43f --- /dev/null +++ b/contract/test/contracts_default_test.cpp @@ -0,0 +1,98 @@ +//===----------------------------------------------------------------------===// +// Distributed under the 3-Clause BSD License. See accompanying file LICENSE or +// copy at https://opensource.org/licenses/BSD-3-Clause). +// SPDX-License-Identifier: BSD-3-Clause +//===----------------------------------------------------------------------===// + +#include "test_helper.h" + +#include "common/compilers.h" +#include "common/platform.h" + +#include <gtest/gtest.h> + +// Disable compiler and linter warnings originating from the unit test framework +// and for which we cannot do anything. Additionally, every TEST or TEST_X macro +// usage must be preceded by a '// NOLINTNEXTLINE'. +ASAP_DIAGNOSTIC_PUSH +#if defined(__clang__) && ASAP_HAS_WARNING("-Wused-but-marked-unused") +#pragma clang diagnostic ignored "-Wused-but-marked-unused" +#pragma clang diagnostic ignored "-Wglobal-constructors" +#pragma clang diagnostic ignored "-Wunused-member-function" +#endif +// NOLINTBEGIN(used-but-marked-unused) + +namespace asap::contract { +namespace { + +// NOLINTNEXTLINE +TEST(DefaultModeContractViolations, ExpectDefaultWillAbort) { +#if !defined(ASAP_WINDOWS) + // NOLINTNEXTLINE + ASSERT_EXIT(testing::TestExpectDefault(nullptr), + ::testing::KilledBySignal(SIGABRT), "precondition.*violated"); +#else + ASSERT_DEATH(testing::TestExpectDefault(nullptr), ""); +#endif +} + +// NOLINTNEXTLINE +TEST(DefaultModeContractViolations, EnsureDefaultWillAbort) { +#if !defined(ASAP_WINDOWS) + // NOLINTNEXTLINE + ASSERT_EXIT(testing::TestEnsureDefault(nullptr), + ::testing::KilledBySignal(SIGABRT), "postcondition.*violated"); +#else + ASSERT_DEATH(testing::TestEnsureDefault(nullptr), ""); +#endif +} + +// NOLINTNEXTLINE +TEST(DefaultModeContractViolations, AssertDefaultWillAbort) { +#if !defined(ASAP_WINDOWS) + // NOLINTNEXTLINE + ASSERT_EXIT(testing::TestAssertDefault(nullptr), + ::testing::KilledBySignal(SIGABRT), "assertion.*violated"); +#else + ASSERT_DEATH(testing::TestAssertDefault(nullptr), ""); +#endif +} + +// NOLINTNEXTLINE +TEST(DefaultModeContractViolations, ExpectAuditWillSegFault) { +#if !defined(ASAP_WINDOWS) + // NOLINTNEXTLINE + ASSERT_EXIT(testing::TestExpectAudit(nullptr), + ::testing::KilledBySignal(SIGSEGV), ".*"); +#else + ASSERT_DEATH(testing::TestExpectAudit(nullptr), ""); +#endif +} + +// NOLINTNEXTLINE +TEST(DefaultModeContractViolations, EnsureAuditWillSegFault) { +#if !defined(ASAP_WINDOWS) + // NOLINTNEXTLINE + ASSERT_EXIT(testing::TestEnsureAudit(nullptr), + ::testing::KilledBySignal(SIGSEGV), ".*"); +#else + ASSERT_DEATH(testing::TestEnsureAudit(nullptr), ""); +#endif +} + +// NOLINTNEXTLINE +TEST(DefaultModeContractViolations, AssertAuditWillSegFault) { +#if !defined(ASAP_WINDOWS) + // NOLINTNEXTLINE + ASSERT_EXIT(testing::TestAssertAudit(nullptr), + ::testing::KilledBySignal(SIGSEGV), ".*"); +#else + ASSERT_DEATH(testing::TestAssertAudit(nullptr), ""); +#endif +} + +} // namespace +} // namespace asap::contract + +// NOLINTEND(used-but-marked-unused) +ASAP_DIAGNOSTIC_POP diff --git a/contract/test/contracts_honored_test.cpp b/contract/test/contracts_honored_test.cpp new file mode 100644 index 0000000..967b4c4 --- /dev/null +++ b/contract/test/contracts_honored_test.cpp @@ -0,0 +1,79 @@ +//===----------------------------------------------------------------------===// +// Distributed under the 3-Clause BSD License. See accompanying file LICENSE or +// copy at https://opensource.org/licenses/BSD-3-Clause). +// SPDX-License-Identifier: BSD-3-Clause +//===----------------------------------------------------------------------===// + +#include "test_helper.h" + +#include "common/compilers.h" + +#include <gtest/gtest.h> + +// Disable compiler and linter warnings originating from the unit test framework +// and for which we cannot do anything. Additionally, every TEST or TEST_X macro +// usage must be preceded by a '// NOLINTNEXTLINE'. +ASAP_DIAGNOSTIC_PUSH +#if defined(__clang__) && ASAP_HAS_WARNING("-Wused-but-marked-unused") +#pragma clang diagnostic ignored "-Wused-but-marked-unused" +#pragma clang diagnostic ignored "-Wglobal-constructors" +#pragma clang diagnostic ignored "-Wunused-member-function" +#endif +// NOLINTBEGIN(used-but-marked-unused) + +namespace asap::contract { +namespace { + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) +#if defined(ASAP_CONTRACT_DEFAULT) +#define INTERNAL_ACT_TESTSUITE_NAME DefaultModeContrcatsHonored +#elif defined(ASAP_CONTRACT_OFF) +#define INTERNAL_ACT_TESTSUITE_NAME OffModeContrcatsHonored +#elif defined(ASAP_CONTRACT_AUDIT) +#define INTERNAL_ACT_TESTSUITE_NAME AuditModeContrcatsHonored +#else +#define INTERNAL_ACT_TESTSUITE_NAME ContrcatsHonored +#endif +// NOLINTEND(cppcoreguidelines-macro-usage) + +// NOLINTNEXTLINE +TEST(INTERNAL_ACT_TESTSUITE_NAME, Expect) { + static int value = 1; + testing::TestExpectDefault(&value); +} + +// NOLINTNEXTLINE +TEST(INTERNAL_ACT_TESTSUITE_NAME, Ensure) { + static int value = 1; + testing::TestEnsureDefault(&value); +} + +// NOLINTNEXTLINE +TEST(INTERNAL_ACT_TESTSUITE_NAME, Assert) { + static int value = 1; + testing::TestAssertDefault(&value); +} + +// NOLINTNEXTLINE +TEST(INTERNAL_ACT_TESTSUITE_NAME, ExpectAudit) { + static int value = 1; + testing::TestExpectAudit(&value); +} + +// NOLINTNEXTLINE +TEST(INTERNAL_ACT_TESTSUITE_NAME, EnsureAudit) { + static int value = 1; + testing::TestEnsureAudit(&value); +} + +// NOLINTNEXTLINE +TEST(INTERNAL_ACT_TESTSUITE_NAME, AssertAudit) { + static int value = 1; + testing::TestAssertAudit(&value); +} + +} // namespace +} // namespace asap::contract + +// NOLINTEND(used-but-marked-unused) +ASAP_DIAGNOSTIC_POP diff --git a/contract/test/contracts_off_test.cpp b/contract/test/contracts_off_test.cpp new file mode 100644 index 0000000..66a421a --- /dev/null +++ b/contract/test/contracts_off_test.cpp @@ -0,0 +1,98 @@ +//===----------------------------------------------------------------------===// +// Distributed under the 3-Clause BSD License. See accompanying file LICENSE or +// copy at https://opensource.org/licenses/BSD-3-Clause). +// SPDX-License-Identifier: BSD-3-Clause +//===----------------------------------------------------------------------===// + +#include "test_helper.h" + +#include "common/compilers.h" +#include "common/platform.h" + +#include <gtest/gtest.h> + +// Disable compiler and linter warnings originating from the unit test framework +// and for which we cannot do anything. Additionally, every TEST or TEST_X macro +// usage must be preceded by a '// NOLINTNEXTLINE'. +ASAP_DIAGNOSTIC_PUSH +#if defined(__clang__) && ASAP_HAS_WARNING("-Wused-but-marked-unused") +#pragma clang diagnostic ignored "-Wused-but-marked-unused" +#pragma clang diagnostic ignored "-Wglobal-constructors" +#pragma clang diagnostic ignored "-Wunused-member-function" +#endif +// NOLINTBEGIN(used-but-marked-unused) + +namespace asap::contract { +namespace { + +// NOLINTNEXTLINE +TEST(OffModeContractViolations, ExpectDefaultWillSegFault) { +#if !defined(ASAP_WINDOWS) + // NOLINTNEXTLINE + ASSERT_EXIT(testing::TestExpectDefault(nullptr), + ::testing::KilledBySignal(SIGSEGV), ".*"); +#else + ASSERT_DEATH(testing::TestExpectDefault(nullptr), ""); +#endif +} + +// NOLINTNEXTLINE +TEST(OffModeContractViolations, EnsureDefaultWillSegFault) { +#if !defined(ASAP_WINDOWS) + // NOLINTNEXTLINE + ASSERT_EXIT(testing::TestEnsureDefault(nullptr), + ::testing::KilledBySignal(SIGSEGV), ".*"); +#else + ASSERT_DEATH(testing::TestEnsureDefault(nullptr), ""); +#endif +} + +// NOLINTNEXTLINE +TEST(OffModeContractViolations, AssertDefaultWillSegFault) { +#if !defined(ASAP_WINDOWS) + // NOLINTNEXTLINE + ASSERT_EXIT(testing::TestAssertDefault(nullptr), + ::testing::KilledBySignal(SIGSEGV), ".*"); +#else + ASSERT_DEATH(testing::TestAssertDefault(nullptr), ""); +#endif +} + +// NOLINTNEXTLINE +TEST(OffModeContractViolations, ExpectAuditWillSegFault) { +#if !defined(ASAP_WINDOWS) + // NOLINTNEXTLINE + ASSERT_EXIT(testing::TestExpectAudit(nullptr), + ::testing::KilledBySignal(SIGSEGV), ".*"); +#else + ASSERT_DEATH(testing::TestExpectAudit(nullptr), ""); +#endif +} + +// NOLINTNEXTLINE +TEST(OffModeContractViolations, EnsureAuditWillSegFault) { +#if !defined(ASAP_WINDOWS) + // NOLINTNEXTLINE + ASSERT_EXIT(testing::TestEnsureAudit(nullptr), + ::testing::KilledBySignal(SIGSEGV), ".*"); +#else + ASSERT_DEATH(testing::TestEnsureAudit(nullptr), ""); +#endif +} + +// NOLINTNEXTLINE +TEST(OffModeContractViolations, AssertAuditWillSegFault) { +#if !defined(ASAP_WINDOWS) + // NOLINTNEXTLINE + ASSERT_EXIT(testing::TestAssertAudit(nullptr), + ::testing::KilledBySignal(SIGSEGV), ".*"); +#else + ASSERT_DEATH(testing::TestAssertAudit(nullptr), ""); +#endif +} + +} // namespace +} // namespace asap::contract + +// NOLINTEND(used-but-marked-unused) +ASAP_DIAGNOSTIC_POP diff --git a/contract/test/main.cpp b/contract/test/main.cpp new file mode 100644 index 0000000..885f1bd --- /dev/null +++ b/contract/test/main.cpp @@ -0,0 +1,13 @@ +//===----------------------------------------------------------------------===// +// Distributed under the 3-Clause BSD License. See accompanying file LICENSE or +// copy at https://opensource.org/licenses/BSD-3-Clause). +// SPDX-License-Identifier: BSD-3-Clause +//===----------------------------------------------------------------------===// + +#include <gmock/gmock.h> + +auto main(int argc, char *argv[]) -> int { + testing::InitGoogleTest(&argc, argv); + testing::InitGoogleMock(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/contract/test/test_helper.cpp b/contract/test/test_helper.cpp new file mode 100644 index 0000000..15b49d3 --- /dev/null +++ b/contract/test/test_helper.cpp @@ -0,0 +1,43 @@ +//===----------------------------------------------------------------------===// +// Distributed under the 3-Clause BSD License. See accompanying file LICENSE or +// copy at https://opensource.org/licenses/BSD-3-Clause). +// SPDX-License-Identifier: BSD-3-Clause +//===----------------------------------------------------------------------===// + +#include "../include/contract/contract.h" + +#include "test_helper.h" + +namespace asap::contract::testing { + +auto TestExpectDefault(const int *ptr) -> int { + ASAP_EXPECT(ptr); + return *ptr; +} + +auto TestEnsureDefault(const int *ptr) -> int { + ASAP_ENSURE(ptr); + return *ptr; +} + +auto TestAssertDefault(const int *ptr) -> int { + ASAP_ASSERT(ptr); + return *ptr; +} + +auto TestExpectAudit(const int *ptr) -> int { + ASAP_EXPECT_AUDIT(ptr); + return *ptr; +} + +auto TestEnsureAudit(const int *ptr) -> int { + ASAP_ENSURE_AUDIT(ptr); + return *ptr; +} + +auto TestAssertAudit(const int *ptr) -> int { + ASAP_ASSERT_AUDIT(ptr); + return *ptr; +} + +} // namespace asap::contract::testing diff --git a/contract/test/test_helper.h b/contract/test/test_helper.h new file mode 100644 index 0000000..7b892b8 --- /dev/null +++ b/contract/test/test_helper.h @@ -0,0 +1,26 @@ +//===----------------------------------------------------------------------===// +// Distributed under the 3-Clause BSD License. See accompanying file LICENSE or +// copy at https://opensource.org/licenses/BSD-3-Clause). +// SPDX-License-Identifier: BSD-3-Clause +//===----------------------------------------------------------------------===// + +#pragma once + +namespace asap::contract::testing { + +/* + * The test functions are placed in their own translation unit so that the + * compiler cannot optimize away the contract checks. Furthermore, the compiler + * cannot remove code that would be unreachable because off contract checks that + * are violated. + */ + +auto TestExpectDefault(const int *ptr) -> int; +auto TestEnsureDefault(const int *ptr) -> int; +auto TestAssertDefault(const int *ptr) -> int; + +auto TestExpectAudit(const int *ptr) -> int; +auto TestEnsureAudit(const int *ptr) -> int; +auto TestAssertAudit(const int *ptr) -> int; + +} // namespace asap::contract::testing diff --git a/contract/test/ut/CMakeLists.txt b/contract/test/ut/CMakeLists.txt new file mode 100644 index 0000000..0157ef4 --- /dev/null +++ b/contract/test/ut/CMakeLists.txt @@ -0,0 +1,51 @@ +# ===-----------------------------------------------------------------------===# +# Distributed under the 3-Clause BSD License. See accompanying file LICENSE or +# copy at https://opensource.org/licenses/BSD-3-Clause). +# SPDX-License-Identifier: BSD-3-Clause +# ===-----------------------------------------------------------------------===# + +# ============================================================================== +# Build instructions +# ============================================================================== + +# If running with google sanitizers, skill all these targets as they are abort +# and segmentation fault tests that do not make sense to run under sanitizers +if(ASAP_WITH_GOOGLE_ASAN + OR ASAP_WITH_GOOGLE_UBSAN + OR ASAP_WITH_GOOGLE_TSAN) + return() +endif() + +function(_remove_contract_mode target) + get_target_property(defs ${target} COMPILE_DEFINITIONS) + list(REMOVE_ITEM defs ASAP_CONTRACT_DEFAULT ASAP_CONTRACT_AUDIT + ASAP_CONTRACT_OFF) + set_property(TARGET ${target} PROPERTY COMPILE_DEFINITIONS ${defs}) +endfunction() + +# ------------------------------------------------------------------------------ +# Unit Test Macros +# ------------------------------------------------------------------------------ + +set(target_name ${MODULE_TARGET_NAME}_contract_ut_test) + +asap_add_test( + ${target_name} + UNIT_TEST + VALGRIND_MEMCHECK + SRCS + "contract_ut_gtest_test.cpp" + LINK + asap::common + asap::contract + gtest + gmock + COMMENT + "Contract unit test macros tests") + +# Remove any existing contract mode compiler definition and force the mode to +# DEFAULT for this target. +_remove_contract_mode(${target_name}) +target_compile_definitions(${target_name} PRIVATE ASAP_CONTRACT_DEFAULT) + +gtest_discover_tests(${target_name}) diff --git a/contract/test/ut/contract_ut_gtest_test.cpp b/contract/test/ut/contract_ut_gtest_test.cpp new file mode 100644 index 0000000..cc7b914 --- /dev/null +++ b/contract/test/ut/contract_ut_gtest_test.cpp @@ -0,0 +1,117 @@ +//===----------------------------------------------------------------------===// +// Distributed under the 3-Clause BSD License. See accompanying file LICENSE or +// copy at https://opensource.org/licenses/BSD-3-Clause). +// SPDX-License-Identifier: BSD-3-Clause +//===----------------------------------------------------------------------===// + +#include <contract/ut/framework.h> +#include <contract/ut/gtest.h> + +#include <common/compilers.h> +#include <contract/contract.h> + +#include <gmock/gmock-matchers.h> +#include <gtest/gtest-matchers.h> +#include <gtest/gtest.h> + +// Disable compiler and linter warnings originating from the unit test framework +// and for which we cannot do anything. Additionally, every TEST or TEST_X macro +// usage must be preceded by a '// NOLINTNEXTLINE'. +ASAP_DIAGNOSTIC_PUSH +#if defined(__clang__) && ASAP_HAS_WARNING("-Wused-but-marked-unused") +#pragma clang diagnostic ignored "-Wused-but-marked-unused" +#pragma clang diagnostic ignored "-Wglobal-constructors" +#pragma clang diagnostic ignored "-Wunused-member-function" +#endif +// NOLINTBEGIN(used-but-marked-unused) + +namespace asap::contract { +namespace { + +auto TestExpectDefault(const int *ptr) -> int { + ASAP_EXPECT(ptr); + return *ptr; +} + +auto TestEnsureDefault(const int *ptr) -> int { + ASAP_ENSURE(ptr); + return *ptr; +} + +auto TestAssertDefault(const int *ptr) -> int { + ASAP_ASSERT(ptr); + return *ptr; +} + +// NOLINTNEXTLINE +TEST(GoogleTestDeathMacros, DefaultModeExpectDeath) { + CHECK_VIOLATES_CONTRACT(TestExpectDefault(nullptr)); +} + +// NOLINTNEXTLINE +TEST(GoogleTestDeathMacros, DefaultModeEnsureDeath) { + CHECK_VIOLATES_CONTRACT(TestEnsureDefault(nullptr)); +} + +// NOLINTNEXTLINE +TEST(GoogleTestDeathMacros, DefaultModeAssertDeath) { + CHECK_VIOLATES_CONTRACT(TestAssertDefault(nullptr)); +} + +void NestedViolator(int *ptr) { + CHECK_VIOLATES_CONTRACT(TestAssertDefault(ptr)); + TestEnsureDefault(nullptr); +} +void Violator(int *ptr) { + CHECK_VIOLATES_CONTRACT(TestAssertDefault(ptr)); + CHECK_VIOLATES_CONTRACT(NestedViolator(ptr)); + TestExpectDefault(nullptr); +} + +// NOLINTNEXTLINE +TEST(GoogleTestDeathMacros, NestedChecks) { + CHECK_VIOLATES_CONTRACT(Violator(nullptr)); +} + +// NOLINTNEXTLINE +TEST(GoogleTestDeathMacros, VerboseTestPrintsViolationInfo) { + class ErrorOutputRedirect { + public: + explicit ErrorOutputRedirect(std::streambuf *new_buffer) + : old(std::cerr.rdbuf(new_buffer)) { + } + + ErrorOutputRedirect(const ErrorOutputRedirect &) = delete; + ErrorOutputRedirect(const ErrorOutputRedirect &&) = delete; + auto operator=(const ErrorOutputRedirect &) + -> ErrorOutputRedirect & = delete; + auto operator=(const ErrorOutputRedirect &&) + -> ErrorOutputRedirect & = delete; + + ~ErrorOutputRedirect() { + std::cerr.rdbuf(old); + } + + private: + std::streambuf *old; + }; + + std::stringstream buffer; + ErrorOutputRedirect output(buffer.rdbuf()); + SetVerbosity(Verbosity::VERBOSE); + CHECK_VIOLATES_CONTRACT(TestExpectDefault(nullptr)); + auto text = buffer.str(); + EXPECT_THAT(text, ::testing::ContainsRegex("violated")); +} + +} // namespace +} // namespace asap::contract + +auto main(int argc, char **argv) -> int { + asap::contract::PrepareForTesting(); + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} + +// NOLINTEND(used-but-marked-unused) +ASAP_DIAGNOSTIC_POP diff --git a/data/REPLACE_WITH_REAL_DATA b/data/REPLACE_WITH_REAL_DATA new file mode 100644 index 0000000..e69de29 diff --git a/doc/01-getting-started/customizing.rst b/doc/01-getting-started/customizing.rst new file mode 100644 index 0000000..0dc8063 --- /dev/null +++ b/doc/01-getting-started/customizing.rst @@ -0,0 +1,122 @@ +.. Structure conventions + # with overline, for parts + * with overline, for chapters + = for sections + - for subsections + ^ for sub-subsections + " for paragraphs + +.. _make-it-your-own: + +**************** +Make it your own +**************** + +.. |date| date:: + +Last Updated on |date| + +We've already seen in :ref:`get-the-code`, the first step of making `asap` your +own, that is renaming your fork to suit your project. There are many more things +you may want to change or adjust to suit your needs. + +Project information +=================== + +Project meta information such as its name, repo, author, etc. are located in the +main `CMakeLists.txt` file. Edit that file to change the information as depicted +below: + +.. code:: cmake + + set(META_PROJECT_NAME "my-project") + set(META_PROJECT_DESCRIPTION "A super cool project started with `asap`") + set(META_AUTHOR_ORGANIZATION "My Company") + set(META_GITHUB_REPO "https://github.com/me/my-project") + set(META_AUTHOR_DOMAIN "https://www.my-company.com") + set(META_AUTHOR_MAINTAINER "Me MySelf and I") + set(META_VERSION_MAJOR "1") + set(META_VERSION_MINOR "0") + set(META_VERSION_PATCH "0") + +For now, you don't need to change anything else in the `CMakeLists.txt` file. +Later, you may need to add submodules, add `CMake` actions, change some of the +logic in the `CMake`` build etc. as you wish. Some of these activities will be +the subject of dedicated chapters in this guide. + +Additional files that need to be adapted to your project include: + +- AUTHORS +- LICENSE +- README.md +- CHANGELOG.md (which needs to be cleared to prepare for the first time you will + release your forked project. + +Deployment package +================== + +.. todo:: + + Add instructions for customizing the content of the `deploy` directory. + +Tools configuration files +========================= + +There are several files in the project which are used to configure the various +tools used in the development lifecycle. While the provided configuration aims +at implementing the best practices, you may want to change it for personal +preference, organization policies, reasons, etc. + +The following list describes the available files and their usage: + +:.gitattributes: The .gitattributes file allows you to specify the files and + paths attributes that should be used by git when performing git actions, such + as git commit, etc. + + In other words git automatically saves the file according to the attributes + specified, every time a file is created or saved. + + One of these attributes is the eol (end of line) and is used to configure the + line endings for a file. This article will now dive deeper into how to + configure the line endings, so every developer uses the same value when using + different machines / OSes across the repository. + +:.gitignore: The purpose of gitignore files is to ensure that certain files not + tracked by Git remain untracked. + +:.versionrc.json: `standard-verion <https://github.com/conventional-changelog/standard-version>`_ + rules and configuration options. + +:.commitlintrc.json: Rules used to validate the commit message used during git + commits. + +:.cmake-format.yaml: Configuration rules for the `cmake-format + <https://github.com/cheshirekow/cmake_format>`_ tool. + +:.clang-tidy: `Clang-tidy <https://clang.llvm.org/extra/clang-tidy/>`_ rules + used for linting the C++ source code. + +:.clang-format: `Clang-format <https://clang.llvm.org/extra/clang-format/>`_ + rules used for formatting the C++ source code. + +Documentation +============= + +.. hint:: + :class: margin + + `asap` uses `Doxygen Awesome + <https://jothepro.github.io/doxygen-awesome-css/>`_ and `Executable Book + <https://sphinx-book-theme.readthedocs.io/en/latest/index.html>`_ themes for + `doxygen` and `sphinx` respectively. + + Apart from the need to document your own project files, you may also want to + change the themes used by `doxygen` or `sphinx` when generating the HTML + documentation. + +The `asap` project comes with an extensive set of documents that you can keep +part of your new project or change them as you wish. + +At a minimum, you need to replace the icon and the logo of the project with your +own. They are both located in the ```doc/_static``` directory. You also need to +change the ```README.md``` file content to adapt it to your new project. diff --git a/doc/01-getting-started/devenv.rst b/doc/01-getting-started/devenv.rst new file mode 100644 index 0000000..1988ab1 --- /dev/null +++ b/doc/01-getting-started/devenv.rst @@ -0,0 +1,554 @@ +.. Structure conventions + # with overline, for parts + * with overline, for chapters + = for sections + - for subsections + ^ for sub-subsections + " for paragraphs + +*********************** +Development environment +*********************** + +.. |date| date:: + +Last Updated on |date| + +In the past, software development comes with a lot of pain. It takes many steps +beginning from Coding, Compiling, Testing, Debugging, Build, and Deployment. All +these different phases would need many commands, scripts and different packages +to be installed and was difficult to maintain by a Developer. + +Luckily, most of these activities are now integrated or combined into single +environments known as IDE (Integrated Development Environment), which made +modern software development more productive and more fun. A typical IDE today +provides several essential features such as **syntax highlighting**, **auto-code +completion**, **search and replace**, **refactoring**, **compiler integration**, +**debugging**, **version control**, **build** and **deployment** etc. + +Developers tend to be quite opinionated when it comes to selecting the IDE, the +source code editor, automation tools, etc. Therefore, it is not the intent here +to force a certain toolkit but rather to provide a coherent project structure +and build system that can work in any environment while staying simple and +easily maintainable. If you are starting as a C++ developer then the +recommendations provided here will get you started faster. If you are an +experienced developer, it is always important to understand the rationale behind +some of the choices made in the `asap` project so that you can own the design +and adapt it to your specific needs. + +Requirements overview +===================== + +- **This is a starter project** and as such, starting a new project based on `asap` should be a + straightforward and simple task. The process should be automated when + possible, customization should be clearly indicated when relevant and by + default the starting project should work out of the box. +- **Onboarding of new developers** on the project should be easy. When it makes + sense, conventions and best practices should be implemented in the tooling, + not the docs. +- The overall goal is to create an environment for developing software + *primarily* in C++ but it is often the case that scripts, tools and even + system modules may be developed in other languages. +- Development may target multiple Operating Systems and developers may use + different desktop environments. At a minimum, it should be possible to develop + for/on **Windows 10+**, **Linux**, and **Mac OS X**. +- The build system should provide support for implementing and **automating** + most of the steps of the development lifecycle including system build, + documentation generation, deployment package creation, software quality checks + (linting, static analysis, testing, metrics). +- The tools used and automation scripts may be invoked in an **interactive + terminal** session or in a completely hands-free CI (**Continuous + Integration**) environment. + +Choices made and rationale +========================== + + +.. note:: + :class: margin + + These are the fundamental choices made in ` asap` that significantly influence + its design and impact the extent to which it achieves the goals. There are + many other small choices made which will be encountered in the build scripts, + coding standards, style, etc... which are documented in the relevant places. + None of these choices is cast in stone and they all can be changed or simply + eliminated. + +Choices were made in the design of the `asap` starter project, some of them are +hard choices, and some are more flexible. Nothing is cast in stone in software +development but some decisions will require more effort than others to change. +The table below summarizes such decisions: + +.. list-table:: + :widths: 40 60 + :header-rows: 1 + + * - Decision + - Rationale + + * - Version control: **git** + - It's just the most widely used VCS and the one that works with github, + gitlab, etc. + + **Variability**: Not recommended. + + You can use a different one if you want, but you will need to adapt the + versioning and release management tooling and slightly modify the build + scripts. + + * - Build system: **CMake** + - We need a system that works across platforms and IDEs and that can allow + us to easily reuse existing OpenSource software projects. The C++ + ecosystem is seriously lacking the diversity we can find for other + languages, but CMake emerges as the community choice for now. The `2021 + C++ ecosystem survey + <https://www.jetbrains.com/lp/devecosystem-2021/cpp/#Which-project-models-or-build-systems-do-you-regularly-use>`_ + puts CMake at the top with **55%** market share. + + **Variability**: None. + + * - Compilers: **Clang, GCC, MSVC** + - All three most used compilers are tested and work well with `asap`. + `Clang` is also used for linting and static code analysis and as such + occupies a more strategic place among compilers. + + **Variability**: Possible. + + As long as the compiler kit is supported by `CMake` it is pretty + straightforward to add support for it. Some changes may be required in the + CMake scripts to customize compiler options, flags, etc... + + * - Documentation: **Doxygen** + - `Doxygen <https://www.doxygen.nl/index.html>`_ is the de-facto standard + for C++ API documentation and is fully supported/integrated in the build + system. It can generate documentation in multiple formats, and most + importantly as XML which is used to generate the integrated project + documentation with `sphinx`. + + **Variability**: Possible. + + The use of doxygen is completely optional, and can be configured on a + per-module basis. Full customization of the documentation generated is + possible via the project-wide configuration file. A HTML theme is provided + via `Doxygen Awesome <https://jothepro.github.io/doxygen-awesome-css/>`_ + which can be easily customized or replaced if needed. + + * - Documentation: **sphinx** + - Project documentation may extend beyond APIs to include architecture, + design, guides, etc. The OpenSource ecosystem has multiple alternatives + for writing such documentation including `markdown`, `restructuredText`, + `html`, `ascii`, and many variations of them. + + `asap` recommends and has full support for **restructuredText** via + **sphinx**. `reStructuredText` is an easy-to-read, + what-you-see-is-what-you-get plaintext markup syntax and parser system. It + is useful for in-line program documentation (such as Python docstrings), + for quickly creating simple web pages, and for standalone documents. + `reStructuredText` is the default plaintext markup language used by + `Sphinx`. + + `Sphinx <https://www.sphinx-doc.org/en/master/index.html>`_ can generate documentation in many + formats including HTML and has many useful extensions to augment its functionality. Among such + extensions, `Breathe <https://breathe.readthedocs.io/en/latest/>`_ can be used to integrate + API documentation generated by `doxygen`. + + **Variability**: Possible. + + The use of sphinx totally optional and is opt-in on a per module basis. It is also possible to + use sphinx as the master documentation system while writing mixed documents using `markdown` + and other formats by adding extensions to `sphinx`. + + * - Preferred IDE: **vscode** + - It's free, OpenSource, extensible, fully functional, portable, and list + goes on... There really is no reason not to use `Visual Studio Code + <https://code.visualstudio.com/>`_. Therefore, full integration with and + support for `vscode` is built into `asap`. + + **Variability**: Possible. + + Nothing in `asap` is tied to a specific IDE. With `CMake` as the build + system, it is easy to open the project in `Visual Studio`, `CLion`, + `XCode`, `CodeBlocks`, or any other IDE. + + It is also totally acceptable to just use a terminal, and your favorite + editor such as `vi`, `Atom`, `Sublime`, etc. Everything that can be done + in the IDE can also be done via command line. + +Recommended Environment +======================= + +.. note:: + + This is a setup that takes 100% out of `asap` and will work with most + developers. Refer to the variability guidelines above if you want to make your + own setup and feel free to build an environment that allows you to **have fun + while coding**. + +The `asap` starter project offers two alternatives for the development +methodology: + +- **Develop everything on the local environment**. All necessary tools, + compilers, code, etc. reside on the developer desktop and are shared across + projects. + +- **Developing inside a Container**. This is a VS Code feature that allows + developers to package a local development tool stack into the internals of a + Docker container while also bringing the VS Code UI experience with them. + Workspace files are mounted from the local file system or copied or cloned + into the container. Extensions are installed and run inside the container, + where they have full access to the tools, platform, and file system. This + means that you can seamlessly switch your entire development environment just + by connecting to a different container. More about this can be found `here + <https://code.visualstudio.com/docs/remote/containers>`_. + +When developing for multiple platforms including Windows, Linux and Mac OS X, a +combination of both models becomes quite important so that the development can +happen locally for Windows, while the Linux version for example is done inside a +container. This is much lighter weight than using full-blown Virtual Machines +and much faster to setup. + +For the rest of this guide, we will split the environment into 3 parts: + +#. The foundation +#. Additional tools for local development +#. Additional tools for development containers + +Foundation Tools +---------------- + +Operating System +^^^^^^^^^^^^^^^^ + +.. list-table:: + :widths: 20 80 + :header-rows: 0 + + * - Recommended + - **Windows 11** + So you can have access to MSVC for Windows builds while still easily + targeting the other platforms via `containers + <https://code.visualstudio.com/docs/remote/containers>`_ and `WSL2 + <https://docs.microsoft.com/en-us/windows/wsl/compare-versions>`_. + +.. important:: + + All instructions in the remainder of this guide are assuming a Windows 10/11 + desktop. + +PowerShell (OpenSource) +^^^^^^^^^^^^^^^^^^^^^^^ + +`PowerShell <https://github.com/PowerShell/PowerShell>`_ Core is a +cross-platform (Windows, Linux, and macOS) automation and configuration +tool/framework that works well with your existing tools and is optimized for +dealing with structured data (e.g. JSON, CSV, XML, etc.), REST APIs, and object +models. It includes a command-line shell, an associated scripting language and a +framework for processing cmdlets. It's definitely better than `command.exe` and +more open/portable than the default `Windows PowerShell`. + +It can be installed by following the steps described in the `Installing PowerShell 7 +<https://docs.microsoft.com/en-us/powershell/scripting/install/installing-powershell-on-windows?view=powershell-7.2>`_ +guide. + +.. tip:: + + As an alternative, you can use `winget` to install the PowerShell package by + executing the following command ```winget install PowerShell```. + +Windows Terminal +^^^^^^^^^^^^^^^^ + +Windows Terminal is a modern terminal application for users of command-line +tools and shells like Command Prompt, PowerShell, and Windows Subsystem for +Linux (WSL). Its main features include multiple tabs, panes, Unicode and UTF-8 +character support, a GPU accelerated text rendering engine, and the ability to +create your own themes and customize text, colors, backgrounds, and shortcuts. + +Install it from the Windows App Store or by following the steps described in +`Install and get started setting up Windows Terminal +<https://docs.microsoft.com/en-us/windows/terminal/install>`_ guide. + +.. tip:: + + As an alternative, you can use `winget` to install the Windows Terminal + package by executing the following command ```winget install --id + Microsoft.WindowsTerminal```. + +.. important:: + + Configure the `Windows Terminal` to your liking but make sure you set the font + for `PowerShell` profile (Settings->Profiles/PowerShell->Appearance->Font + face) to one of the `Nerd Fonts <https://www.nerdfonts.com/>`_ e.g. **MesloLGS + Nerd Font Mono**. This is required for a great user experience with `Oh My + Posh`. + +Git +^^^ + +Git is a free and open source distributed version control system designed to +handle everything from small to very large projects with speed and efficiency. + +Git is easy to learn and has a tiny footprint with lightning fast performance. +It outclasses SCM tools like Subversion, CVS, Perforce, and ClearCase with +features like cheap local branching, convenient staging areas, and multiple +workflows. + +Install it by following the steps described in `Downloading git +<https://git-scm.com/download/win>`_ guide. + +.. tip:: + + As an alternative, you can use `winget` to install the git package by + executing the following command ```winget install --id Git.Git```. + +Oh My Posh +^^^^^^^^^^ + +`Oh My Posh <https://ohmyposh.dev/>`_ is a custom prompt engine for any shell +that has the ability to adjust the prompt string with a function or variable. It +is customizable and offers many components in the prompt that can be useful +during development such as the current git branch, the current python +environment, etc.. + +Install it by following the steps described in `Oh My Posh Installation for +PowerShell <https://ohmyposh.dev/docs/pwsh>`_. + +In summary: + +.. code-block:: powershell + + Install-Module oh-my-posh -Scope CurrentUser + Install-Module posh-git -Scope CurrentUser + +Open the PowerShell profile settings file in notepad: + +.. code-block:: powershell + + notepad $PROFILE + +Copy/Paste the following and save the file. + +.. code-block:: powershell + + Import-Module oh-my-posh + Import-Module posh-git + Set-PoshPrompt -Theme powerlevel10k_modern + +If you now open a Windows Terminal with a PowerShell +session, you should see something similar to the picture below: + +.. image:: ../_static/windows_terminal_posh.png + :width: 600 + :alt: Windows Terminal with Oh My Posh + +.. tip:: + + Feel free to customize `Oh My Posh` and change the theme to your liking. All + the documentation to do that is located at `Oh My Posh documentation + <https://ohmyposh.dev/docs/>`_. + +Visual Studio Code +^^^^^^^^^^^^^^^^^^ + +Visual Studio Code is a lightweight but powerful source code editor which runs +on your desktop and is available for Windows, macOS and Linux. It comes with +built-in support for JavaScript, TypeScript and Node.js and has a rich ecosystem +of extensions for other languages (such as C++, C#, Java, Python, PHP, Go) and +runtimes (such as .NET and Unity). It is the recommended IDE for `asap`. + +Install by following the instructions at `Visual Studio Code Installation +<https://code.visualstudio.com/docs/setup/windows>`_ guide and add it to the +**PATH** environment variable. + +.. tip:: + + As an alternative, you can use `winget` to install the Doxygen package by + executing the following command ```winget install --id + Microsoft.VisualStudioCode```. + + +Local Development +----------------- + +CMake +^^^^^ + +`CMake <https://cmake.org/>`_ is an open-source, cross-platform family of tools +designed to build, test and package software. CMake is used to control the +software compilation process using simple platform and compiler independent +configuration files, and generate native makefiles and workspaces that can be +used in the compiler environment of your choice. It is the build system used in +`asap`. + +Install it by following the instructions described in `CMake Installation Guide +<https://cmake.org/download/>`_. + +.. tip:: + + As an alternative, you can use `winget` to install the CMake package by + executing the following command ```winget install --id Kitware.CMake```. + +Ninja Build +^^^^^^^^^^^ + +`Ninja <https://ninja-build.org/>`_ is a small build system with a focus on +speed. It differs from other build systems in two major respects: it is designed +to have its input files generated by a higher-level build system, and it is +designed to run builds as fast as possible. Ninja is the preferred generator for +CMake in ` asap`. + +Install it by following the instructions described in `Getting Ninja +<https://ninja-build.org/>`_. Add the ninja executable to the **PATH** +environment variable and restart your terminal. + +Python 3 via Miniconda +^^^^^^^^^^^^^^^^^^^^^^ + +Miniconda is a free minimal installer for conda. It is a small, bootstrap +version of Anaconda that includes only conda, Python, the packages they depend +on, and a small number of other useful packages, including pip, zlib and a few +others. + +Miniconda is a simple and reliable way to manage python environments and is used +in `asap` for the documentation generation. Even if you have python installed on +your development environment, it is still **recommended to install conda and use +an isolated environment** for each project needs. + +Install it by following the instructions described in `Miniconda Installation +<https://docs.conda.io/en/latest/miniconda.html>`_ guide. + +.. tip:: + + As an alternative, you can use `winget` to install the CMake package by + executing the following command ```winget install --id Anaconda.Miniconda3```. + +Locate the conda installation directory and add the following to your PowerShell +profile (`notepad $PROFILE`): + +.. code-block:: powershell + + . >>replace-with-path-to-miniconda-install-dir<<\shell\condabin\conda-hook.ps1 + conda activate '>>replace-with-path-to-miniconda-install-dir<<' + +Doxygen +^^^^^^^ + +Doxygen is the de facto standard tool for generating documentation from +annotated C++ sources. It is the selected tool for generating API documentation +in `asap`. + +Install by following the instructions described in `Doxygen Downloads +<https://www.doxygen.nl/download.html>`_ and add it to the **PATH** environment +variable. + +.. tip:: + + As an alternative, you can use `winget` to install the Doxygen package by + executing the following command `winget install --id + DimitriVanHeesch.Doxygen`. + +Graphviz +^^^^^^^^ + +`Graphviz <https://graphviz.org/>`_ is open source graph visualization software. +Graph visualization is a way of representing structural information as diagrams +of abstract graphs and networks. It is an optional tool used by doxygen to +generate class diagrams and dependency graphs. It is recommended as it makes API +documentation more complete. + +Install by following the steps at `Graphviz Installation +<https://graphviz.org/download/>`_ guide and add it to the **PATH** environment +variable. + +.. tip:: + + As an alternative, you can use `winget` to install the Doxygen package by + executing the following command ```winget install --id Graphviz.Graphviz```. + +NodeJS +^^^^^^ + +`NodeJS <https://nodejs.org/en/>`_ is a JavaScript runtime built on Chrome's V8 +JavaScript engine. It's used in `asap` to take advantage of some of the many +tools written for the JavaScript ecosystem. + +Install it by following the instructions at `NodeJS INstallation +<https://nodejs.org/en/download/>`_ guide and add it to the **PATH** environment +variable. + +.. tip:: + + As an alternative, you can use `winget` to install the Doxygen package by + executing the following command ```winget install --id OpenJS.NodeJS```. + +Visual Studio +^^^^^^^^^^^^^ + +For the Windows Platform target you will need Visual Studio MSVC. You can +install the community edition by following the instructions described in 'Visual +Studio Community Install<https://visualstudio.microsoft.com/vs/community/>`_ +guide.` + +Once installed, open your PowerShell **PROFILE** (```notepad $PROFILE```), add +the following lines to it and save it. + +for Visual Studio 2019 + +.. code-block:: powershell + + Import-Module ">>replace-with-path-to-vstudio<<\Microsoft Visual Studio\2019\Community\Common7\Tools\Microsoft.VisualStudio.DevShell.dll" + Enter-VsDevShell 284a69d6 + +or for Visual Studio 2022 + +.. code-block:: powershell + + Import-Module "E:\dev\Microsoft Visual Studio\2022\Community\Common7\Tools\Microsoft.VisualStudio.DevShell.dll" + Enter-VsDevShell 205f0af7 + +Restart your terminal to activate the changes. + +Dev Containers +-------------- + +Windows WSL2 +^^^^^^^^^^^^ + +The Windows Subsystem for Linux lets developers run a GNU/Linux environment -- +including most command-line tools, utilities, and applications -- directly on +Windows, unmodified, without the overhead of a traditional virtual machine or +dual-boot setup. + +You can install it by following the steps described at `Install WSL +<https://docs.microsoft.com/en-us/windows/wsl/install>`_ guide. + +.. important:: + + Make sure virtualization is enabled at the BIOS and in Windows features. + +Docker Desktop +^^^^^^^^^^^^^^ + +Docker is an open platform for developing, shipping, and running applications. +Docker enables you to separate your applications from your infrastructure so you +can deliver software quickly. With Docker, you can manage your infrastructure in +the same ways you manage your applications. By taking advantage of Docker's +methodologies for shipping, testing, and deploying code quickly, you can +significantly reduce the delay between writing code and running it in +production. + +In `asap`, `Docker` is used in conjunction with `Visual Studio Code` to offer +the possibility to do the development completely within a development container, +thus keeping the current host clean. This methodology can also be used for quick +work on a branch without having the need to switch the local environment. + +Install it by following the instructions described at `Docker Desktop +Installation <https://www.docker.com/products/docker-desktop>`_ guide and ensure +that the necessary virtualization features inside Windows are installed and +properly configured. + +.. tip:: + + As an alternative, you can use `winget` to install the Doxygen package by + executing the following command ```winget install --id + Docker.DockerDesktop```. diff --git a/doc/01-getting-started/get-the-code.rst b/doc/01-getting-started/get-the-code.rst new file mode 100644 index 0000000..1b5c56c --- /dev/null +++ b/doc/01-getting-started/get-the-code.rst @@ -0,0 +1,141 @@ +.. Structure conventions + # with overline, for parts + * with overline, for chapters + = for sections + - for subsections + ^ for sub-subsections + " for paragraphs + +.. _get-the-code: + +************ +Get the code +************ + +.. |date| date:: + +Last Updated on |date| + +The `asap` development model recommends the use of the `Fork and pull model +<https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/getting-started/about-collaborative-development-models#fork-and-pull-model>`_ +of github projects. + +.. note:: + :class: margin + + If you want to create a new repository from the contents of an existing + repository but don't want to merge your changes to the upstream in the future, + you can duplicate the repository or, if the repository is a template, you can + use the repository as a template. For more information, see "`Duplicating a + repository <https://docs.github.com/en/articles/duplicating-a-repository>`_" + and "`Creating a repository from a template + <https://docs.github.com/en/articles/creating-a-repository-from-a-template>`_". + +A fork is a copy of a repository that you manage. Forks let you make changes to +a project without affecting the original repository. You can fetch updates from +or submit changes to the original repository with pull requests. This will give +you total freedom to change the code after the way you want, while still keeping +the option to pull updates from the original `asap` project or to contribute +useful changes or bug fixes in the form of pull requests to the original `asap` +project. + +In the remainder of this guide, we will follow the `Fork and pull model`. + +1. Fork the `asap` project +========================== + +.. hint:: + :class: margin + + The `asap` project is located at `https://github.com/abdes/asap`. + +Follow the instructions described in the `Fork a repo +<https://docs.github.com/en/get-started/quickstart/fork-a-repo>`_ documentation +of GitHub to fork the `asap` project. + +.. important:: + + Rename your fork to match your project name as early as possible in the + process. Although you can do this step at any time in the future, it is + recommended to do it immediately after the fork. + + Follow the instructions described in `Renaming a repository + <https://docs.github.com/en/get-started/quickstart/fork-a-repo>`_ document + from the GitHub documentation. + +Configuring a remote for the fork +--------------------------------- + +You must configure a remote that points to the upstream `asap` repository in Git +to sync changes made in the original repository with your fork. This also allows +you to sync bug fixes and enhancements you make in your fork with the original +`asap` repository in the form of submitted pull requests. + +#. List the current configured remote repository for your fork. + + .. code-block:: bash + + $ git remote -v + > origin https://github.com/YOUR_USERNAME/YOUR_FORK.git (fetch) + > origin https://github.com/YOUR_USERNAME/YOUR_FORK.git (push) + +2. Specify a new remote upstream repository that will be synced with the fork. + + .. code-block:: bash + + $ git remote add upstream https://github.com/abdes/asap.git + +3. Verify the new upstream repository you've specified for your fork. + + .. code-block:: bash + + $ git remote -v + > origin https://github.com/YOUR_USERNAME/YOUR_FORK.git (fetch) + > origin https://github.com/YOUR_USERNAME/YOUR_FORK.git (push) + > upstream https://github.com/abdes/asap.git (fetch) + > upstream https://github.com/abdes/asap.git (push) + +2. Clone your fork +================== + +.. tip:: + :class: margin + + -j4 requests git to parallelize cloning of repos. Needs a relatively recent + version of git. If that is not available, simply do not use this option. + +The `asap` project makes use of git submodules. There is a simple way to get the +project and the submodules (populated) in one step. If you pass +--recurse-submodules to the git clone command, it will automatically initialize +and update each submodule in the repository, including nested submodules if any +of the submodules in the repository have submodules themselves. You can refer to +the `Git Submodules <https://git-scm.com/book/en/v2/Git-Tools-Submodules>`_ +documentation for more details. + +.. code-block:: bash + + git clone --recurse-submodules -j4 https://github.com/YOUR_USERNAME/YOUR_FORK.git + +If you already cloned the project and forgot --recurse-submodules, you can +combine the git submodule init and git submodule update steps by running ```git +submodule update --init```. To also initialize, fetch and checkout any nested +submodules, you can use the foolproof ```git submodule update --init +--recursive```. + +3. Post-clone setup +=================== + +.. note:: + :class: margin + + This step needs to be done only once after the project is cloned. It sets up + the hooks and additional tools needed for commit message linting and automatic + changelog generation. + +Only once, after the project is cloned, do the following: + +.. code-block:: bash + + npx husky install + npm install -g @commitlint/cli @commitlint/config-conventional + npm install -g standard-version diff --git a/doc/01-getting-started/get-updates.rst b/doc/01-getting-started/get-updates.rst new file mode 100644 index 0000000..9107847 --- /dev/null +++ b/doc/01-getting-started/get-updates.rst @@ -0,0 +1,60 @@ +.. Structure conventions + # with overline, for parts + * with overline, for chapters + = for sections + - for subsections + ^ for sub-subsections + " for paragraphs + +.. _get-updates: + +*********** +Get updates +*********** + +.. |date| date:: + +Last Updated on |date| + +If you have properly followed the instructions when :ref:`get-the-code`, you +should have a fork setup with the original `asap` as its upstream, thus allowing +you to sync updates that may have been made in `asap` after you forked it. + +Syncing your fork to keep it up-to-date with the upstream repository can be done +from the Github web UI or from the command line. Both methods are described in +detail at the GitHub `Syncing a fork +<https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/syncing-a-fork>`_ +documentation page. + +The following steps provide a summary of the command line method, assuming that +the branch being worked on in your forked project is the `develop` branch. + +.. prompt:: bash $ auto + + $ git fetch upstream + remote: Enumerating objects: 5, done. + remote: Counting objects: 100% (5/5), done. + remote: Compressing objects: 100% (1/1), done. + remote: Total 3 (delta 2), reused 3 (delta 2), pack-reused 0 + Unpacking objects: 100% (3/3), 361 bytes | 6.00 KiB/s, done. + From https://github.com/abdes/asap + 3d8ca08..a085b56 develop -> upstream/develop + +.. prompt:: bash $ auto + + $ git checkout develop + Switched to branch 'main' + +.. prompt:: bash $ auto + + $ git merge upstream/master + Updating 3d8ca08..a085b56 + Fast-forward + CMakePresets.json | 14 ++++++++++++-- + 1 file changed, 12 insertions(+), 2 deletions(-) + +.. tip:: + + The example uses `upstream/master` as the branch with which the fork is kept + in sync. You may want to use `upstream/develop` if you want to live on the + cutting edge of `asap` features. diff --git a/doc/01-getting-started/index.rst b/doc/01-getting-started/index.rst new file mode 100644 index 0000000..b264a96 --- /dev/null +++ b/doc/01-getting-started/index.rst @@ -0,0 +1,95 @@ +.. Structure conventions + # with overline, for parts + * with overline, for chapters + = for sections + - for subsections + ^ for sub-subsections + " for paragraphs + +########### +Get Started +########### + +.. |date| date:: + +Last Updated on |date| + +.. toctree:: + :maxdepth: 1 + :titlesonly: + :hidden: + + devenv + get-the-code + structure + useful-commands + customizing + get-updates + +You chose to get started with `asap` and you made the right choice. This part of +the documentation will help you quickly get started. But before you start, here +are a couple of tips and answers to some frequently asked questions: + +#. **What platform can I use to do the development of an `asap` based project?** + + You can clone and use the `asap` project on Windows, macOS and Linux. Most of + the features will be working just fine but some features may require a + specific compiler or development tool that is available only on a specific + operating system. + + There is a recommended setup that is described in detail in the :doc:`Getting + ready for C++ development <devenv>` document. + +#. **What compilers and debuggers can I use?** + + You can use You `GCC <https://gcc.gnu.org>`_-based compilers, `Clang + <https://clang.llvm.org>`_ and `Visual Studio C++ compiler + <https://visualstudio.microsoft.com/>`_. + +#. **Do I need to install anything in advance?** + + Yes. Refer to :doc:`Getting ready for C++ development <devenv>` document for + a complete and detailed guide. + +#. **Can I use `asap` with my favorite IDE?** + + Definitely. However to get the maximum out of it your IDE should have some + level of support of and integration with `CMake`. + + +:doc:`Getting ready for C++ development <devenv>` +------------------------------------------------- + +*at the minimum you need a terminal program, a C++ compiler and a text editor, +but we're here for maximum productivity and pleasure while coding. Start here to +understand the recommended tooling and the rationale behind some of the +important choices made in `asap`.* + +:doc:`Creating the project <get-the-code>` +------------------------------------------------- + +*with the development environment in place, you're ready to start working on +your project. Follow this guide to get the `asap` code.* + +:doc:`Understanding the project structure <structure>` +------------------------------------------------------ + +*the project structure follows best practices to implement multi-module projects +that maintain clean dependencies, easily integrate 3rd party libraries and use +cmake in an as simple as possible way. You can learn more about it here.* + +:doc:`Useful commands <useful-commands>` +------------------------------------------------------ + +*when using this starter project to build your own project, you might follow the +instructions in this guide to customize it to fit your needs or your +organization's policies and style.* + + +:doc:`Make it your own <customizing>` +------------------------------------------------------ + +*when using this starter project to build your own project, you might follow the +instructions in this guide to customize it to fit your needs or your +organization's policies and style.* + diff --git a/doc/01-getting-started/structure.rst b/doc/01-getting-started/structure.rst new file mode 100644 index 0000000..5905307 --- /dev/null +++ b/doc/01-getting-started/structure.rst @@ -0,0 +1,312 @@ +.. Structure conventions + # with overline, for parts + * with overline, for chapters + = for sections + - for subsections + ^ for sub-subsections + " for paragraphs + +.. _project-structure: + +******************************** +Understand the project structure +******************************** + +.. |date| date:: + +Last Updated on |date| + +Compared to other languages such JavaScript, Java or Rust, C++ seriously lacks +built-in capabilities for defining, building and using modules. Many competing +approaches exist such as `Conan <https://conan.io/>`_, `vcpkg +<https://vcpkg.io/>`_, `Hunter <https://github.com/cpp-pm/hunter>`_, etc. None +of them completely solves the problem and the worst part is that they often are +mutually exclusive. + +The choices made in the `asap` starter project with regard to the project layout +and to how modules (internal and 3rd party) are integrated and managed do not +exclude the use of package managers or lock you down to one approach or another. + +The emphasis is put on a clean and extensible project layout and a build system +that stays as much as possible within the commonly used things in CMake. + +Software Architecture: The Module Viewtype [#f1]_ +================================================= + +Decomposing a system into manageable units remains one of the most common ways +to think about the structure of the system. Often, it also determines how a +system's source code is partitioned into separate parts, what kind of interfaces +exist between these parts and how these parts are aggregated into larger +ensembles. + +In the context of `asap` we adopt a broad definition of the `module` concept as +an implementation unit of software that provides a coherent unit of +functionality. Because we are dealing specifically with C++ development, that +implementation unit will be a set of C++ files, configuration files, static data +files, etc. In addition to that,we also deal with documentation files, and +various build scripts, including CMake and other tools. + +Modules can be related to each others in different ways: + +- **A is part of B**: this type of relation can indicate simple aggregation, + such as when a module is included into one or many others, or could indicate a + more sophisticated decomposition relationship. +- **A depends on B**: this relation can take several forms such as uses, + allowed-to-use, sends-data-to, transfers-control-to, and so forth. +- **A is a B**: this one defines a generalization relationship and in general + would not impact the macro-level structure of the project. We do however use + it in this starter project to define library and executable modules for which + we provide generic reusable CMake build scripts that can be simply extended. + +To the maximum possible extent we want to preserve and faithfully map the +modules and their relations in the software architecture to the development +infrastructure. Implementing a module always results in many separate files, +such as those that contain the source code, files that have to be included and +usually contain definitions, files that describe how to build an executable, and +files that are the result of translating, or compiling, the module. Those files +need to be organized so as not to lose control and integrity of the system. +Configuration management techniques usually do this job: in the simplest case, a +hierarchy of directories in a file system. + +Assuming this starter project is used to build a simple application that reads +and writes files on the local filesystem, a representation of the application +architecture in a module decomposition view would look like this: + +.. code-block:: text + + ┌─────────────────────────────────────────────────────────────────┐ + │ ASAP Application │ + │ (System) │ + │ ┌─────────────────────┐ ┌──────────┐ │ + │ │ ├──────────────────────────► │ │ + │ │ │ │ │ │ + │ │ │ ┌───────────────────┐ │ │ │ + │ │ │ │ │ │ │ │ + │ │ │ │ logging framework │ │ │ │ + │ │ │ │ │ │ │ │ + │ │ │ │ 3PM │ │ │ │ + │ │ │ └───────────▲───────┘ │ │ │ + │ │ │ │ │ │ │ + │ │ Main │ │ │ │ │ + │ │ │ ┌───────────┴───────┐ │ │ │ + │ │ Application │ │ common │ │ │ │ + │ │ │ │ │ │ │ │ + │ │ Module │ │ │ │ │ │ + │ │ ├──► ├───► │ │ + │ │ │ │ │ │ unit │ │ + │ │ │ │ │ │ │ │ + │ │ │ └───▲───────────────┘ │ testing │ │ + │ │ │ │ │ │ │ + │ │ │ ┌───┴───────────────┐ │ framework│ │ + │ │ │ │ │ │ │ │ + │ │ ├──► other module ├───► │ │ + │ │ │ │ │ │ │ │ + │ │ │ │ │ │ 3PM │ │ + │ └─────────────────────┘ └───────────────────┘ └──────────┘ │ + │ │ + └─────────────────────────────────────────────────────────────────┘ + +.. note:: + :class: margin + + Composition is allowed at any depth within the structure of an `asap` based + project. Often, it is implemented using a `git` submodule. + +The arrow in the diagram above mainly refer to a uses relationship. Notice how +the modules can be 3rd party modules (indicated with a `3PM` sticker), provided +by `asap` as reusable components or specifically made for the application. + +The `Unit Testing Framework` is extensively used in the project but only during +development. It won't be part of the final application package for deployment +for instance. Such modules exist within the project and the CMake build system +can clear make the difference between the artifacts intended for use during +development only vs. the ones destined for deployment. + +Project Layout +============== + +Based on the module decomposition view above, we now can look at how this +translates into the hierarchy of directories forming the `asap` project layout. + +.. code-block:: text + + . + ├── <.git> I + ├── <.sphinx> + ├── <build> + │ ---------------------------------------- + ├── <.github> + ├── <.husky> + ├── <.vscode> + ├── <doxygen> II + ├── .clang-format + ├── .clang-tidy + ├── .commitlintrc.json + ├── .gitattributes + ├── .gitignore + ├── .gitmodules + ├── .runsettings + ├── .versionrc.json + ├── requirements.txt + │ ---------------------------------------- + ├── <doc> III + │ ---------------------------------------- + ├── <deploy> IV + │ ---------------------------------------- + ├── <data> V + │ ---------------------------------------- + ├── <templates> VI + │ ---------------------------------------- + ├── <common> + ├── <filesystem> + ├── <third_party> VII + │ ├── <fmt> + │ ├── <spdlog> + │ └── CMakeLists.txt + │ ---------------------------------------- + ├── <cmake> + ├── CMakeLists.txt VIII + ├── asap-config.cmake + │ ---------------------------------------- + ├── AUTHORS + ├── LICENSE IX + ├── README.md + └── CHANGELOG.md + +The above diagram shows a typical layout with directories and files of a startup +project based on asap. The actual files and directories will of course differ +from project to project based on how you customize it to add or remove things +from the starting state. + +The files and directories have been grouped into clusters based on their common +purpose. We'll explain what each cluster role is and what are the important +directories and files it contains. + +.. list-table:: + :widths: 10 90 + :header-rows: 1 + + * - Part + - Description + + * - I + - Transient directories created by the various tools used during the + development. Items other than the `.git` directory will not be present + when the project is freshly cloned. The `.sphinx` directory holds the + python virtual env used for the documentation generation. The `build` + directory is where the `cmake` build will happen and all build artifacts + will be located. + + * - II + - Various files used to configure and customize the behavior of tools used + in the development. Their usage will be explained in the corresponding + sections of the documentation. + + * - III + - This is where all project documentation resides. The documentation system + primarily uses `sphinx` and `restructuredText` but occasionally markdown + files can also be used. Maintaining the project documentation is an + important task which will be thoroughly explained separately. + + * - IV + - This directory holds data and configuration files required to build the + final deployable package of the project artifacts. These packages are + often operating system dependent. + + * - V + - This directory is intentionally present in the starter project even if no + data is inside to emphasize the best practice of separating application + data from code, configuration, etc... Use this place to store static data + files that need to be deployed with the system. + + * - VI + - System wide templates used during the build to generate other files. If a + template is only used within a specific module, place it within its + corresponding module. + + * - VII + - The various modules in the system. Third party libraries are preferably + placed under the `third_party` directory for clear demarcation of what is + coming from outside the project. Sometimes, it is more natural or + practical to embed the third party code directly within the source code of + a system module (e.g. this is how Hedley is included inside the `common` + module). + + * - VIII + - The CMakeLists.txt and supported scripts which are used to build the + system deployable artifacts from source code. Customization of the starter + project build to suit the real project happens primarily in the + `CMakeLists.txt` and `asap-config.cmake` files. All files under the + `cmake` directory are generic reusable scripts and, in general, should not + be modified. + + * - IX + - Various meta-information files present in each project repository. Adapt + the contents as needed to suit your project, keeping in mind the + `CHANGELOG.md` can be automatically maintained as described in this + documentation. + + +General Layout of a C++ source Module +===================================== + +Since we are heavily using the system decomposition into modules as an +architectural principle, we'll be creating a lot of these modules and therefore +we need to be consistent and efficient in how we layout the source code +artifacts inside each one of them. + +I have looked at so many OpenSource projects, coding standards, recommended +project layouts,... for inspiration and while there is no holy grail for this +problem, a few best practices have emerged: + +- separate test code from the module functional code. That way, it is clear + which files contribute to build the module deployable artifacts vs. testing + artifacts. +- we're doing C++, so we have interface files (.h) and implementation files + (.cpp) and while there is no consensus on where to put them, I have found + that it is much simpler and cleaner to keep them each in its own directory. + With modern editors, we don't navigate code anymore based on the file browser. + We use intellisense. +- inside the `include` directory, repeat the module name so that when we include + files from this module we say `#include <module/foo.h>` and not `<foo.h>`. + In coding, explicit intents are much safer than implicit intents. +- in-house developed modules containing C++ code are much easier reused in their + source code form than in any other form. Unfortunately C++ package managers + are still far from mature. Therefore, we keep each module in its own git + repository and we embed it as a submodule when reused. +- building a module should be descriptive. The build logic and complexity should + already be managed at the system level. We just need to describe a module to + get it included in the system. That description is in the module's + `CMakeLists.txt` which almost does not contain any scripted logic. +- each module should be properly documented. The system documentation build will + ensure modules can be referred to and get integrated into the overall + documentation. + +With these in mind, the layout of each module looks like this: + +.. code-block:: text + + │ .gitignore + │ .gitmodules + │ AUTHORS + │ CMakeLists.txt + │ LICENSE + │ README.md + │ + ├───doc + │ + ├───include + │ ├───common + │ │ └───traits + │ └───contracts + │ + ├───src + │ + └───test + + +.. rubric:: Footnotes + +.. [#f1] An excellent book on this topic is `Documenting Software Architectures: + Views and Beyond <https://www.amazon.com/Documenting-Software-Architectures-Views-Beyond/dp/0201703726/ref=sr_1_1?_encoding=UTF-8&camp=212361&creative=380601&dchild=1&keywords=0201703726&link_code=wql&qid=1635883655&s=books&sr=1-1>`_ + (available on Amazon, or just Google it). diff --git a/doc/01-getting-started/useful-commands.rst b/doc/01-getting-started/useful-commands.rst new file mode 100644 index 0000000..bf400eb --- /dev/null +++ b/doc/01-getting-started/useful-commands.rst @@ -0,0 +1,87 @@ +.. Structure conventions + # with overline, for parts + * with overline, for chapters + = for sections + - for subsections + ^ for sub-subsections + " for paragraphs + +.. _useful-commands: + +************************ +Know the useful commands +************************ + +.. |date| date:: + +Last Updated on |date| + +With your project properly forked->renamed->cloned, here are a couple of useful +commands you can immediately try out to see the project in action. Many of these +commands will be extensively documented in later chapters. + +List all available `cmake` presets + .. code:: bash + + ❯ cmake --list-presets + Available configure presets: + + "dev-windows" + "rel-windows" + + .. tip:: + + on a Linux or macOS system, the list of available presets will change. For + example on Linux, the list would like like this: + + .. code:: bash + + Available configure presets: + + "dev-linux" + "dev-clang" + "dev-gcc" + "dev-valgrind" + "dev-sanitizers" + "dev-sanitizers-thread" + "rel-linux" + +Configure the project + .. code:: bash + + cmake --preset=dev-windows + +Build the project (default targets) + .. code:: bash + + cmake --build --preset=dev-windows + +Build specific target + .. code:: bash + + cmake --build --preset=dev-windows --target=asap_common_test + +Build and run all tests + .. code:: bash + + cmake --build --preset=dev-windows --target=do-all-tests + +Build `doxygen` and `sphinx` documentation + .. code:: bash + + cmake --build --preset=dev-windows --target=dox --target=sphinx + +Open a browser with the documentation (Windows) + .. code:: powershell + + start out/build/dev-windows/sphinx/index.html + +Run tests with `ctest` + .. code:: powershell + + cmake --build --preset=dev-windows --target=test + +Clean the project build + .. code:: powershell + + cmake --build --preset=dev-windows --target=clean diff --git a/doc/02-project-development/build.rst b/doc/02-project-development/build.rst new file mode 100644 index 0000000..0d2b8d9 --- /dev/null +++ b/doc/02-project-development/build.rst @@ -0,0 +1,202 @@ +.. Structure conventions + # with overline, for parts + * with overline, for chapters + = for sections + - for subsections + ^ for sub-subsections + " for paragraphs + +****************** +Build instructions +****************** + +The project uses `CMake` as a build system and defines `presets` in the +`CmakePrests.json` file that describe the most commonly used configuration, +build and test settings. While you can still use the traditional way of incoking +`cmake` commands in a build directory, we recommend that you understand and +embrace the cmake presets way, which simplifies the command lines required to +invoke `cmake` and integrates nicely with the most commonly used IDEs. + +Build targets +============= + +The project defines a number of targets that can be used to build specific +artifacts or groups of related artifacts. + +:all: build all primary targets of the project. That includes all library and + executable targets defined using the `asap_add_library` and + `asap_add_executable` commands. Each of these targets may also be built + individually. Tests are usually excluded from the ```all``` target unless they + have been added with the `POST_BUILD` option. + +:build-all-tests: build all tests added using the `asap_add_test` or + `asap_add_test_runner` commands. + +:build-all-post-build-tests: build all tests added using the `asap_add_test` or + `asap_add_test_runner` commands and with the option `POST_BUILD`. Such tests + are also built as part of the ```all``` target. + +:do-all-tests: builds and runs all tests added using the `asap_add_test` or + `asap_add_test_runner` commands, including unit tests and integration tests. + +:do-all-unit-tests: builds and runs all tests added using the `asap_add_test` or + `asap_add_test_runner` commands with the option `UNIT_TEST`. + +:do-all-integration-tests: builds and runs all tests added using the + `asap_add_test` or `asap_add_test_runner` commands with the option + `INTEGRATION_TEST`. + +:dox: build `doxygen` documentation. This target is only available when + `doxygen` and its dependencies (such as `graphviz`) have been properly + detected and were found. + +:sphinx: build `sphinx` documentation. This target is only available when + `sphinx` and its dependencies have been properly detected and were found. A + complete documentation also requires doxygen documentation to have been built + prior to building the `sphinx` manual. Such dependency is not enforced in the + targets graph to make it faster to build `sphinx` docs without rebuilding + `doxygen` docs each time. + +The following targets are only defined when code coverage tools have been +detected on the system. Such tools depend on the compiler being used (clang or +gcc, MSVC is not supported). + +:ccov-all: run all tests to collect code coverage statistics and produce a + consolidated report in HTML for all the source files included in coverage + collection. + +:ccov-xxxtargetxxx: where xxxtargetxxx refers to a specific executable target in + the project. Run the executable with code coverage collection enabled and + produce a html report just for that target. This can be used to run code + coverage analysis just for a specific test. + +:ccov-clean: clean intermediary files generated for code coverage analysis. + +The following target are only defined if the compiler is `clang`, the +`clang-format`tool has been properly detected and found on the system and the +option `ASAP_ENABLE_CLANG_FORMAT` is not set to `OFF`. + +:clang-format-all: formats all C++ source code files in the project using + `clang-format`. + +:clang-format-all-check: formats all C++ source code files in the project using + `clang-format` and exits with a non-zero exit-code if changes were made. + +:clang-format-diff: formats only changed C++ source code files in the project + using `clang-format`. + +:clang-format-diff-check: formats only changed C++ source code files in the + project using `clang-format` and exits with a non-zero exit-code if changes + were made. + +The following target are only defined if the compiler is `clang`, the +`clang-tidy`tool has been properly detected and found on the system and the +option `ASAP_ENABLE_CLANG_TIDY` is not set to `OFF`. + +:clang-tidy-all: lints all C++ source code files in the project using + `clang-tidy`. + +:clang-tidy-all-check: lints all C++ source code files in the project using + `clang-tidy` and exits with a non-zero exit-code if any warnings were + generated. + +:clang-tidy-diff: formats only changed C++ source code files in the project + using `clang-format`. + +:clang-tidy-diff-check: lints only changed C++ source code files in the project + using `clang-tidy` and exits with a non-zero exit-code if any warnings were + generated. + +.. note:: + + Targets for running `clang-tidy` on a specific target in the project are also + defined. For example to run it on the `asap_common` module, use + `clang-tidy-asp_common`. + +`CMake` Presets +=============== + +CMake supports two files, `CMakePresets.json` and `CMakeUserPresets.json`, that +allow users to specify common configure options and share them with others. + +`CMakePresets.json` and `CMakeUserPresets.json` live in the project's root +directory. They both have exactly the same format. `CMakePresets.json` is meant +to save project-wide builds, while `CMakeUserPresets.json` is meant for +developers to save their own local builds. `CMakePresets.json` may be checked +into a version control system, and `CMakeUserPresets.json` should NOT be checked +in. + +For a complete description of the format refer to the `cmake-presets +documentation +<https://cmake.org/cmake/help/latest/manual/cmake-presets.7.html#id3>`_. + +This project defines presets in a hierarchical way and uses 'mixins' to add +specific aspects of the build configuration such as debug/release, code +coverage, etc. Once you understand the format of the presets files, it's easy to +understand the logic and the reasoning behind how `asap` presets are defined. + +.. literalinclude:: ../../CMakePresets.json + :language: json + :linenos: + :emphasize-lines: 8,255,387 + +Integration in IDEs +=================== + +Refer to the relevant documentation of the IDE you are using to understand how +cmake presets are integrated in the IDE and how you can select the right preset +for configure, build and test phases. + +Command-line build with presets +=============================== + +.. note:: + All output goes into the `out` directory under the project root. + + All commands should be run from the project root. + +Configure +--------- + +.. code-block:: shell + + $ cmake --preset=dev-linux + +Build +----- + +.. code-block:: shell + + $ cmake --build --preset=dev-linux + + $ cmake --build --preset=dev-linux --target=dox + +Test +---- + +.. code-block:: shell + + $ cmake --build --preset=dev-linux --target=test + + $ cmake --build --preset=dev-linux --target=do-all-tests + +Run tests with code coverage collection enabled: + +.. code-block:: shell + + $ cmake --build --preset=dev-linux --target=ccov-all + +Run tests with sanitizers enabled: + +.. code-block:: shell + + $ cmake --preset=dev-sanitizers + $ cmake --build --preset=dev-sanitizers --target=do-all-tests + +Run tests with `valgrind` enabled (output reports are in +`out/build/dev-valgrind/profiling`): + +.. code-block:: shell + + $ cmake --preset=dev-valgrind + $ cmake --build --preset=dev-valgrind --target=do-all-valgrind diff --git a/doc/02-project-development/devflow.rst b/doc/02-project-development/devflow.rst new file mode 100644 index 0000000..8afaee4 --- /dev/null +++ b/doc/02-project-development/devflow.rst @@ -0,0 +1,199 @@ +.. Structure conventions + # with overline, for parts + * with overline, for chapters + = for sections + - for subsections + ^ for sub-subsections + " for paragraphs + +******************* +Development Process +******************* + +Version control +=============== + +We use Git and we require a recent version of the git tools to take advantage of +enhanced features and avoid unnecessary bugs. It is strongly recommended to use +the latest available version of git on the supported platforms. + +Cloning for the first time +-------------------------- + +.. code-block:: bash + + $ git clone --recurse-submodules https://github.com/abdes/asap.github + + +Development workflow +==================== +We adopt a simplified git-flow workflow, keeping only the develop and master +branches (no release branch), with the following principles: + + * The master branch should be the stable, golden version of the repos + * The develop branch is the current stable, buildable version of the repos + * New feature development is done in feature branches + +Feature branches +---------------- +The workflow for feature branches is similar ot git-flow except that the +git-flow tools are not used. Instead, either the git commands are used directly +from the command line or via the web interface of github. + +Start a new feature +^^^^^^^^^^^^^^^^^^^ +.. code-block:: bash + + $ git checkout -b feature/MYFEATURE develop + +Finish a feature +^^^^^^^^^^^^^^^^ +.. code-block:: bash + + git checkout develop + git merge --no-ff feature/MYFEATURE + git branch -d feature/MYFEATURE + +.. _fast-forward-label: +.. important:: + Whenever possible, Git normally tries to “fast forward” commits during a + merge. The decision to FF is based upon whether or not the commit’s ancestry + in the history doesn’t require a branch in the commit graph. However, in some + cases it may be preferable to create a dedicated merge commit (effectively + resulting in a commit graph branch), even though technically-speaking the + linear commit ancestry does not require it. + + The --no-ff flag forces the creation of such “merge commit”, which thereby + records the merge as a distinct branch in the commit graph. This can be + useful for reverting a set of commits (e.g. the entirety of a feature branch, + or release branch). + + This flag is used by default by git-flow, and by GitHub’s Pull Request + “merge” button (thus why the “revert” button is available in the GitHub web + user interface). + +Release Process +--------------- +A key element of the release approach for the project is that there is NO +release branch. Instead, the process is: + + * Once the ``develop`` branch has the right set of features and is stable, + * Changes to ``develop`` are “closed” for the interim, + * The release process is followed (see table below), + * Once the release is complete (merged to ``master``, tagged) the ``develop`` + branch is open for work again. + +.. list-table:: + :widths: 10 90 + :header-rows: 0 + + * - 1 + - git checkout develop + + * - 2 + - git pull + * - 3 + - git commit -a + + * - 4 + - git push + + * - 5 + - git checkout master + + * - 6 + - git pull + + * - 7 + - Merge into master from the develop branch (no fast-forward => ensure + there is always a dedicated merge commit) + + .. code-block:: shell + + git merge --no-ff develop + +.. tip:: + :class: margin + + This will generate the changelog, update version numbers in `CMakeLists.txt`, + and create a new tag. + + See `Conventional Commits <https://www.conventionalcommits.org/en/v1.0.0/>`_ + for more information on how conventional commit messages can simplify + changelog preparation. + +.. list-table:: + :widths: 10 90 + :header-rows: 0 + + * - 8 + - Release a new version with `standard-version`. + + .. code-block:: shell + + npx standard-version --help + + npx standard-version --dry-run + + npx standard-version --skip.commit --skip.tag + + Check the generated changelog and edit if needed. Verify the updated + version number is correct. + + Commit the cnhages and push to the remote. + + .. code-block:: shell + + git commit -a -m "version bump to M.m.p" + git push + + .. note:: + + These steps can be automatic with standard-version, but it is + recommended to not automate them as often the generated changelog + needs some refinements before it is committed and a release tag is + made. + +.. list-table:: + :widths: 10 90 + :header-rows: 0 + + * - 9 + - Create a new tag on the `master` branch with appropriate label and push + it to the remote. + + .. code-block:: shell + + git tag -a M.m.p -m "release M.m.p" + git push origin M.m.p + + * - 10 + - Merge master back into develop to include the merge commit (see --no-ff + notes below) + + .. code-block:: shell + + git checkout develop + git merge master + git push + +.. note:: + See the :ref:`Feature branches <fast-forward-label>` section above to read + more about --no-ff. + + Because a separate “merge commit” is created, it is important to merge the + master branch back into develop as-is, so that master and develop are in + sync (just as per `git-flow`). In fact, the tag will be associated with the + merge commit, so it is important to have this tag present in the develop + branch too. + +This process needs to be done carefully to avoid any problems from the +submodules. Always check that the `master` and `develop` branches still have +the same, correct submodule pointer: + +.. code-block:: bash + + $ git checkout master + $ git submodule status + $ git checkout develop + $ git submodule status diff --git a/doc/02-project-development/index.rst b/doc/02-project-development/index.rst new file mode 100644 index 0000000..a39237f --- /dev/null +++ b/doc/02-project-development/index.rst @@ -0,0 +1,39 @@ +.. Structure conventions + # with overline, for parts + * with overline, for chapters + = for sections + - for subsections + ^ for sub-subsections + " for paragraphs + +################ +Developer Guides +################ + +.. toctree:: + :maxdepth: 1 + :titlesonly: + :hidden: + + build + devflow + modules + third_party + +:doc:`Build <build>` +-------------------- +*using the build system to build the project targets and manage its artifacts.* + +:doc:`Development workflow <devflow>` +------------------------------------- +*a possible workflow to manage the development and release processes in the +project.* + +:doc:`Modules <modules>` +------------------------------------- +*understand how to add/remove modules and how they get integrated into the build +system.* + +:doc:`Third-party modules <modules>` +------------------------------------- +*understand how to add/remove third party libraries/modules from the project.* diff --git a/doc/02-project-development/modules.rst b/doc/02-project-development/modules.rst new file mode 100644 index 0000000..031633f --- /dev/null +++ b/doc/02-project-development/modules.rst @@ -0,0 +1,253 @@ +.. Structure conventions + # with overline, for parts + * with overline, for chapters + = for sections + - for subsections + ^ for sub-subsections + " for paragraphs + +******* +Modules +******* + +.. |date| date:: + +Last Updated on |date| + +The `asap` project architecture adopts a modular approach as described in +:doc:`/01-getting-started/structure` document. This structure allows you to +easily add, remove and compose modules inside the project. + +Adding a module +=============== + +.. tip:: + :class: margin + + To add a library module, just look at one of the existing sub-modules in + `asap` such as `common` or `logging`. + +For the sake of illustrating this process, we're gonna be adding a module for an +executable program. The program will be called `myapp`, and so we will be +calling our new module `myapp-main`. + +1. Create the module structure as a sub-directory of the project. This would + look something like this: + + .. code-block:: text + + │ CMakeLists.txt + │ README.md + └───src + ├───things.h + └───main.cpp + + It's a minimal structure, and as the module gets more and more sophisticated, + it will most likely have test cases, more implementation files, include + headers, etc... It may even have submodules itself. + +2. Create a ```CMakeLists.txt``` file and edit its content to describe how the + targets in the module get built. Make sure to use the `asap` target helper + functions so that you automatically benefit from all the goodness built into + them such as linting, formatting, code coverage etc. + + For an executable target as simple as ours, the contents of the file would + look something like this: + + .. code-block:: CMake + + # ----------------------------------------------------- + # Meta information about the this module + # ----------------------------------------------------- + + asap_declare_module( + MODULE_NAME + "myapp-main" + DESCRIPTION + "Main module for the `myapp` program" + GITHUB_REPO + "https://github.com/xxx/myapp" + AUTHOR_MAINTAINER + "The Author" + VERSION_MAJOR + "1" + VERSION_MINOR + "0" + VERSION_PATCH + "0") + + # ===================================================== + # Build instructions + # ===================================================== + + # ----------------------------------------------------- + # Main module target + # ----------------------------------------------------- + + set(MODULE_TARGET_NAME "myapp") + + asap_add_executable( + ${MODULE_TARGET_NAME} + WARNING + SOURCES + # Headers + src/things.h + # Sources + src/main.cpp) + + target_link_libraries( + ${MODULE_TARGET_NAME} + PRIVATE + GSL + asap::common + asap::logging) + + target_include_directories(${MODULE_TARGET_NAME} PRIVATE ${CMAKE_BINARY_DIR}/include) + target_include_directories(${MODULE_TARGET_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src) + + # ===================================================== + # Deployment instructions + # ===================================================== + + # Executable + install( + TARGETS ${MODULE_TARGET_NAME} + RUNTIME DESTINATION ${INSTALL_BIN} COMPONENT programs + BUNDLE DESTINATION ${INSTALL_BIN} COMPONENT programs) + +3. Add the module to main `CMakeLists.txt` by calling + ```add_subdirectory(myapp-main)``` + + .. code-block:: cmake + + # ===================================================== + # Project modules + # ===================================================== + + add_subdirectory(common) + add_subdirectory(logging) + add_subdirectory(myapp-main) + +4. Reconfigure your project to regenerate CMake build files with the new targets + for the new module. You should now have a target for building the `myapp` + executable. + +Adding tests to a module +======================== + +Let's add some tests to the module using `Google Test Framework` as the unit +testing framework. If you decide to use a different framework, the instructions +would still be similar although you will need to link to the appropriate +libraries and add the appropriate include directories for that framework. + +1. Add test cases to a `test` subdirectory under the module. + +2. Add `CMakeLists.txt` under the `test` subdirectory to describe how the test + targets are built. + + The directory structure should now look like this: + + .. code-block:: text + + │ CMakeLists.txt + │ README.md + ├───src + └───test + ├───foo_test.cpp + └───main.cpp + + The new `CMakeLists.txt` for the test targets should contains something like + this: + + .. code-block:: CMake + + # ==================================================== + # Build instructions + # ==================================================== + + set(MAIN_TEST_TARGET_NAME ${MODULE_TARGET_NAME}_test) + + asap_add_test( + ${MAIN_TEST_TARGET_NAME} + UNIT_TEST + VALGRIND_MEMCHECK + SRCS + "foo_test.cpp" + "main.cpp" + LINK + # Add any linked libraries here, including the module to be tested + gtest + gmock + COMMENT + "ASAP common unit tests") + + gtest_discover_tests(${MAIN_TEST_TARGET_NAME}) + + # Add support for (optional) code quality tools + asap_add_sanitizers(${MAIN_TEST_TARGET_NAME}) + swift_add_valgrind_massif(${MAIN_TEST_TARGET_NAME}) + swift_add_valgrind_callgrind(${MAIN_TEST_TARGET_NAME}) + +3. Add the `test` subdirectory to the module's `CMakeLists.txt` + + .. code-block:: cmake + + # ----------------------------------------------- + # Tests + # ----------------------------------------------- + + if(ASAP_BUILD_TESTS) + add_subdirectory(test) + endif() + +Adding documentation to a module +================================ + +Documentation comes in two forms: `doxygen` API documentation and `sphinx` +documentation. The former is embedded in the source code files, while the latter +is written in its own separate files placed in a ```doc``` subdirectory under +the module root. + +1. The contents of the `doc` directory can be started by copying some of the + files from another existing module. In particular, the ```conf.py.in``` file, + can be copied from an existing module and used without modification. + + The module's `CMakeLists.txt` should then be modified to add the targets for + `doxygen` and `sphinx` documentation build as appropriate. + + .. code-block:: CMake + + # -------------------------------------------- + # API Documentation + # -------------------------------------------- + + asap_with_doxygen( + MODULE_NAME + ${MODULE_TARGET_NAME} + VERSION + ${META_MODULE_VERSION} + TITLE + "\"MyApp Module\"" + BRIEF + "\"Provides some stuff for MyApp.\"" + INPUT_PATH + "${CMAKE_CURRENT_SOURCE_DIR}/src ${CMAKE_CURRENT_SOURCE_DIR}/include") + + asap_with_sphinx(${MODULE_TARGET_NAME}) + +2. Add target dependencies from the master sphinx documentation target to the + module's sphinx documentation target. This step is only required if you have + added a module sphinx documentation target. + + .. code-block:: CMake + + add_dependencies(master_sphinx + copy_doc_index + # Hardcode `asap` in the module name as we do not want this prefix to + # change with the forked project name. + asap_common_sphinx + asap_logging_sphinx + # Add more submodule documentation targets after this, using variables + # in the target names consistently with the module's CMakeLists.txt. + myapp + ) diff --git a/doc/02-project-development/third_party.rst b/doc/02-project-development/third_party.rst new file mode 100644 index 0000000..ca74384 --- /dev/null +++ b/doc/02-project-development/third_party.rst @@ -0,0 +1,272 @@ +.. Structure conventions + # with overline, for parts + * with overline, for chapters + = for sections + - for subsections + ^ for sub-subsections + " for paragraphs + +******************* +Third party modules +******************* + +.. |date| date:: + +Last Updated on |date| + +The `asap` project fully leverage the open source development model by allowing +to easily add third party libraries. We want to increase reuse of existing +software but not at the cost of adding unnecessary complexity to the build +environment. + +C++ does not provide any built-in package management mechanism like what we can +find in `Rust <https://doc.rust-lang.org/cargo/>`_ or `Go +<https://go.dev/doc/modules/managing-dependencies>`_. + +Over time, many workaround were used to deal with this gap, including git +submodules, independent package managers such as `conan <https://conan.io/>`_, +or even copying the third party code or binaries into the project. + +If we want to have any level of real dependency management, we'll need some kind +of package management. But then, the package manager itself becomes a +dependency. Everyone that wants to build our project has to install the correct +version. If you’re using CI, you have to set up everything on the build server +as well, which can be a pain. + +It would be nice if we can achieve our goals with just CMake, and that's what +we're gonna do. We'll go through at least 3 methods of integrating third party +libraries/modules. + +CMake ExternalProject +===================== + +CMake introduced a module called ExternalProject in version 3.0. ExternalProject +wraps dependencies into a CMake target and allows managing foreign code from +your CMakeLists.txt. + +To use it, one must add a target via ExternalProject_Add(). CMake will then run +the following steps for this target: + +:DOWNLOAD: Download the dependency. Here one can use a version control system or + download from an URL. + +:UPDATE: Update the downloaded code if anything changed since the last CMake + run. + +:CONFIGURE: Configure the project code. + +:BUILD: Build the dependencies code. + +:INSTALL: Install the built code into a specified directory. + +:TEST: (optional) Run tests. + +All the above commands are configurable. ExternalProject also allows for custom +steps. For more information, have a look at the `documentation +<https://cmake.org/cmake/help/latest/module/ExternalProject.html>`_. + +This definitely works with one small issue: when using ExternalProject, all its +steps will run at build time. This means that CMake downloads and builds your +dependencies after the generation step. So your dependencies will not be +available yet when CMake configures your project. If this is acceptable, then +`ExternalProject` is a go. + +CMake FetchContent +================== + +With version 3.11 CMake introduced a new module: `FetchContent +<https://cmake.org/cmake/help/latest/module/FetchContent.html>`_. The module +offers the same functionality as ExternalProject but will download dependencies +before the configure step. + +Here is an example of how to use it to fetch, configure, build and make +available the Google Test libraries in the project. + +.. code-block:: CMake + + include(FetchContent) + # We make sure that we have 'thirdparty' in the name so that the targets get + # excluded from the generated target lists for the various tools. + set(FETCHCONTENT_BASE_DIR ${CMAKE_BINARY_DIR}/third_party_deps) + FetchContent_Declare( + googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG # GoogleTest now follows the Abseil Live at Head philosophy. We + # recommend using the latest commit in the main branch in your + # projects. + origin/main) + # For Windows: Prevent overriding the parent project's compiler/linker settings + set(gtest_force_shared_crt + ON + CACHE BOOL "" FORCE) + FetchContent_MakeAvailable(googletest) + +`FetchContent` works very well but there are a couple of things to keep in mind +when using it: + +- **Downloading requires an internet connection** + Of course, you have to be online for first downloading your dependencies. + Compared to using git submodules, this requirement is now hidden. So you might + forget about it when building your code. To mitigate this problem, there are + options we can use: + + - `FETCHCONTENT_FULLY_DISCONNECTED=ON` will skip the DOWNLOAD and UPDATE steps + - `FETCHCONTENT_UPDATES_DISCONNECTED=ON` will skip the UPDATE step + +- **The library has to be installable** + Every once in a while you will come across libraries that are missing the call + to install() in their CMakeLists.txt. In this case, FetchContent does not know + how to copy the built code into the install folder and will fail. + +FetchContent works best with CMake based dependencies. I haven’t had a chance to +test it with libraries that are not built with CMake. But I would expect that +some extra configuration is necessary to make it work. + +Git submodules +============== + +For dependencies with a modern CMake setup, this is enough to make it available +to your project. Several of the third party dependencies used in `asap` are +integrated this way. It's very simple to integrate in the master project +CMakeLists.txt, just like adding a regular module in a subdirectory. + +.. code-block:: CMake + + # --------------------------------------------- + # Third party modules + # --------------------------------------------- + + add_subdirectory(third_party) + +To keep third party modules configuration in a single place, all modules are +located under the ```third_party``` subdirectory. That directory has a +`CMakeLists.txt` file which only contains configuration for third party modules +before they are included. Here is an example for `spdlog`: + +.. code-block:: + + # ------------------------------------------------------- + # spdlog + # ------------------------------------------------------- + + # We want the spdlog install target to be generated even + # though we are using it as a submodule. The reason for + # that is that we want our project to be self contained + # with all dependencies included. + set(SPDLOG_INSTALL + ON + CACHE BOOL "Generate the spdlog install target") + + set(SPDLOG_FMT_EXTERNAL + ON + CACHE BOOL "Use external fmt library instead of bundled") + + if(WIN32) + set(SPDLOG_WCHAR_SUPPORT + ON + CACHE BOOL "Support wchar api") + set(SPDLOG_WCHAR_FILENAMES + ON + CACHE BOOL "Support wchar filenames") + endif() + + add_subdirectory(spdlog) + +This approach also works well and is the preferred approach for modules that use +CMake as their build system. It does however require some familiarity with git +submodules. + +.. tip:: + + Visit the `Git Submodule Tutorial + <https://git.wiki.kernel.org/index.php/GitSubmoduleTutorial>`_. + +Git configuration +----------------- + +To make the work with git submodules more reliable, it is strongly recommended +to have the following git configuration setup: + +.. prompt:: shell, $ + + git config --global diff.submodule log + git config --global status.submoduleSummary true + +Adding a git submodule +---------------------- + +Adding submodules is easy: + +.. prompt:: shell, $ + + git submodule add https://github.com/gabime/spdlog.git + +This will have created a .gitmodules in the project directory if none already +exists. Future cloning of the project will automatically add the submodules +while fetching the project repo. + +By default, submodules will add the sub-project into a directory named the same +as the repository, in this case `spdlog`. You can add a different path at the +end of the command if you want it to go elsewhere. + +Finally we can commit the changes. + +.. prompt:: shell, $ + + git commit -m "Added submodules" + +Grabbing updates +---------------- + +When pulling updates from the container repo's remote, Git auto-fetches, but +does not auto-update. The local cache is up-to-date with the submodule’s remote, +but the submodule’s working directory stays with its former contents. Although +this auto-fetching is limited to already-known submodules: any new ones, not yet +copied into local configuration, are not auto-fetched. + +If you don’t explicitly update the submodule’s working directory, your next +container commit will regress the submodule. Is is therefore mandatory that you +finalize the update. + +.. prompt:: shell, $ + + git pull + git submodule sync --recursive + git submodule update --init --recursive + +Deleting git submodules +----------------------- + +To remove a submodule you need to: + +1. Delete the relevant section from the `.gitmodules` file. +2. Stage the .gitmodules changes: + + .. prompt:: shell, $ + + git add .gitmodules + +3. Delete the relevant section from `.git/config`. +4. Remove the submodule files from the working tree and index: + + .. prompt:: shell, $ + + git rm --cached path_to_submodule (no trailing slash). + +5. Remove the submodule's `.git` directory: + + .. prompt:: shell, $ + + rm -rf .git/modules/path_to_submodule + +6. Commit the changes: + + .. prompt:: shell, $ + + git commit -m "Removed submodule <name>" + +7. Delete the now untracked submodule files: + + .. prompt:: shell, $ + + rm -rf path_to_submodule diff --git a/doc/03-documentation/doxygen-doc.rst b/doc/03-documentation/doxygen-doc.rst new file mode 100644 index 0000000..58e4a5e --- /dev/null +++ b/doc/03-documentation/doxygen-doc.rst @@ -0,0 +1,63 @@ +.. Structure conventions + # with overline, for parts + * with overline, for chapters + = for sections + - for subsections + ^ for sub-subsections + " for paragraphs + +**************** +Documenting code +**************** + +Writing documentation +===================== + +Doxygen uses special comment blocks that contain documentation directives. +Multiple styles are supported by doxygen for these special comment blocks, and +you can use any of them. It is strongly recommended however to stick to a single +style for the entirety of your code base. + +.. tip:: + + Check the `Doxygen documentation + <https://www.doxygen.nl/manual/starting.html>`_ for detailed information on + how doxygeen works. + +Themes and styling the documentation +==================================== + +The project uses `Doxygen Awesome +<https://jothepro.github.io/doxygen-awesome-css/>`_ theme which generates +functional and awesome looking HTML documentation. It can be customized to fit +your needs. + +All the configuration files for `doxygen` are located in the `doxygen` directory +under the project root. + +Building the documentation +========================== + +For consistency reasons, the documentation build uses CMake similar to what is +done for the source code and related artifacts. Specific targets are defined for +generating the *API** documentation using `doxygen`. + +From within the `build` directory, use the following commands to generate the +documentation: + +.. prompt:: bash $,(env)...$ auto + + $ cmake --build . --target dox + +The generated documentation in `HTML` will be available under the folders `dox` +as shown below: + +.. code-block:: text + + ...\_BUILD\DOX + ├───asap_common + │ ├───html + │ └───xml + └───asap_logging + ├───html + └───xml diff --git a/doc/03-documentation/index.rst b/doc/03-documentation/index.rst new file mode 100644 index 0000000..72554db --- /dev/null +++ b/doc/03-documentation/index.rst @@ -0,0 +1,45 @@ +.. Structure conventions + # with overline, for parts + * with overline, for chapters + = for sections + - for subsections + ^ for sub-subsections + " for paragraphs + +############# +Documentation +############# + +.. toctree:: + :maxdepth: 1 + :titlesonly: + :hidden: + + doxygen-doc + sphinx-doc + +The documentation for the project covers two complementary parts: *APIs* and +*User Manual*. + +The APIs are documented inline with the source code comments using the `doxygen +<https://www.doxygen.nl/manual/docblocks.html>`_ style, while the *User Manual* +is written in `restructuredText +<https://en.wikipedia.org/wiki/ReStructuredText>`_ and uses `Sphinx +<https://www.sphinx-doc.org/en/master/index.html>`_ to generate the final output +(HTML, or other). + +The *User Manual* refers to the API documentation generated by *doxygen* and +therefore it is necessary to build the API documentation at least once before +you build the *User Manual* in order to have working links in previews and +generated files. + +:doc:`Doxygen <doxygen-doc>` +--------------------------------- +*start here to get a quick overview of how API documentation is generated from +the source code using `doxygen`* + +:doc:`Sphinx <sphinx-doc>` +--------------------------------- +*if you are maintaining parts in the user manual of this project, you should +read this document. It will help you understand how this User Manual is written +and transformed into the final HTML output* diff --git a/doc/03-documentation/sphinx-doc.rst b/doc/03-documentation/sphinx-doc.rst new file mode 100644 index 0000000..e6b99ec --- /dev/null +++ b/doc/03-documentation/sphinx-doc.rst @@ -0,0 +1,70 @@ +.. Structure conventions + # with overline, for parts + * with overline, for chapters + = for sections + - for subsections + ^ for sub-subsections + " for paragraphs + +**************************** +Sphinx and restructured text +**************************** + +Setting up the environment +========================== + +It is recommended to use a dedicated environment for python and the additional +extensions required for sphinx documentation. Managing python versions and +environments can be done in a multitude of ways. Describing all of them is out +of scope of this documentation. We do however provide instructions for doing so +with Anaconda/Miniconda. + +From within the root folder of the project, follow the instructions below: + +.. prompt:: bash $,(env)...$ auto + + $ conda create --prefix=./.sphinx python=3.9 + $ conda activate $PWD/.sphinx + (env)...$ pip install -r requirements.txt + +This will have python 3.9 installed in a dedicated environment in the `.sphinx` +directory under the project root. All python modules required to produce the +documentation will be installed and ready to use. + +Building the documentation +========================== + +For consistency reasons, the documentation build uses CMake similar to what is +done for the source code and related artifacts. Specific targets are defined for +generating the *API** documentation and the *User Manual*. + +From within the `build` directory, use the following commands to generate the +documentation: + +.. prompt:: bash $,(env)...$ auto + + $ cmake --build . --target dox + $ cmake --build . --target sphinx + +The generated documentation in `HTML` will be available under the folders `dox` +and `sphinx` as shown below: + +.. code-block:: text + + ...\_BUILD\DOX + ├───asap_common + │ ├───html + │ └───xml + └───asap_logging + ├───html + └───xml + + ...\_BUILD\SPHINX + ├───asap_common + │ └───html + │ └───api + ├───asap_logging + │ └───html + │ └───api + └───master + └───html diff --git a/doc/04-library-modules/index.rst b/doc/04-library-modules/index.rst new file mode 100644 index 0000000..1a2009e --- /dev/null +++ b/doc/04-library-modules/index.rst @@ -0,0 +1,14 @@ +.. Structure conventions + # with overline, for parts + * with overline, for chapters + = for sections + - for subsections + ^ for sub-subsections + " for paragraphs + +############### +Library Modules +############### + +:doc:`common <common:api>` +========================== diff --git a/doc/_static/favicon.ico b/doc/_static/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..e2da7c884008a26f52634b2c5e2b3ade92a4705a GIT binary patch literal 178091 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 Xz};O&0NL{Rp92(S)!tW0e+u~z;o@TU literal 0 HcmV?d00001 diff --git a/doc/_static/logo.png b/doc/_static/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..4ca2442366da0413a602034a62330c08407e9e4f GIT binary patch literal 34593 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 literal 0 HcmV?d00001 diff --git a/doc/_static/windows_terminal_posh.png b/doc/_static/windows_terminal_posh.png new file mode 100644 index 0000000000000000000000000000000000000000..e6644691106ae48df259b297e723497f4485c52d GIT binary patch literal 75225 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≪=WP1G-YRR40^RmS8)BC<fN> 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 literal 0 HcmV?d00001 diff --git a/doc/changelog.md b/doc/changelog.md new file mode 100644 index 0000000..66efc0f --- /dev/null +++ b/doc/changelog.md @@ -0,0 +1,2 @@ +```{include} ../CHANGELOG.md +``` diff --git a/doc/conf.py.in b/doc/conf.py.in new file mode 100644 index 0000000..abf8337 --- /dev/null +++ b/doc/conf.py.in @@ -0,0 +1,134 @@ +# -*- coding: utf-8 -*- +# +# Configuration file for the Sphinx documentation builder. +# +# This file does only contain a selection of the most common options. For a +# full list see the documentation: +# http://www.sphinx-doc.org/en/master/config + +# -- Path setup -------------------------------------------------------------- + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +# import os +# import sys +# sys.path.insert(0, os.path.abspath('.')) + + +# -- Project information ----------------------------------------------------- + +project = u'@META_PROJECT_NAME@' +copyright = u'2018, @META_AUTHOR_ORGANIZATION@' + +# The short X.Y version +version = u'@META_VERSION@' +# The full version, including alpha/beta/rc tags +release = u'@META_VERSION@ (@META_VERSION_REVISION@)' + +rst_epilog = """ +.. |version| replace:: {0} +.. |release| replace:: {1} +""".format(version, release) + +# -- General configuration --------------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. +# +# needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + 'sphinx.ext.intersphinx', + 'sphinx.ext.todo', + 'sphinx.ext.coverage', + 'sphinx.ext.viewcode', + 'sphinx-prompt', + 'sphinx_copybutton', + 'myst_parser' +] + +# Tell sphinx what the primary language being documented is. +primary_domain = 'cpp' + +# Tell sphinx what the pygments highlight language should be. +highlight_language = 'cpp' + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, so +# a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ["_static"] + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. This pattern also affects +# html_static_path and html_extra_path. +exclude_patterns = ["_build", "Thumbs.db", ".DS_Store", ".sphinx"] + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +# +# source_suffix = ['.rst', '.md'] +source_suffix = '.rst' + +# The master toctree document. +master_doc = 'index' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +language = None + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + + +# -- Options for HTML output ------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = "sphinx_book_theme" + +html_title = u'Start Now!' +html_logo = "_static/logo.png" +html_favicon = "_static/favicon.ico" + +# If this is not None, a ‘Last updated on:’ timestamp is inserted at every page +# bottom, using the given strftime() format. The empty string is equivalent to +# '%b %d, %Y' (or a locale-dependent equivalent). +html_last_updated_fmt = None + +html_theme_options = { + "repository_url": '@META_GITHUB_REPO@', + "use_repository_button": True, + "use_issues_button": True, + "use_edit_page_button": False, + "use_download_button": True, + "use_fullscreen_button": True, + # We don't want the default navigation bar footer to be displayed on every + # page. Mention of the book theme will be added in the home page. + "extra_navbar": "" +} + +# -- Extension configuration ------------------------------------------------- + +# -- Options for intersphinx extension --------------------------------------- + +intersphinx_mapping = { + 'common': ( + '@SPHINX_BUILD_DIR@/asap_common/html', + '@SPHINX_BUILD_DIR@/asap_common/html/objects.inv') +} + +# -- Options for todo extension ---------------------------------------------- + +# If true, `todo` and `todoList` produce output, else they produce nothing. +todo_include_todos = True diff --git a/doc/index.html b/doc/index.html new file mode 100644 index 0000000..efdd7ce --- /dev/null +++ b/doc/index.html @@ -0,0 +1,44 @@ +<html> + +<head> + <style> + p { + text-align: center; + } + </style> + <script> + // Countdown timer for redirecting to another URL after several seconds + var seconds = 5; // seconds for HTML + var foo; // variable for clearInterval() function + + function updateSecs() { + document.getElementById("seconds").innerHTML = seconds; + seconds--; + if (seconds == -1) { + clearInterval(foo); + } + } + + function countdownTimer() { + foo = setInterval(function () { + updateSecs() + }, 1000); + } + + countdownTimer(); + </script> + <meta http-equiv="refresh" content="5; URL=master/html/index.html" /> +</head> + +<body> + <p><img style="display: block; margin-left: auto; margin-right: auto;" src="master/html/_static/logo.png" alt="Logo" + width="428" height="427" />Redirecting you automatically in <span id="seconds"></span> to the master + documentation starting page...</p> + <p>Or you can instead go one of the modules documentation:</p> + <p><a href="asap_common/html/index.html">common</a></p> + <p><a href="asap_logging/html/index.html">logging</a></p> + <p><a href="asap_fsm/html/index.html">fsm</a></p> + <p><a href="asap_textwrap/html/index.html">textwrap</a></p> +</body> + +</html> diff --git a/doc/index.rst b/doc/index.rst new file mode 100644 index 0000000..d643bcc --- /dev/null +++ b/doc/index.rst @@ -0,0 +1,75 @@ +.. Structure conventions + # with overline, for parts + * with overline, for chapters + = for sections + - for subsections + ^ for sub-subsections + " for paragraphs + +################### +ASAP documentation! +################### + +.. |date| date:: + +Last Updated on |date| + +.. toctree:: + :maxdepth: 2 + :titlesonly: + :hidden: + + 01-getting-started/index + 02-project-development/index + 03-documentation/index + 04-library-modules/index + license + changelog.md + version + +Welcome! This is the documentation for the *asap* project. This is the top level +container project for the asap modules. It provides the integrated build system +and the overall structure for projects following the asap development workflow. + +The main goal from this family of modules and projects is to significantly cut +the bootstrap time of starting a new c++ application, using CMake as the build +system. + +Parts of the documentation +========================== + +:doc:`Getting Started! <01-getting-started/index>` +-------------------------------------------------- + +*start here to understand how to use this project as a starter for your own +project* + +:doc:`Project Development <02-project-development/index>` +--------------------------------------------------------- + +*refer to this part of the documentation to understand the build system +specifics for this project, the development workflow, coding and unit testing +guidelines* + +:doc:`Documentation <03-documentation/index>` +--------------------------------------------------- + +*refer to this part of the documentation to understand how project documentation +is structured and built from source using `doxygen` and `sphinx`.* + +:doc:`Library Modules <04-library-modules/index>` +------------------------------------------------- +*check this out to explore the different modules part of this project. From +there, you can also jump to the detailed API documentation of each of those +modules.* + +Acknowledgements +================ + +.. figure:: https://executablebooks.org/en/latest/_static/logo-wide.png + :figclass: margin + :alt: Executable Books Project + :name: executable_book_logo + +This documentation uses the theme provided by the `Executable Books Project +<https://executablebooks.org/>`_ Project. diff --git a/doc/license.rst b/doc/license.rst new file mode 100644 index 0000000..b9c6233 --- /dev/null +++ b/doc/license.rst @@ -0,0 +1,14 @@ +.. Structure conventions + # with overline, for parts + * with overline, for chapters + = for sections + - for subsections + ^ for sub-subsections + " for paragraphs + +******* +License +******* + +.. include:: ../LICENSE + diff --git a/doc/version.rst b/doc/version.rst new file mode 100644 index 0000000..05f835d --- /dev/null +++ b/doc/version.rst @@ -0,0 +1,13 @@ +.. Structure conventions + # with overline, for parts + * with overline, for chapters + = for sections + - for subsections + ^ for sub-subsections + " for paragraphs + +******* +Version +******* + +|release| diff --git a/doxygen/Doxyfile.in b/doxygen/Doxyfile.in new file mode 100644 index 0000000..9b41899 --- /dev/null +++ b/doxygen/Doxyfile.in @@ -0,0 +1,2658 @@ +# Doxyfile 1.9.3 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a double hash (##) is considered a comment and is placed in +# front of the TAG it is preceding. +# +# All text after a single hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists, items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (\" \"). + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the configuration +# file that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# https://www.gnu.org/software/libiconv/ for the list of possible encodings. +# The default value is: UTF-8. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by +# double-quotes, unless you are using Doxywizard) that should identify the +# project for which the documentation is generated. This name is used in the +# title of most generated pages and in a few other places. +# The default value is: My Project. + +PROJECT_NAME = @DOXY_TITLE@ + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. This +# could be handy for archiving the generated documentation or if some version +# control system is used. + +PROJECT_NUMBER = @DOXY_MODULE_VERSION@ + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer a +# quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = @DOXY_BRIEF@ + +# With the PROJECT_LOGO tag one can specify a logo or an icon that is included +# in the documentation. The maximum height of the logo should not exceed 55 +# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy +# the logo to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path +# into which the generated documentation will be written. If a relative path is +# entered, it will be relative to the location where doxygen was started. If +# left blank the current directory will be used. + +OUTPUT_DIRECTORY = @DOXY_OUTPUT_DIR@ + +# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- +# directories (in 2 levels) under the output directory of each output format and +# will distribute the generated files over these directories. Enabling this +# option can be useful when feeding doxygen a huge amount of source files, where +# putting all generated files in the same directory would otherwise causes +# performance problems for the file system. +# The default value is: NO. + +CREATE_SUBDIRS = NO + +# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII +# characters to appear in the names of generated files. If set to NO, non-ASCII +# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode +# U+3044. +# The default value is: NO. + +ALLOW_UNICODE_NAMES = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, +# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), +# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, +# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), +# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, +# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, +# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, +# Ukrainian and Vietnamese. +# The default value is: English. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member +# descriptions after the members that are listed in the file and class +# documentation (similar to Javadoc). Set to NO to disable this. +# The default value is: YES. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief +# description of a member or function before the detailed description +# +# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. +# The default value is: YES. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator that is +# used to form the text in various listings. Each string in this list, if found +# as the leading text of the brief description, will be stripped from the text +# and the result, after processing the whole list, is used as the annotated +# text. Otherwise, the brief description is used as-is. If left blank, the +# following values are used ($name is automatically replaced with the name of +# the entity):The $name class, The $name widget, The $name file, is, provides, +# specifies, contains, represents, a, an and the. + +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# doxygen will generate a detailed section even if there is only a brief +# description. +# The default value is: NO. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. +# The default value is: NO. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path +# before files name in the file list and in the header files. If set to NO the +# shortest path that makes the file name unique will be used +# The default value is: YES. + +FULL_PATH_NAMES = YES + +# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. +# Stripping is only done if one of the specified strings matches the left-hand +# part of the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the path to +# strip. +# +# Note that you can specify absolute paths here, but also relative paths, which +# will be relative from the directory where doxygen is started. +# This tag requires that the tag FULL_PATH_NAMES is set to YES. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the +# path mentioned in the documentation of a class, which tells the reader which +# header file to include in order to use a class. If left blank only the name of +# the header file containing the class definition is used. Otherwise one should +# specify the list of include paths that are normally passed to the compiler +# using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but +# less readable) file names. This can be useful is your file systems doesn't +# support long names like on DOS, Mac, or CD-ROM. +# The default value is: NO. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the +# first line (until the first dot) of a Javadoc-style comment as the brief +# description. If set to NO, the Javadoc-style will behave just like regular Qt- +# style comments (thus requiring an explicit @brief command for a brief +# description.) +# The default value is: NO. + +JAVADOC_AUTOBRIEF = NO + +# If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line +# such as +# /*************** +# as being the beginning of a Javadoc-style comment "banner". If set to NO, the +# Javadoc-style will behave just like regular comments and it will not be +# interpreted by doxygen. +# The default value is: NO. + +JAVADOC_BANNER = NO + +# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first +# line (until the first dot) of a Qt-style comment as the brief description. If +# set to NO, the Qt-style will behave just like regular Qt-style comments (thus +# requiring an explicit \brief command for a brief description.) +# The default value is: NO. + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a +# multi-line C++ special comment block (i.e. a block of //! or /// comments) as +# a brief description. This used to be the default behavior. The new default is +# to treat a multi-line C++ comment block as a detailed description. Set this +# tag to YES if you prefer the old behavior instead. +# +# Note that setting this tag to YES also means that rational rose comments are +# not recognized any more. +# The default value is: NO. + +MULTILINE_CPP_IS_BRIEF = NO + +# By default Python docstrings are displayed as preformatted text and doxygen's +# special commands cannot be used. By setting PYTHON_DOCSTRING to NO the +# doxygen's special commands can be used and the contents of the docstring +# documentation blocks is shown as doxygen documentation. +# The default value is: YES. + +PYTHON_DOCSTRING = YES + +# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the +# documentation from any documented member that it re-implements. +# The default value is: YES. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new +# page for each member. If set to NO, the documentation of a member will be part +# of the file/class/namespace that contains it. +# The default value is: NO. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen +# uses this value to replace tabs by spaces in code fragments. +# Minimum value: 1, maximum value: 16, default value: 4. + +TAB_SIZE = 2 + +# This tag can be used to specify a number of aliases that act as commands in +# the documentation. An alias has the form: +# name=value +# For example adding +# "sideeffect=@par Side Effects:^^" +# will allow you to put the command \sideeffect (or @sideeffect) in the +# documentation, which will result in a user-defined paragraph with heading +# "Side Effects:". Note that you cannot put \n's in the value part of an alias +# to insert newlines (in the resulting output). You can put ^^ in the value part +# of an alias to insert a newline as if a physical newline was in the original +# file. When you need a literal { or } or , in the value part of an alias you +# have to escape them by means of a backslash (\), this can lead to conflicts +# with the commands \{ and \} for these it is advised to use the version @{ and +# @} or use a double escape (\\{ and \\}) + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. For +# instance, some of the names that are used will be different. The list of all +# members will be omitted, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or +# Python sources only. Doxygen will then generate output that is more tailored +# for that language. For instance, namespaces will be presented as packages, +# qualified scopes will look different, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources. Doxygen will then generate output that is tailored for Fortran. +# The default value is: NO. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for VHDL. +# The default value is: NO. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice +# sources only. Doxygen will then generate output that is more tailored for that +# language. For instance, namespaces will be presented as modules, types will be +# separated into more groups, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_SLICE = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, and +# language is one of the parsers supported by doxygen: IDL, Java, JavaScript, +# Csharp (C#), C, C++, Lex, D, PHP, md (Markdown), Objective-C, Python, Slice, +# VHDL, Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: +# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser +# tries to guess whether the code is fixed or free formatted code, this is the +# default for Fortran type files). For instance to make doxygen treat .inc files +# as Fortran files (default is PHP), and .f files as C (default is Fortran), +# use: inc=Fortran f=C. +# +# Note: For files without extension you can use no_extension as a placeholder. +# +# Note that for custom extensions you also need to set FILE_PATTERNS otherwise +# the files are not read by doxygen. When specifying no_extension you should add +# * to the FILE_PATTERNS. +# +# Note see also the list of default file extension mappings. + +EXTENSION_MAPPING = + +# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments +# according to the Markdown format, which allows for more readable +# documentation. See https://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you can +# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in +# case of backward compatibilities issues. +# The default value is: YES. + +MARKDOWN_SUPPORT = YES + +# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up +# to that level are automatically included in the table of contents, even if +# they do not have an id attribute. +# Note: This feature currently applies only to Markdown headings. +# Minimum value: 0, maximum value: 99, default value: 5. +# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. + +TOC_INCLUDE_HEADINGS = 5 + +# When enabled doxygen tries to link words that correspond to documented +# classes, or namespaces to their corresponding documentation. Such a link can +# be prevented in individual cases by putting a % sign in front of the word or +# globally by setting AUTOLINK_SUPPORT to NO. +# The default value is: YES. + +AUTOLINK_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should set this +# tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); +# versus func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. +# The default value is: NO. + +BUILTIN_STL_SUPPORT = YES + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. +# The default value is: NO. + +CPP_CLI_SUPPORT = YES + +# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: +# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen +# will parse them like normal C++ but will assume all classes use public instead +# of private inheritance when no explicit protection keyword is present. +# The default value is: NO. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES will make +# doxygen to replace the get and set methods by a property in the documentation. +# This will only work if the methods are indeed getting or setting a simple +# type. If this is not the case, or you want to show the methods anyway, you +# should set this option to NO. +# The default value is: YES. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. +# The default value is: NO. + +DISTRIBUTE_GROUP_DOC = NO + +# If one adds a struct or class to a group and this option is enabled, then also +# any nested class or struct is added to the same group. By default this option +# is disabled and one has to add nested compounds explicitly via \ingroup. +# The default value is: NO. + +GROUP_NESTED_COMPOUNDS = NO + +# Set the SUBGROUPING tag to YES to allow class member groups of the same type +# (for instance a group of public functions) to be put as a subgroup of that +# type (e.g. under the Public Functions section). Set it to NO to prevent +# subgrouping. Alternatively, this can be done per class using the +# \nosubgrouping command. +# The default value is: YES. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions +# are shown inside the group in which they are included (e.g. using \ingroup) +# instead of on a separate page (for HTML and Man pages) or section (for LaTeX +# and RTF). +# +# Note that this feature does not work in combination with +# SEPARATE_MEMBER_PAGES. +# The default value is: NO. + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions +# with only public data fields or simple typedef fields will be shown inline in +# the documentation of the scope in which they are defined (i.e. file, +# namespace, or group documentation), provided this scope is documented. If set +# to NO, structs, classes, and unions are shown on a separate page (for HTML and +# Man pages) or section (for LaTeX and RTF). +# The default value is: NO. + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or +# enum is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically be +# useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. +# The default value is: NO. + +TYPEDEF_HIDES_STRUCT = NO + +# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This +# cache is used to resolve symbols given their name and scope. Since this can be +# an expensive process and often the same symbol appears multiple times in the +# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small +# doxygen will become slower. If the cache is too large, memory is wasted. The +# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range +# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 +# symbols. At the end of a run doxygen will report the cache usage and suggest +# the optimal cache size from a speed point of view. +# Minimum value: 0, maximum value: 9, default value: 0. + +LOOKUP_CACHE_SIZE = 0 + +# The NUM_PROC_THREADS specifies the number threads doxygen is allowed to use +# during processing. When set to 0 doxygen will based this on the number of +# cores available in the system. You can set it explicitly to a value larger +# than 0 to get more control over the balance between CPU load and processing +# speed. At this moment only the input processing can be done using multiple +# threads. Since this is still an experimental feature the default is set to 1, +# which effectively disables parallel processing. Please report any issues you +# encounter. Generating dot graphs in parallel is controlled by the +# DOT_NUM_THREADS setting. +# Minimum value: 0, maximum value: 32, default value: 1. + +NUM_PROC_THREADS = 1 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in +# documentation are documented, even if no documentation was available. Private +# class members and static file members will be hidden unless the +# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. +# Note: This will also disable the warnings about undocumented members that are +# normally produced when WARNINGS is set to YES. +# The default value is: NO. + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will +# be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual +# methods of a class will be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIV_VIRTUAL = NO + +# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal +# scope will be included in the documentation. +# The default value is: NO. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be +# included in the documentation. +# The default value is: NO. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined +# locally in source files will be included in the documentation. If set to NO, +# only classes defined in header files are included. Does not have any effect +# for Java sources. +# The default value is: YES. + +EXTRACT_LOCAL_CLASSES = NO + +# This flag is only useful for Objective-C code. If set to YES, local methods, +# which are defined in the implementation section but not in the interface are +# included in the documentation. If set to NO, only methods in the interface are +# included. +# The default value is: NO. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base name of +# the file that contains the anonymous namespace. By default anonymous namespace +# are hidden. +# The default value is: NO. + +EXTRACT_ANON_NSPACES = NO + +# If this flag is set to YES, the name of an unnamed parameter in a declaration +# will be determined by the corresponding definition. By default unnamed +# parameters remain unnamed in the output. +# The default value is: YES. + +RESOLVE_UNNAMED_PARAMS = YES + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all +# undocumented members inside documented classes or files. If set to NO these +# members will be included in the various overviews, but no documentation +# section is generated. This option has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. If set +# to NO, these classes will be included in the various overviews. This option +# has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend +# declarations. If set to NO, these declarations will be included in the +# documentation. +# The default value is: NO. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any +# documentation blocks found inside the body of a function. If set to NO, these +# blocks will be appended to the function's detailed documentation block. +# The default value is: NO. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation that is typed after a +# \internal command is included. If the tag is set to NO then the documentation +# will be excluded. Set it to YES to include the internal documentation. +# The default value is: NO. + +INTERNAL_DOCS = NO + +# With the correct setting of option CASE_SENSE_NAMES doxygen will better be +# able to match the capabilities of the underlying filesystem. In case the +# filesystem is case sensitive (i.e. it supports files in the same directory +# whose names only differ in casing), the option must be set to YES to properly +# deal with such files in case they appear in the input. For filesystems that +# are not case sensitive the option should be be set to NO to properly deal with +# output files written for symbols that only differ in casing, such as for two +# classes, one named CLASS and the other named Class, and to also support +# references to files without having to specify the exact matching casing. On +# Windows (including Cygwin) and MacOS, users should typically set this option +# to NO, whereas on Linux or other Unix flavors it should typically be set to +# YES. +# The default value is: system dependent. + +CASE_SENSE_NAMES = NO + +# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with +# their full class and namespace scopes in the documentation. If set to YES, the +# scope will be hidden. +# The default value is: NO. + +HIDE_SCOPE_NAMES = NO + +# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will +# append additional text to a page's title, such as Class Reference. If set to +# YES the compound reference will be hidden. +# The default value is: NO. + +HIDE_COMPOUND_REFERENCE= NO + +# If the SHOW_HEADERFILE tag is set to YES then the documentation for a class +# will show which file needs to be included to use the class. +# The default value is: YES. + +SHOW_HEADERFILE = YES + +# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of +# the files that are included by a file in the documentation of that file. +# The default value is: YES. + +SHOW_INCLUDE_FILES = YES + +# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each +# grouped member an include statement to the documentation, telling the reader +# which file to include in order to use the member. +# The default value is: NO. + +SHOW_GROUPED_MEMB_INC = NO + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include +# files with double quotes in the documentation rather than with sharp brackets. +# The default value is: NO. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the +# documentation for inline members. +# The default value is: YES. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the +# (detailed) documentation of file and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. +# The default value is: YES. + +SORT_MEMBER_DOCS = NO + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief +# descriptions of file, namespace and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. Note that +# this will also influence the order of the classes in the class list. +# The default value is: NO. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the +# (brief and detailed) documentation of class members so that constructors and +# destructors are listed first. If set to NO the constructors will appear in the +# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. +# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief +# member documentation. +# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting +# detailed member documentation. +# The default value is: NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy +# of group names into alphabetical order. If set to NO the group names will +# appear in their defined order. +# The default value is: NO. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by +# fully-qualified names, including namespaces. If set to NO, the class list will +# be sorted only by class name, not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the alphabetical +# list. +# The default value is: NO. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper +# type resolution of all parameters of a function it will reject a match between +# the prototype and the implementation of a member function even if there is +# only one candidate or it is obvious which candidate to choose by doing a +# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still +# accept a match between prototype and implementation in such cases. +# The default value is: NO. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo +# list. This list is created by putting \todo commands in the documentation. +# The default value is: YES. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test +# list. This list is created by putting \test commands in the documentation. +# The default value is: YES. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug +# list. This list is created by putting \bug commands in the documentation. +# The default value is: YES. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) +# the deprecated list. This list is created by putting \deprecated commands in +# the documentation. +# The default value is: YES. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional documentation +# sections, marked by \if <section_label> ... \endif and \cond <section_label> +# ... \endcond blocks. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the +# initial value of a variable or macro / define can have for it to appear in the +# documentation. If the initializer consists of more lines than specified here +# it will be hidden. Use a value of 0 to hide initializers completely. The +# appearance of the value of individual variables and macros / defines can be +# controlled using \showinitializer or \hideinitializer command in the +# documentation regardless of this setting. +# Minimum value: 0, maximum value: 10000, default value: 30. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at +# the bottom of the documentation of classes and structs. If set to YES, the +# list will mention the files that were used to generate the documentation. +# The default value is: YES. + +SHOW_USED_FILES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This +# will remove the Files entry from the Quick Index and from the Folder Tree View +# (if specified). +# The default value is: YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces +# page. This will remove the Namespaces entry from the Quick Index and from the +# Folder Tree View (if specified). +# The default value is: YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command command input-file, where command is the value of the +# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided +# by doxygen. Whatever the program writes to standard output is used as the file +# version. For an example see the documentation. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. You can +# optionally specify a file name after the option, if omitted DoxygenLayout.xml +# will be used as the name of the layout file. See also section "Changing the +# layout of pages" for information. +# +# Note that if you run doxygen from a directory containing a file called +# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE +# tag is left empty. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files containing +# the reference definitions. This must be a list of .bib files. The .bib +# extension is automatically appended if omitted. This requires the bibtex tool +# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info. +# For LaTeX the style of the bibliography can be controlled using +# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the +# search path. See also \cite for info how to create references. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated to +# standard output by doxygen. If QUIET is set to YES this implies that the +# messages are off. +# The default value is: NO. + +QUIET = YES + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES +# this implies that the warnings are on. +# +# Tip: Turn warnings on while writing the documentation. +# The default value is: YES. + +WARNINGS = YES + +# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate +# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: YES. + +WARN_IF_UNDOCUMENTED = YES + +# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as documenting some parameters in +# a documented function twice, or documenting parameters that don't exist or +# using markup commands wrongly. +# The default value is: YES. + +WARN_IF_DOC_ERROR = YES + +# If WARN_IF_INCOMPLETE_DOC is set to YES, doxygen will warn about incomplete +# function parameter documentation. If set to NO, doxygen will accept that some +# parameters have no documentation without warning. +# The default value is: YES. + +WARN_IF_INCOMPLETE_DOC = YES + +# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that +# are documented, but have no documentation for their parameters or return +# value. If set to NO, doxygen will only warn about wrong parameter +# documentation, but not about the absence of documentation. If EXTRACT_ALL is +# set to YES then this flag will automatically be disabled. See also +# WARN_IF_INCOMPLETE_DOC +# The default value is: NO. + +WARN_NO_PARAMDOC = NO + +# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when +# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS +# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but +# at the end of the doxygen process doxygen will return with a non-zero status. +# Possible values are: NO, YES and FAIL_ON_WARNINGS. +# The default value is: NO. + +WARN_AS_ERROR = NO + +# The WARN_FORMAT tag determines the format of the warning messages that doxygen +# can produce. The string should contain the $file, $line, and $text tags, which +# will be replaced by the file and line number from which the warning originated +# and the warning text. Optionally the format may contain $version, which will +# be replaced by the version of the file (if it could be obtained via +# FILE_VERSION_FILTER) +# The default value is: $file:$line: $text. + +WARN_FORMAT = "FIX: $file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning and error +# messages should be written. If left blank the output is written to standard +# error (stderr). In case the file specified cannot be opened for writing the +# warning and error messages are written to standard error. When as file - is +# specified the warning and error messages are written to standard output +# (stdout). + +WARN_LOGFILE = @DOXY_OUTPUT_DIR@/module_warnings.txt + +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag is used to specify the files and/or directories that contain +# documented source files. You may enter file names like myfile.cpp or +# directories like /usr/src/myproject. Separate the files or directories with +# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING +# Note: If this tag is empty the current directory is searched. + +INPUT = @DOXY_INPUT_PATH@ + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses +# libiconv (or the iconv built into libc) for the transcoding. See the libiconv +# documentation (see: +# https://www.gnu.org/software/libiconv/) for the list of possible encodings. +# The default value is: UTF-8. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and +# *.h) to filter out the source-files in the directories. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# read by doxygen. +# +# Note the list of default checked file patterns might differ from the list of +# default file extension mappings. +# +# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, +# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, +# *.hh, *.hxx, *.hpp, *.h++, *.l, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, +# *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C +# comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd, +# *.vhdl, *.ucf, *.qsf and *.ice. + +FILE_PATTERNS = *.c \ + *.cc \ + *.cxx \ + *.cpp \ + *.c++ \ + *.java \ + *.ii \ + *.ixx \ + *.ipp \ + *.i++ \ + *.inl \ + *.idl \ + *.ddl \ + *.odl \ + *.h \ + *.hh \ + *.hxx \ + *.hpp \ + *.h++ \ + *.cs \ + *.d \ + *.php \ + *.php4 \ + *.php5 \ + *.phtml \ + *.inc \ + *.m \ + *.markdown \ + *.md \ + *.mm \ + *.dox \ + *.py \ + *.pyw \ + *.f90 \ + *.f95 \ + *.f03 \ + *.f08 \ + *.f \ + *.for \ + *.tcl \ + *.vhd \ + *.vhdl \ + *.ucf \ + *.qsf + +# The RECURSIVE tag can be used to specify whether or not subdirectories should +# be searched for input files as well. +# The default value is: NO. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. +# The default value is: NO. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories for example use the pattern */test/* + +EXCLUDE_PATTERNS = */test/* + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# ANamespace::AClass, ANamespace::*Test +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories use the pattern */test/* + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or directories +# that contain example code fragments that are included (see the \include +# command). + +EXAMPLE_PATH = @DOXY_EXAMPLE_PATH@ + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank all +# files are included. + +EXAMPLE_PATTERNS = * + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude commands +# irrespective of the value of the RECURSIVE tag. +# The default value is: NO. + +EXAMPLE_RECURSIVE = YES + +# The IMAGE_PATH tag can be used to specify one or more files or directories +# that contain images that are to be included in the documentation (see the +# \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command: +# +# <filter> <input-file> +# +# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the +# name of an input file. Doxygen will then use the output that the filter +# program writes to standard output. If FILTER_PATTERNS is specified, this tag +# will be ignored. +# +# Note that the filter must not add or remove lines; it is applied before the +# code is scanned, but not when the output code is generated. If lines are added +# or removed, the anchors will not be placed correctly. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: pattern=filter +# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how +# filters are used. If the FILTER_PATTERNS tag is empty or if none of the +# patterns match the file name, INPUT_FILTER is applied. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will also be used to filter the input files that are used for +# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). +# The default value is: NO. + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and +# it is also possible to disable source filtering for a specific pattern using +# *.ext= (so without naming a filter). +# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page +# (index.html). This can be useful if you have a project on for instance GitHub +# and want to reuse the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = + +#--------------------------------------------------------------------------- +# Configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will be +# generated. Documented entities will be cross-referenced with these sources. +# +# Note: To get rid of all source code in the generated output, make sure that +# also VERBATIM_HEADERS is set to NO. +# The default value is: NO. + +SOURCE_BROWSER = YES + +# Setting the INLINE_SOURCES tag to YES will include the body of functions, +# classes and enums directly into the documentation. +# The default value is: NO. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any +# special comment blocks from generated source code fragments. Normal C, C++ and +# Fortran comments will always remain visible. +# The default value is: YES. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES then for each documented +# entity all documented functions referencing it will be listed. +# The default value is: NO. + +REFERENCED_BY_RELATION = YES + +# If the REFERENCES_RELATION tag is set to YES then for each documented function +# all documented entities called/used by that function will be listed. +# The default value is: NO. + +REFERENCES_RELATION = YES + +# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set +# to YES then the hyperlinks from functions in REFERENCES_RELATION and +# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will +# link to the documentation. +# The default value is: YES. + +REFERENCES_LINK_SOURCE = YES + +# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the +# source code will show a tooltip with additional information such as prototype, +# brief description and links to the definition and documentation. Since this +# will make the HTML file larger and loading of large files a bit slower, you +# can opt to disable this feature. +# The default value is: YES. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +SOURCE_TOOLTIPS = YES + +# If the USE_HTAGS tag is set to YES then the references to source code will +# point to the HTML generated by the htags(1) tool instead of doxygen built-in +# source browser. The htags tool is part of GNU's global source tagging system +# (see https://www.gnu.org/software/global/global.html). You will need version +# 4.8.6 or higher. +# +# To use it do the following: +# - Install the latest version of global +# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file +# - Make sure the INPUT points to the root of the source tree +# - Run doxygen as normal +# +# Doxygen will invoke htags (and that will in turn invoke gtags), so these +# tools must be available from the command line (i.e. in the search path). +# +# The result: instead of the source browser generated by doxygen, the links to +# source code will now point to the output of htags. +# The default value is: NO. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a +# verbatim copy of the header file for each class for which an include is +# specified. Set to NO to disable this. +# See also: Section \class. +# The default value is: YES. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# Configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all +# compounds will be generated. Enable this if the project contains a lot of +# classes, structs, unions or interfaces. +# The default value is: YES. + +ALPHABETICAL_INDEX = YES + +# In case all classes in a project start with a common prefix, all classes will +# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag +# can be used to specify a prefix (or a list of prefixes) that should be ignored +# while generating the index headers. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output +# The default value is: YES. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each +# generated HTML page (for example: .htm, .php, .asp). +# The default value is: .html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a user-defined HTML header file for +# each generated HTML page. If the tag is left blank doxygen will generate a +# standard header. +# +# To get valid HTML the header file that includes any scripts and style sheets +# that doxygen needs, which is dependent on the configuration options used (e.g. +# the setting GENERATE_TREEVIEW). It is highly recommended to start with a +# default header using +# doxygen -w html new_header.html new_footer.html new_stylesheet.css +# YourConfigFile +# and then modify the file new_header.html. See also section "Doxygen usage" +# for information on how to generate the default header that doxygen normally +# uses. +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. For a description +# of the possible markers and block names see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_HEADER = @CMAKE_SOURCE_DIR@/doxygen/header.html + +# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each +# generated HTML page. If the tag is left blank doxygen will generate a standard +# footer. See HTML_HEADER for more information on how to generate a default +# footer and what special commands can be used inside the footer. See also +# section "Doxygen usage" for information on how to generate the default footer +# that doxygen normally uses. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FOOTER = @CMAKE_SOURCE_DIR@/doxygen/footer.html + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style +# sheet that is used by each HTML page. It can be used to fine-tune the look of +# the HTML output. If left blank doxygen will generate a default style sheet. +# See also section "Doxygen usage" for information on how to generate the style +# sheet that doxygen normally uses. +# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as +# it is more robust and this tag (HTML_STYLESHEET) will in the future become +# obsolete. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# cascading style sheets that are included after the standard style sheets +# created by doxygen. Using this option one can overrule certain style aspects. +# This is preferred over using HTML_STYLESHEET since it does not replace the +# standard style sheet and is therefore more robust against future updates. +# Doxygen will copy the style sheet files to the output directory. +# Note: The order of the extra style sheet files is of importance (e.g. the last +# style sheet in the list overrules the setting of the previous ones in the +# list). For an example see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_STYLESHEET = @CMAKE_SOURCE_DIR@/doxygen/doxygen-awesome-css/doxygen-awesome.css \ + @CMAKE_SOURCE_DIR@/doxygen/doxygen-awesome-css/doxygen-awesome-sidebar-only.css \ + @CMAKE_SOURCE_DIR@/doxygen/doxygen-awesome-css/doxygen-awesome-sidebar-only-darkmode-toggle.css + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that the +# files will be copied as-is; there are no commands or markers available. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_FILES = @CMAKE_SOURCE_DIR@/doxygen/doxygen-awesome-css/doxygen-awesome-darkmode-toggle.js + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen +# will adjust the colors in the style sheet and background images according to +# this color. Hue is specified as an angle on a color-wheel, see +# https://en.wikipedia.org/wiki/Hue for more information. For instance the value +# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 +# purple, and 360 is red again. +# Minimum value: 0, maximum value: 359, default value: 220. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors +# in the HTML output. For a value of 0 the output will use gray-scales only. A +# value of 255 will produce the most vivid colors. +# Minimum value: 0, maximum value: 255, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the +# luminance component of the colors in the HTML output. Values below 100 +# gradually make the output lighter, whereas values above 100 make the output +# darker. The value divided by 100 is the actual gamma applied, so 80 represents +# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not +# change the gamma. +# Minimum value: 40, maximum value: 240, default value: 80. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting this +# to YES can help to show when doxygen was last run and thus if the +# documentation is up to date. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_TIMESTAMP = YES + +# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML +# documentation will contain a main index with vertical navigation menus that +# are dynamically created via JavaScript. If disabled, the navigation index will +# consists of multiple levels of tabs that are statically embedded in every HTML +# page. Disable this option to support browsers that do not have JavaScript, +# like the Qt help browser. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_MENUS = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_SECTIONS = YES + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries +# shown in the various tree structured indices initially; the user can expand +# and collapse entries dynamically later on. Doxygen will expand the tree to +# such a level that at most the specified number of entries are visible (unless +# a fully collapsed tree already exceeds this amount). So setting the number of +# entries 1 will produce a full collapsed tree by default. 0 is a special value +# representing an infinite number of entries and will result in a full expanded +# tree by default. +# Minimum value: 0, maximum value: 9999, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files will be +# generated that can be used as input for Apple's Xcode 3 integrated development +# environment (see: +# https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To +# create a documentation set, doxygen will generate a Makefile in the HTML +# output directory. Running make will produce the docset in that directory and +# running make install will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at +# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy +# genXcode/_index.html for more information. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_DOCSET = NO + +# This tag determines the name of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# The default value is: Doxygen generated docs. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# This tag determines the URL of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDURL = + +# This tag specifies a string that should uniquely identify the documentation +# set bundle. This should be a reverse domain-name style string, e.g. +# com.mycompany.MyDocSet. Doxygen will append .docset to the name. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. +# The default value is: org.doxygen.Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. +# The default value is: Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three +# additional HTML index files: index.hhp, index.hhc, and index.hhk. The +# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop +# on Windows. In the beginning of 2021 Microsoft took the original page, with +# a.o. the download links, offline the HTML help workshop was already many years +# in maintenance mode). You can download the HTML help workshop from the web +# archives at Installation executable (see: +# http://web.archive.org/web/20160201063255/http://download.microsoft.com/downlo +# ad/0/A/9/0A939EF6-E31C-430F-A3DF-DFAE7960D564/htmlhelp.exe). +# +# The HTML Help Workshop contains a compiler that can convert all HTML output +# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML +# files are now used as the Windows 98 help format, and will replace the old +# Windows help format (.hlp) on all Windows platforms in the future. Compressed +# HTML files also contain an index, a table of contents, and you can search for +# words in the documentation. The HTML workshop also contains a viewer for +# compressed HTML files. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_HTMLHELP = NO + +# The CHM_FILE tag can be used to specify the file name of the resulting .chm +# file. You can add a path in front of the file if the result should not be +# written to the html output directory. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_FILE = + +# The HHC_LOCATION tag can be used to specify the location (absolute path +# including file name) of the HTML help compiler (hhc.exe). If non-empty, +# doxygen will try to run the HTML help compiler on the generated index.hhp. +# The file has to be specified with full path. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +HHC_LOCATION = + +# The GENERATE_CHI flag controls if a separate .chi index file is generated +# (YES) or that it should be included in the main .chm file (NO). +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +GENERATE_CHI = NO + +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) +# and project file content. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_INDEX_ENCODING = + +# The BINARY_TOC flag controls whether a binary table of contents is generated +# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it +# enables the Previous and Next buttons. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members to +# the table of contents of the HTML help documentation and to the tree view. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that +# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help +# (.qch) of the generated HTML documentation. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify +# the file name of the resulting .qch file. The path specified is relative to +# the HTML output folder. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help +# Project output. For more information please see Qt Help Project / Namespace +# (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_NAMESPACE = org.doxygen.@DOXY_NAME@ + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt +# Help Project output. For more information please see Qt Help Project / Virtual +# Folders (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders). +# The default value is: doc. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_VIRTUAL_FOLDER = doc + +# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom +# filter to add. For more information please see Qt Help Project / Custom +# Filters (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see Qt Help Project / Custom +# Filters (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's filter section matches. Qt Help Project / Filter Attributes (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_SECT_FILTER_ATTRS = + +# The QHG_LOCATION tag can be used to specify the location (absolute path +# including file name) of Qt's qhelpgenerator. If non-empty doxygen will try to +# run qhelpgenerator on the generated .qhp file. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be +# generated, together with the HTML files, they form an Eclipse help plugin. To +# install this plugin and make it available under the help contents menu in +# Eclipse, the contents of the directory containing the HTML and XML files needs +# to be copied into the plugins directory of eclipse. The name of the directory +# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. +# After copying Eclipse needs to be restarted before the help appears. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the Eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have this +# name. Each documentation set should have its own identifier. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# If you want full control over the layout of the generated HTML pages it might +# be necessary to disable the index and replace it with your own. The +# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top +# of each HTML page. A value of NO enables the index and the value YES disables +# it. Since the tabs in the index contain the same information as the navigation +# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. If the tag +# value is set to YES, a side panel will be generated containing a tree-like +# index structure (just like the one that is generated for HTML Help). For this +# to work a browser that supports JavaScript, DHTML, CSS and frames is required +# (i.e. any modern browser). Windows users are probably better off using the +# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can +# further fine tune the look of the index (see "Fine-tuning the output"). As an +# example, the default style sheet generated by doxygen has an example that +# shows how to put an image at the root of the tree instead of the PROJECT_NAME. +# Since the tree basically has the same information as the tab index, you could +# consider setting DISABLE_INDEX to YES when enabling this option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_TREEVIEW = YES + +# When both GENERATE_TREEVIEW and DISABLE_INDEX are set to YES, then the +# FULL_SIDEBAR option determines if the side bar is limited to only the treeview +# area (value NO) or if it should extend to the full height of the window (value +# YES). Setting this to YES gives a layout similar to +# https://docs.readthedocs.io with more room for contents, but less room for the +# project logo, title, and description. If either GENERATE_TREEVIEW or +# DISABLE_INDEX is set to NO, this option has no effect. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FULL_SIDEBAR = NO + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that +# doxygen will group on one line in the generated HTML documentation. +# +# Note that a value of 0 will completely suppress the enum values from appearing +# in the overview section. +# Minimum value: 0, maximum value: 20, default value: 4. +# This tag requires that the tag GENERATE_HTML is set to YES. + +ENUM_VALUES_PER_LINE = 4 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used +# to set the initial width (in pixels) of the frame in which the tree is shown. +# Minimum value: 0, maximum value: 1500, default value: 250. +# This tag requires that the tag GENERATE_HTML is set to YES. + +TREEVIEW_WIDTH = 250 + +# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to +# external symbols imported via tag files in a separate window. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +EXT_LINKS_IN_WINDOW = NO + +# If the OBFUSCATE_EMAILS tag is set to YES, doxygen will obfuscate email +# addresses. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +OBFUSCATE_EMAILS = YES + +# If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg +# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see +# https://inkscape.org) to generate formulas as SVG images instead of PNGs for +# the HTML output. These images will generally look nicer at scaled resolutions. +# Possible values are: png (the default) and svg (looks nicer but requires the +# pdf2svg or inkscape tool). +# The default value is: png. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FORMULA_FORMAT = png + +# Use this tag to change the font size of LaTeX formulas included as images in +# the HTML documentation. When you change the font size after a successful +# doxygen run you need to manually remove any form_*.png images from the HTML +# output directory to force them to be regenerated. +# Minimum value: 8, maximum value: 50, default value: 10. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANSPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are not +# supported properly for IE 6.0, but are supported on all modern browsers. +# +# Note that when changing this option you need to delete any form_*.png files in +# the HTML output directory before the changes have effect. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_TRANSPARENT = YES + +# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands +# to create new LaTeX commands to be used in formulas as building blocks. See +# the section "Including formulas" for details. + +FORMULA_MACROFILE = + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see +# https://www.mathjax.org) which uses client side JavaScript for the rendering +# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX +# installed or if you want to formulas look prettier in the HTML output. When +# enabled you may also need to install MathJax separately and configure the path +# to it using the MATHJAX_RELPATH option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +USE_MATHJAX = NO + +# With MATHJAX_VERSION it is possible to specify the MathJax version to be used. +# Note that the different versions of MathJax have different requirements with +# regards to the different settings, so it is possible that also other MathJax +# settings have to be changed when switching between the different MathJax +# versions. +# Possible values are: MathJax_2 and MathJax_3. +# The default value is: MathJax_2. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_VERSION = MathJax_2 + +# When MathJax is enabled you can set the default output format to be used for +# the MathJax output. For more details about the output format see MathJax +# version 2 (see: +# http://docs.mathjax.org/en/v2.7-latest/output.html) and MathJax version 3 +# (see: +# http://docs.mathjax.org/en/latest/web/components/output.html). +# Possible values are: HTML-CSS (which is slower, but has the best +# compatibility. This is the name for Mathjax version 2, for MathJax version 3 +# this will be translated into chtml), NativeMML (i.e. MathML. Only supported +# for NathJax 2. For MathJax version 3 chtml will be used instead.), chtml (This +# is the name for Mathjax version 3, for MathJax version 2 this will be +# translated into HTML-CSS) and SVG. +# The default value is: HTML-CSS. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the HTML +# output directory using the MATHJAX_RELPATH option. The destination directory +# should contain the MathJax.js script. For instance, if the mathjax directory +# is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax +# Content Delivery Network so you can quickly see the result without installing +# MathJax. However, it is strongly recommended to install a local copy of +# MathJax from https://www.mathjax.org before deployment. The default value is: +# - in case of MathJax version 2: https://cdn.jsdelivr.net/npm/mathjax@2 +# - in case of MathJax version 3: https://cdn.jsdelivr.net/npm/mathjax@3 +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_RELPATH = https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.2/ + +# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax +# extension names that should be enabled during MathJax rendering. For example +# for MathJax version 2 (see https://docs.mathjax.org/en/v2.7-latest/tex.html +# #tex-and-latex-extensions): +# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# For example for MathJax version 3 (see +# http://docs.mathjax.org/en/latest/input/tex/extensions/index.html): +# MATHJAX_EXTENSIONS = ams +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_EXTENSIONS = + +# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces +# of code that will be used on startup of the MathJax code. See the MathJax site +# (see: +# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an +# example see the documentation. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_CODEFILE = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box for +# the HTML output. The underlying search engine uses javascript and DHTML and +# should work on any modern browser. Note that when using HTML help +# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) +# there is already a search function so this one should typically be disabled. +# For large projects the javascript based search engine can be slow, then +# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to +# search using the keyboard; to jump to the search box use <access key> + S +# (what the <access key> is depends on the OS and browser, but it is typically +# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down +# key> to jump into the search results window, the results can be navigated +# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel +# the search. The filter options can be selected when the cursor is inside the +# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys> +# to select a filter and <Enter> or <escape> to activate or cancel the filter +# option. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +SEARCHENGINE = YES + +# When the SERVER_BASED_SEARCH tag is enabled the search engine will be +# implemented using a web server instead of a web client using JavaScript. There +# are two flavors of web server based searching depending on the EXTERNAL_SEARCH +# setting. When disabled, doxygen will generate a PHP script for searching and +# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing +# and searching needs to be provided by external tools. See the section +# "External Indexing and Searching" for details. +# The default value is: NO. +# This tag requires that the tag SEARCHENGINE is set to YES. + +SERVER_BASED_SEARCH = NO + +# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP +# script for searching. Instead the search results are written to an XML file +# which needs to be processed by an external indexer. Doxygen will invoke an +# external search engine pointed to by the SEARCHENGINE_URL option to obtain the +# search results. +# +# Doxygen ships with an example indexer (doxyindexer) and search engine +# (doxysearch.cgi) which are based on the open source search engine library +# Xapian (see: +# https://xapian.org/). +# +# See the section "External Indexing and Searching" for details. +# The default value is: NO. +# This tag requires that the tag SEARCHENGINE is set to YES. + +EXTERNAL_SEARCH = NO + +# The SEARCHENGINE_URL should point to a search engine hosted by a web server +# which will return the search results when EXTERNAL_SEARCH is enabled. +# +# Doxygen ships with an example indexer (doxyindexer) and search engine +# (doxysearch.cgi) which are based on the open source search engine library +# Xapian (see: +# https://xapian.org/). See the section "External Indexing and Searching" for +# details. +# This tag requires that the tag SEARCHENGINE is set to YES. + +SEARCHENGINE_URL = + +# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed +# search data is written to a file for indexing by an external tool. With the +# SEARCHDATA_FILE tag the name of this file can be specified. +# The default file is: searchdata.xml. +# This tag requires that the tag SEARCHENGINE is set to YES. + +SEARCHDATA_FILE = searchdata.xml + +# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the +# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is +# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple +# projects and redirect the results back to the right project. +# This tag requires that the tag SEARCHENGINE is set to YES. + +EXTERNAL_SEARCH_ID = + +# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen +# projects other than the one defined by this configuration file, but that are +# all added to the same external search index. Each project needs to have a +# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of +# to a relative location where the documentation can be found. The format is: +# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ... +# This tag requires that the tag SEARCHENGINE is set to YES. + +EXTRA_SEARCH_MAPPINGS = + +#--------------------------------------------------------------------------- +# Configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output. +# The default value is: YES. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: latex. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. +# +# Note that when not enabling USE_PDFLATEX the default is latex when enabling +# USE_PDFLATEX the default is pdflatex and when in the later case latex is +# chosen this is overwritten by pdflatex. For specific output languages the +# default can have been set differently, this depends on the implementation of +# the output language. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate +# index for LaTeX. +# Note: This tag is used in the Makefile / make.bat. +# See also: LATEX_MAKEINDEX_CMD for the part in the generated output file +# (.tex). +# The default file is: makeindex. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +MAKEINDEX_CMD_NAME = makeindex + +# The LATEX_MAKEINDEX_CMD tag can be used to specify the command name to +# generate index for LaTeX. In case there is no backslash (\) as first character +# it will be automatically added in the LaTeX code. +# Note: This tag is used in the generated output file (.tex). +# See also: MAKEINDEX_CMD_NAME for the part in the Makefile / make.bat. +# The default value is: makeindex. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_MAKEINDEX_CMD = makeindex + +# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX +# documents. This may be useful for small projects and may help to save some +# trees in general. +# The default value is: NO. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used by the +# printer. +# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x +# 14 inches) and executive (7.25 x 10.5 inches). +# The default value is: a4. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +PAPER_TYPE = a4 + +# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names +# that should be included in the LaTeX output. The package can be specified just +# by its name or with the correct syntax as to be used with the LaTeX +# \usepackage command. To get the times font for instance you can specify : +# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times} +# To use the option intlimits with the amsmath package you can specify: +# EXTRA_PACKAGES=[intlimits]{amsmath} +# If left blank no extra packages will be included. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a user-defined LaTeX header for +# the generated LaTeX document. The header should contain everything until the +# first chapter. If it is left blank doxygen will generate a standard header. It +# is highly recommended to start with a default header using +# doxygen -w latex new_header.tex new_footer.tex new_stylesheet.sty +# and then modify the file new_header.tex. See also section "Doxygen usage" for +# information on how to generate the default header that doxygen normally uses. +# +# Note: Only use a user-defined header if you know what you are doing! +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. The following +# commands have a special meaning inside the header (and footer): For a +# description of the possible markers and block names see the documentation. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_HEADER = + +# The LATEX_FOOTER tag can be used to specify a user-defined LaTeX footer for +# the generated LaTeX document. The footer should contain everything after the +# last chapter. If it is left blank doxygen will generate a standard footer. See +# LATEX_HEADER for more information on how to generate a default footer and what +# special commands can be used inside the footer. See also section "Doxygen +# usage" for information on how to generate the default footer that doxygen +# normally uses. Note: Only use a user-defined footer if you know what you are +# doing! +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_FOOTER = + +# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# LaTeX style sheets that are included after the standard style sheets created +# by doxygen. Using this option one can overrule certain style aspects. Doxygen +# will copy the style sheet files to the output directory. +# Note: The order of the extra style sheet files is of importance (e.g. the last +# style sheet in the list overrules the setting of the previous ones in the +# list). +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_EXTRA_STYLESHEET = + +# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the LATEX_OUTPUT output +# directory. Note that the files will be copied as-is; there are no commands or +# markers available. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_EXTRA_FILES = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is +# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will +# contain links (just like the HTML output) instead of page references. This +# makes the output suitable for online browsing using a PDF viewer. +# The default value is: YES. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +PDF_HYPERLINKS = YES + +# If the USE_PDFLATEX tag is set to YES, doxygen will use the engine as +# specified with LATEX_CMD_NAME to generate the PDF file directly from the LaTeX +# files. Set this option to YES, to get a higher quality PDF documentation. +# +# See also section LATEX_CMD_NAME for selecting the engine. +# The default value is: YES. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +USE_PDFLATEX = YES + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode +# command to the generated LaTeX files. This will instruct LaTeX to keep running +# if errors occur, instead of asking the user for help. +# The default value is: NO. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_BATCHMODE = NO + +# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the +# index chapters (such as File Index, Compound Index, etc.) in the output. +# The default value is: NO. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_HIDE_INDICES = NO + +# The LATEX_BIB_STYLE tag can be used to specify the style to use for the +# bibliography, e.g. plainnat, or ieeetr. See +# https://en.wikipedia.org/wiki/BibTeX and \cite for more info. +# The default value is: plain. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_BIB_STYLE = plain + +# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated +# page will contain the date and time when the page was generated. Setting this +# to NO can help when comparing the output of multiple runs. +# The default value is: NO. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_TIMESTAMP = NO + +# The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute) +# path from which the emoji images will be read. If a relative path is entered, +# it will be relative to the LATEX_OUTPUT directory. If left blank the +# LATEX_OUTPUT directory will be used. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_EMOJI_DIRECTORY = + +#--------------------------------------------------------------------------- +# Configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The +# RTF output is optimized for Word 97 and may not look too pretty with other RTF +# readers/editors. +# The default value is: NO. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: rtf. +# This tag requires that the tag GENERATE_RTF is set to YES. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF +# documents. This may be useful for small projects and may help to save some +# trees in general. +# The default value is: NO. +# This tag requires that the tag GENERATE_RTF is set to YES. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will +# contain hyperlink fields. The RTF file will contain links (just like the HTML +# output) instead of page references. This makes the output suitable for online +# browsing using Word or some other Word compatible readers that support those +# fields. +# +# Note: WordPad (write) and others do not support links. +# The default value is: NO. +# This tag requires that the tag GENERATE_RTF is set to YES. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# configuration file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. +# +# See also section "Doxygen usage" for information on how to generate the +# default style sheet that doxygen normally uses. +# This tag requires that the tag GENERATE_RTF is set to YES. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an RTF document. Syntax is +# similar to doxygen's configuration file. A template extensions file can be +# generated using doxygen -e rtf extensionFile. +# This tag requires that the tag GENERATE_RTF is set to YES. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# Configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for +# classes and files. +# The default value is: NO. + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. A directory man3 will be created inside the directory specified by +# MAN_OUTPUT. +# The default directory is: man. +# This tag requires that the tag GENERATE_MAN is set to YES. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to the generated +# man pages. In case the manual section does not start with a number, the number +# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is +# optional. +# The default value is: .3. +# This tag requires that the tag GENERATE_MAN is set to YES. + +MAN_EXTENSION = .3 + +# The MAN_SUBDIR tag determines the name of the directory created within +# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by +# MAN_EXTENSION with the initial . removed. +# This tag requires that the tag GENERATE_MAN is set to YES. + +MAN_SUBDIR = + +# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it +# will generate one additional man file for each entity documented in the real +# man page(s). These additional files only source the real man page, but without +# them the man command would be unable to find the correct page. +# The default value is: NO. +# This tag requires that the tag GENERATE_MAN is set to YES. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# Configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that +# captures the structure of the code including all documentation. +# The default value is: NO. + +GENERATE_XML = YES + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: xml. +# This tag requires that the tag GENERATE_XML is set to YES. + +XML_OUTPUT = xml + +# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program +# listings (including syntax highlighting and cross-referencing information) to +# the XML output. Note that enabling this will significantly increase the size +# of the XML output. +# The default value is: YES. +# This tag requires that the tag GENERATE_XML is set to YES. + +XML_PROGRAMLISTING = YES + +# If the XML_NS_MEMB_FILE_SCOPE tag is set to YES, doxygen will include +# namespace members in file scope as well, matching the HTML output. +# The default value is: NO. +# This tag requires that the tag GENERATE_XML is set to YES. + +XML_NS_MEMB_FILE_SCOPE = NO + +#--------------------------------------------------------------------------- +# Configuration options related to the DOCBOOK output +#--------------------------------------------------------------------------- + +# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files +# that can be used to generate PDF. +# The default value is: NO. + +GENERATE_DOCBOOK = NO + +# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in +# front of it. +# The default directory is: docbook. +# This tag requires that the tag GENERATE_DOCBOOK is set to YES. + +DOCBOOK_OUTPUT = docbook + +#--------------------------------------------------------------------------- +# Configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an +# AutoGen Definitions (see http://autogen.sourceforge.net/) file that captures +# the structure of the code including all documentation. Note that this feature +# is still experimental and incomplete at the moment. +# The default value is: NO. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# Configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module +# file that captures the structure of the code including all documentation. +# +# Note that this feature is still experimental and incomplete at the moment. +# The default value is: NO. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary +# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI +# output from the Perl module output. +# The default value is: NO. +# This tag requires that the tag GENERATE_PERLMOD is set to YES. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely +# formatted so it can be parsed by a human reader. This is useful if you want to +# understand what is going on. On the other hand, if this tag is set to NO, the +# size of the Perl module output will be much smaller and Perl will parse it +# just the same. +# The default value is: YES. +# This tag requires that the tag GENERATE_PERLMOD is set to YES. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file are +# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful +# so different doxyrules.make files included by the same Makefile don't +# overwrite each other's variables. +# This tag requires that the tag GENERATE_PERLMOD is set to YES. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all +# C-preprocessor directives found in the sources and include files. +# The default value is: YES. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names +# in the source code. If set to NO, only conditional compilation will be +# performed. Macro expansion can be done in a controlled way by setting +# EXPAND_ONLY_PREDEF to YES. +# The default value is: NO. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +MACRO_EXPANSION = YES + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then +# the macro expansion is limited to the macros specified with the PREDEFINED and +# EXPAND_AS_DEFINED tags. +# The default value is: NO. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES, the include files in the +# INCLUDE_PATH will be searched if a #include is found. +# The default value is: YES. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by the +# preprocessor. +# This tag requires that the tag SEARCH_INCLUDES is set to YES. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will be +# used. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that are +# defined before the preprocessor is started (similar to the -D option of e.g. +# gcc). The argument of the tag is a list of macros of the form: name or +# name=definition (no spaces). If the definition and the "=" are omitted, "=1" +# is assumed. To prevent a macro definition from being undefined via #undef or +# recursively expanded use the := operator instead of the = operator. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +PREDEFINED = DOXYGEN_DOCUMENTATION_BUILD + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this +# tag can be used to specify a list of macro names that should be expanded. The +# macro definition that is found in the sources will be used. Use the PREDEFINED +# tag if you want to use a different macro definition that overrules the +# definition found in the source code. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will +# remove all references to function-like macros that are alone on a line, have +# an all uppercase name, and do not end with a semicolon. Such function macros +# are typically used for boiler-plate code, and will confuse the parser if not +# removed. +# The default value is: YES. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +SKIP_FUNCTION_MACROS = NO + +#--------------------------------------------------------------------------- +# Configuration options related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES tag can be used to specify one or more tag files. For each tag +# file the location of the external documentation should be added. The format of +# a tag file without this location is as follows: +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where loc1 and loc2 can be relative or absolute paths or URLs. See the +# section "Linking to external documentation" for more information about the use +# of tag files. +# Note: Each tag file must have a unique name (where the name does NOT include +# the path). If a tag file is not located in the directory in which doxygen is +# run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create a +# tag file that is based on the input files it reads. See section "Linking to +# external documentation" for more information about the usage of tag files. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES, all external class will be listed in +# the class index. If set to NO, only the inherited external classes will be +# listed. +# The default value is: NO. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will be +# listed. +# The default value is: YES. + +EXTERNAL_GROUPS = YES + +# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in +# the related pages index. If set to NO, only the current project's pages will +# be listed. +# The default value is: YES. + +EXTERNAL_PAGES = YES + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# You can include diagrams made with dia in doxygen documentation. Doxygen will +# then run dia to produce the diagram and insert it in the documentation. The +# DIA_PATH tag allows you to specify the directory where the dia binary resides. +# If left empty dia is assumed to be found in the default search path. + +DIA_PATH = + +# If set to YES the inheritance and collaboration graphs will hide inheritance +# and usage relations if the target is undocumented or is not a class. +# The default value is: YES. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz (see: +# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent +# Bell Labs. The other options in this section have no effect if this option is +# set to NO +# The default value is: NO. + +HAVE_DOT = YES + +# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed +# to run in parallel. When set to 0 doxygen will base this on the number of +# processors available in the system. You can set it explicitly to a value +# larger than 0 to get control over the balance between CPU load and processing +# speed. +# Minimum value: 0, maximum value: 32, default value: 0. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_NUM_THREADS = 0 + +# When you want a differently looking font in the dot files that doxygen +# generates you can specify the font name using DOT_FONTNAME. You need to make +# sure dot is able to find the font, which can be done by putting it in a +# standard location or by setting the DOTFONTPATH environment variable or by +# setting DOT_FONTPATH to the directory containing the font. +# The default value is: Helvetica. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_FONTNAME = Helvetica + +# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of +# dot graphs. +# Minimum value: 4, maximum value: 24, default value: 10. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_FONTSIZE = 10 + +# By default doxygen will tell dot to use the default font as specified with +# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set +# the path where dot can find it using this tag. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_FONTPATH = + +# If the CLASS_GRAPH tag is set to YES (or GRAPH) then doxygen will generate a +# graph for each documented class showing the direct and indirect inheritance +# relations. In case HAVE_DOT is set as well dot will be used to draw the graph, +# otherwise the built-in generator will be used. If the CLASS_GRAPH tag is set +# to TEXT the direct and indirect inheritance relations will be shown as texts / +# links. +# Possible values are: NO, YES, TEXT and GRAPH. +# The default value is: YES. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a +# graph for each documented class showing the direct and indirect implementation +# dependencies (inheritance, containment, and class references variables) of the +# class with other documented classes. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for +# groups, showing the direct groups dependencies. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +UML_LOOK = NO + +# If the UML_LOOK tag is enabled, the fields and methods are shown inside the +# class node. If there are many fields or methods and many nodes the graph may +# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the +# number of items for each type to make the size more manageable. Set this to 0 +# for no limit. Note that the threshold may be exceeded by 50% before the limit +# is enforced. So when you set the threshold to 10, up to 15 fields may appear, +# but if the number exceeds 15, the total amount of fields shown is limited to +# 10. +# Minimum value: 0, maximum value: 100, default value: 10. +# This tag requires that the tag UML_LOOK is set to YES. + +UML_LIMIT_NUM_FIELDS = 10 + +# If the DOT_UML_DETAILS tag is set to NO, doxygen will show attributes and +# methods without types and arguments in the UML graphs. If the DOT_UML_DETAILS +# tag is set to YES, doxygen will add type and arguments for attributes and +# methods in the UML graphs. If the DOT_UML_DETAILS tag is set to NONE, doxygen +# will not generate fields with class member information in the UML graphs. The +# class diagrams will look similar to the default class diagrams but using UML +# notation for the relationships. +# Possible values are: NO, YES and NONE. +# The default value is: NO. +# This tag requires that the tag UML_LOOK is set to YES. + +DOT_UML_DETAILS = NO + +# The DOT_WRAP_THRESHOLD tag can be used to set the maximum number of characters +# to display on a single line. If the actual line length exceeds this threshold +# significantly it will wrapped across multiple lines. Some heuristics are apply +# to avoid ugly line breaks. +# Minimum value: 0, maximum value: 1000, default value: 17. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_WRAP_THRESHOLD = 17 + +# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and +# collaboration graphs will show the relations between templates and their +# instances. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +TEMPLATE_RELATIONS = YES + +# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to +# YES then doxygen will generate a graph for each documented file showing the +# direct and indirect include dependencies of the file with other documented +# files. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +INCLUDE_GRAPH = YES + +# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are +# set to YES then doxygen will generate a graph for each documented file showing +# the direct and indirect include dependencies of the file with other documented +# files. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH tag is set to YES then doxygen will generate a call +# dependency graph for every global function or class method. +# +# Note that enabling this option will significantly increase the time of a run. +# So in most cases it will be better to enable call graphs for selected +# functions only using the \callgraph command. Disabling a call graph can be +# accomplished by means of the command \hidecallgraph. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +CALL_GRAPH = NO + +# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller +# dependency graph for every global function or class method. +# +# Note that enabling this option will significantly increase the time of a run. +# So in most cases it will be better to enable caller graphs for selected +# functions only using the \callergraph command. Disabling a caller graph can be +# accomplished by means of the command \hidecallergraph. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +CALLER_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical +# hierarchy of all classes instead of a textual one. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the +# dependencies a directory has on other directories in a graphical way. The +# dependency relations are determined by the #include relations between the +# files in the directories. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +DIRECTORY_GRAPH = YES + +# The DIR_GRAPH_MAX_DEPTH tag can be used to limit the maximum number of levels +# of child directories generated in directory dependency graphs by dot. +# Minimum value: 1, maximum value: 25, default value: 1. +# This tag requires that the tag DIRECTORY_GRAPH is set to YES. + +DIR_GRAPH_MAX_DEPTH = 1 + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. For an explanation of the image formats see the section +# output formats in the documentation of the dot tool (Graphviz (see: +# http://www.graphviz.org/)). +# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order +# to make the SVG files visible in IE 9+ (other browsers do not have this +# requirement). +# Possible values are: png, jpg, gif, svg, png:gd, png:gd:gd, png:cairo, +# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and +# png:gdiplus:gdiplus. +# The default value is: png. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_IMAGE_FORMAT = svg + +# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to +# enable generation of interactive SVG images that allow zooming and panning. +# +# Note that this requires a modern browser other than Internet Explorer. Tested +# and working are Firefox, Chrome, Safari, and Opera. +# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make +# the SVG files visible. Older versions of IE do not have SVG support. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +INTERACTIVE_SVG = NO + +# The DOT_PATH tag can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the \dotfile +# command). +# This tag requires that the tag HAVE_DOT is set to YES. + +DOTFILE_DIRS = + +# The MSCFILE_DIRS tag can be used to specify one or more directories that +# contain msc files that are included in the documentation (see the \mscfile +# command). + +MSCFILE_DIRS = + +# The DIAFILE_DIRS tag can be used to specify one or more directories that +# contain dia files that are included in the documentation (see the \diafile +# command). + +DIAFILE_DIRS = + +# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the +# path where java can find the plantuml.jar file or to the filename of jar file +# to be used. If left blank, it is assumed PlantUML is not used or called during +# a preprocessing step. Doxygen will generate a warning when it encounters a +# \startuml command in this case and will not generate output for the diagram. + +PLANTUML_JAR_PATH = + +# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a +# configuration file for plantuml. + +PLANTUML_CFG_FILE = + +# When using plantuml, the specified paths are searched for files specified by +# the !include statement in a plantuml block. + +PLANTUML_INCLUDE_PATH = + +# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes +# that will be shown in the graph. If the number of nodes in a graph becomes +# larger than this value, doxygen will truncate the graph, which is visualized +# by representing a node as a red box. Note that doxygen if the number of direct +# children of the root node in a graph is already larger than +# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that +# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. +# Minimum value: 0, maximum value: 10000, default value: 50. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_GRAPH_MAX_NODES = 50 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs +# generated by dot. A depth value of 3 means that only nodes reachable from the +# root by following a path via at most 3 edges will be shown. Nodes that lay +# further from the root node will be omitted. Note that setting this option to 1 +# or 2 may greatly reduce the computation time needed for large code bases. Also +# note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. +# Minimum value: 0, maximum value: 1000, default value: 0. +# This tag requires that the tag HAVE_DOT is set to YES. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, because dot on Windows does not seem +# to support this out of the box. +# +# Warning: Depending on the platform used, enabling this option may lead to +# badly anti-aliased labels on the edges of a graph (i.e. they become hard to +# read). +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_TRANSPARENT = YES + +# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) support +# this, this feature is disabled by default. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_MULTI_TARGETS = NO + +# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page +# explaining the meaning of the various boxes and arrows in the dot generated +# graphs. +# Note: This tag requires that UML_LOOK isn't set, i.e. the doxygen internal +# graphical representation for inheritance and collaboration diagrams is used. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate +# files that are used to generate the various graphs. +# +# Note: This setting is not only used for dot files but also for msc temporary +# files. +# The default value is: YES. + +DOT_CLEANUP = YES diff --git a/doxygen/doxygen-awesome-css b/doxygen/doxygen-awesome-css new file mode 160000 index 0000000..b6a3373 --- /dev/null +++ b/doxygen/doxygen-awesome-css @@ -0,0 +1 @@ +Subproject commit b6a337311ec3bbc5603fa2ce541850a603844e58 diff --git a/doxygen/footer.html b/doxygen/footer.html new file mode 100644 index 0000000..2924e1f --- /dev/null +++ b/doxygen/footer.html @@ -0,0 +1,32 @@ +<!-- HTML footer for doxygen 1.9.1--> +<!-- start footer part --> +<!--BEGIN GENERATE_TREEVIEW--> +<div id="nav-path" class="navpath"> + <!-- id is needed for treeview function! --> + <ul> + $navpath + <li class="footer">$generatedby <a href="https://www.doxygen.org/index.html"><img class="footer" + src="$relpath^doxygen.svg" width="104" height="31" alt="doxygen" /></a> $doxygenversion </li> + </ul> +</div> +<!--END GENERATE_TREEVIEW--> +<!--BEGIN !GENERATE_TREEVIEW--> +<hr class="footer" /> +<address class="footer"><small> + $generatedby <a href="https://www.doxygen.org/index.html"><img class="footer" src="$relpath^doxygen.svg" + width="104" height="31" alt="doxygen" /></a> $doxygenversion + </small></address> +<!--END !GENERATE_TREEVIEW--> +<script type="text/javascript"> + // script for doxygen 1.9.1 + $(function () { + $(document).ready(function () { + toggleButton = document.createElement('doxygen-awesome-dark-mode-toggle') + toggleButton.title = "Toggle Light/Dark Mode" + document.getElementById("MSearchBox").parentNode.appendChild(toggleButton) + }) + }) +</script> +</body> + +</html> \ No newline at end of file diff --git a/doxygen/header.html b/doxygen/header.html new file mode 100644 index 0000000..12dbfcb --- /dev/null +++ b/doxygen/header.html @@ -0,0 +1,130 @@ +<!-- HTML header for doxygen 1.9.1--> +<!DOCTYPE html + PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> + +<head> + <meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8" /> + <meta http-equiv="X-UA-Compatible" content="IE=9" /> + <meta name="generator" content="Doxygen $doxygenversion" /> + <meta name="viewport" content="width=device-width, initial-scale=1" /> + <!--BEGIN PROJECT_NAME--> + <title>$projectname: $title + + + $title + + + + + + $treeview + $search + $mathjax + + $extrastylesheet + + + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + +
+
$projectname +  $projectnumber + +
+ +
$projectbrief
+ +
+
$projectbrief
+
$searchbox
+
+ + \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..8ade9bb --- /dev/null +++ b/requirements.txt @@ -0,0 +1,14 @@ +autopep8 +bandit +breathe +cmakelang +docutils +esbonio +myst-parser +pygments +rstcheck +sphinx +sphinx-autobuild +sphinx-book-theme +sphinx-copybutton +sphinx-prompt diff --git a/templates/template_api.h.in b/templates/template_api.h.in new file mode 100644 index 0000000..9db3061 --- /dev/null +++ b/templates/template_api.h.in @@ -0,0 +1,29 @@ +//===----------------------------------------------------------------------===// +// Distributed under the 3-Clause BSD License. See accompanying file LICENSE or +// copy at https://opensource.org/licenses/BSD-3-Clause). +// SPDX-License-Identifier: BSD-3-Clause +//===----------------------------------------------------------------------===// + +#pragma once + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) + +#include <${TEMPLATE_INCLUDE_DIR}/${TEMPLATE_TARGET}_export.h> + +// clang-format off +#ifdef ${target_id}_STATIC_DEFINE +# define ${target_id}_TEMPLATE_API +#else +# ifndef ${TEMPLATE_TARGET_ID}_TEMPLATE_API +# ifdef ${TEMPLATE_TARGET_ID}_EXPORTS + /* We are building this library */ +# define ${TEMPLATE_TARGET_ID}_TEMPLATE_API __attribute__((visibility("default"))) +# else + /* We are using this library */ +# define ${TEMPLATE_TARGET_ID}_TEMPLATE_API __attribute__((visibility("default"))) +# endif +# endif +#endif +// clang-format on + +// NOLINTEND(cppcoreguidelines-macro-usage) diff --git a/templates/template_msvc_api.h.in b/templates/template_msvc_api.h.in new file mode 100644 index 0000000..5e50a5b --- /dev/null +++ b/templates/template_msvc_api.h.in @@ -0,0 +1,29 @@ +//===----------------------------------------------------------------------===// +// Distributed under the 3-Clause BSD License. See accompanying file LICENSE or +// copy at https://opensource.org/licenses/BSD-3-Clause). +// SPDX-License-Identifier: BSD-3-Clause +//===----------------------------------------------------------------------===// + +#pragma once + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) + +#include <${TEMPLATE_INCLUDE_DIR}/${TEMPLATE_TARGET}_export.h> + +// clang-format off +#ifdef ${TEMPLATE_TARGET_ID}_STATIC_DEFINE +# define ${target_id}_TEMPLATE_API +#else +# ifndef ${TEMPLATE_TARGET_ID}_TEMPLATE_API +# ifdef ${TEMPLATE_TARGET_ID}_EXPORTS + /* We are building this library */ +# define ${TEMPLATE_TARGET_ID}_TEMPLATE_API +# else + /* We are using this library */ +# define ${TEMPLATE_TARGET_ID}_TEMPLATE_API +# endif +# endif +#endif +// clang-format on + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) diff --git a/templates/version.h.in b/templates/version.h.in new file mode 100644 index 0000000..bf2e8a3 --- /dev/null +++ b/templates/version.h.in @@ -0,0 +1,22 @@ +//===----------------------------------------------------------------------===// +// Distributed under the 3-Clause BSD License. See accompanying file LICENSE or +// copy at https://opensource.org/licenses/BSD-3-Clause). +// SPDX-License-Identifier: BSD-3-Clause +//===----------------------------------------------------------------------===// + +#pragma once + +#define ASAP_PROJECT_NAME "@META_PROJECT_NAME@" +#define ASAP_PROJECT_DESCRIPTION "@META_PROJECT_DESCRIPTION@" + +#define ASAP_AUTHOR_ORGANIZATION "@META_AUTHOR_ORGANIZATION@" +#define ASAP_AUTHOR_DOMAIN "@META_AUTHOR_DOMAIN@" +#define ASAP_AUTHOR_MAINTAINER "@META_AUTHOR_MAINTAINER@" + +#define ASAP_VERSION_MAJOR "@META_VERSION_MAJOR@" +#define ASAP_VERSION_MINOR "@META_VERSION_MINOR@" +#define ASAP_VERSION_PATCH "@META_VERSION_PATCH@" +#define ASAP_VERSION_REVISION "@META_VERSION_REVISION@" + +#define ASAP_VERSION "@META_VERSION@" +#define ASAP_NAME_VERSION "@META_NAME_VERSION@" diff --git a/third_party/CMakeLists.txt b/third_party/CMakeLists.txt new file mode 100644 index 0000000..b938168 --- /dev/null +++ b/third_party/CMakeLists.txt @@ -0,0 +1,12 @@ +# ===-----------------------------------------------------------------------===# +# Distributed under the 3-Clause BSD License. See accompanying file LICENSE or +# copy at https://opensource.org/licenses/BSD-3-Clause). +# SPDX-License-Identifier: BSD-3-Clause +# ===-----------------------------------------------------------------------===# + +message("=> [third-party modules]") + +# ------------------------------------------------------------------------------ +# gsl +# ------------------------------------------------------------------------------ +add_subdirectory(gsl) diff --git a/third_party/gsl b/third_party/gsl new file mode 160000 index 0000000..0f6dbc9 --- /dev/null +++ b/third_party/gsl @@ -0,0 +1 @@ +Subproject commit 0f6dbc9e2915ef5c16830f3fa3565738de2a9230