From 4dbb5600b03087f5532140781ec690b4145fac18 Mon Sep 17 00:00:00 2001 From: Zhanwei Wang Date: Mon, 22 Apr 2024 18:22:18 +0800 Subject: [PATCH] WIP --- .clang-format | 212 ++ .clang-tidy | 82 + .clang-tidy-ignore | 12 + .github/workflows/check.yml | 104 + .github/workflows/image.yml | 40 + .gitignore | 38 + CMakeLists.txt | 42 + Dockerfile | 59 + README.md | 9 + check_header.py | 61 + clang-tidy-wrap | 72 + cmake/build.cmake | 189 ++ cmake/coverage.cmake | 801 +++++ cmake/demoConfig.cmake.in | 4 + cmake/docs.cmake | 37 + cmake/install.cmake | 65 + cmake/options.cmake | 16 + cmake/package.cmake | 33 + cmake/sanitizers/.gitignore | 3 + cmake/sanitizers/CMakeLists.txt | 51 + cmake/sanitizers/LICENSE | 22 + cmake/sanitizers/README.md | 73 + cmake/sanitizers/cmake/FindASan.cmake | 59 + cmake/sanitizers/cmake/FindMSan.cmake | 57 + cmake/sanitizers/cmake/FindSanitizers.cmake | 94 + cmake/sanitizers/cmake/FindTSan.cmake | 65 + cmake/sanitizers/cmake/FindUBSan.cmake | 46 + cmake/sanitizers/cmake/asan-wrapper | 55 + cmake/sanitizers/cmake/sanitize-helpers.cmake | 178 ++ cmake/sanitizers/tests/CMakeLists.txt | 67 + cmake/sanitizers/tests/asan_test.cpp | 40 + cmake/sanitizers/tests/shortest.ext.test.cpp | 40 + docker-compose.yml | 14 + docs/05-examples.md | 3 + docs/06-design.md | 7 + docs/07-reference.md | 11 + docs/theme/DoxygenLayout.xml | 233 ++ docs/theme/doxygen-awesome-darkmode-toggle.js | 157 + .../doxygen-awesome-fragment-copy-button.js | 85 + docs/theme/doxygen-awesome-interactive-toc.js | 81 + docs/theme/doxygen-awesome-paragraph-link.js | 51 + ...n-awesome-sidebar-only-darkmode-toggle.css | 40 + docs/theme/doxygen-awesome-sidebar-only.css | 116 + docs/theme/doxygen-awesome-tabs.js | 90 + docs/theme/doxygen-awesome.css | 2669 +++++++++++++++++ docs/theme/header.html | 104 + src/CMakeLists.txt | 11 + src/demo/CMakeLists.txt | 5 + src/demo/cmd/CMakeLists.txt | 18 + src/demo/cmd/cmd.cpp | 8 + src/demo/cmd/cmd_test.cpp | 4 + src/demo/common/CMakeLists.txt | 23 + src/demo/common/common_utils.cpp | 11 + src/demo/common/common_utils.h | 16 + src/demo/common/common_utils_test.cpp | 18 + src/demo/common/version.h.in | 13 + src/demo/liba/CMakeLists.txt | 21 + src/demo/liba/demo.cpp | 11 + src/demo/liba/demo.h | 15 + src/demo/liba/demo_test.cpp | 4 + src/examples/CMakeLists.txt | 5 + src/examples/grpc/CMakeLists.txt | 31 + src/examples/grpc/greeter_service.cpp | 5 + src/examples/grpc/greeter_service.h | 16 + src/examples/grpc/grpc_client.cpp | 10 + src/examples/grpc/grpc_server.cpp | 13 + src/examples/grpc/proto/helloworld.proto | 42 + src/tests/CMakeLists.txt | 7 + src/tests/benchmark/CMakeLists.txt | 10 + src/tests/benchmark/demo_benchmark.cpp | 18 + src/tests/function/CMakeLists.txt | 8 + src/tests/function/function_test.cpp | 4 + thirdparty/.gitignore | 1 + thirdparty/CMakeLists.txt | 80 + 74 files changed, 6815 insertions(+) create mode 100644 .clang-format create mode 100644 .clang-tidy create mode 100644 .clang-tidy-ignore create mode 100644 .github/workflows/check.yml create mode 100644 .github/workflows/image.yml create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 Dockerfile create mode 100644 README.md create mode 100755 check_header.py create mode 100755 clang-tidy-wrap create mode 100644 cmake/build.cmake create mode 100644 cmake/coverage.cmake create mode 100644 cmake/demoConfig.cmake.in create mode 100644 cmake/docs.cmake create mode 100644 cmake/install.cmake create mode 100644 cmake/options.cmake create mode 100644 cmake/package.cmake create mode 100644 cmake/sanitizers/.gitignore create mode 100644 cmake/sanitizers/CMakeLists.txt create mode 100644 cmake/sanitizers/LICENSE create mode 100644 cmake/sanitizers/README.md create mode 100644 cmake/sanitizers/cmake/FindASan.cmake create mode 100644 cmake/sanitizers/cmake/FindMSan.cmake create mode 100755 cmake/sanitizers/cmake/FindSanitizers.cmake create mode 100644 cmake/sanitizers/cmake/FindTSan.cmake create mode 100644 cmake/sanitizers/cmake/FindUBSan.cmake create mode 100755 cmake/sanitizers/cmake/asan-wrapper create mode 100755 cmake/sanitizers/cmake/sanitize-helpers.cmake create mode 100644 cmake/sanitizers/tests/CMakeLists.txt create mode 100644 cmake/sanitizers/tests/asan_test.cpp create mode 100644 cmake/sanitizers/tests/shortest.ext.test.cpp create mode 100644 docker-compose.yml create mode 100644 docs/05-examples.md create mode 100644 docs/06-design.md create mode 100644 docs/07-reference.md create mode 100644 docs/theme/DoxygenLayout.xml create mode 100644 docs/theme/doxygen-awesome-darkmode-toggle.js create mode 100644 docs/theme/doxygen-awesome-fragment-copy-button.js create mode 100644 docs/theme/doxygen-awesome-interactive-toc.js create mode 100644 docs/theme/doxygen-awesome-paragraph-link.js create mode 100644 docs/theme/doxygen-awesome-sidebar-only-darkmode-toggle.css create mode 100644 docs/theme/doxygen-awesome-sidebar-only.css create mode 100644 docs/theme/doxygen-awesome-tabs.js create mode 100644 docs/theme/doxygen-awesome.css create mode 100644 docs/theme/header.html create mode 100644 src/CMakeLists.txt create mode 100644 src/demo/CMakeLists.txt create mode 100644 src/demo/cmd/CMakeLists.txt create mode 100644 src/demo/cmd/cmd.cpp create mode 100644 src/demo/cmd/cmd_test.cpp create mode 100644 src/demo/common/CMakeLists.txt create mode 100644 src/demo/common/common_utils.cpp create mode 100644 src/demo/common/common_utils.h create mode 100644 src/demo/common/common_utils_test.cpp create mode 100644 src/demo/common/version.h.in create mode 100644 src/demo/liba/CMakeLists.txt create mode 100644 src/demo/liba/demo.cpp create mode 100644 src/demo/liba/demo.h create mode 100644 src/demo/liba/demo_test.cpp create mode 100644 src/examples/CMakeLists.txt create mode 100644 src/examples/grpc/CMakeLists.txt create mode 100644 src/examples/grpc/greeter_service.cpp create mode 100644 src/examples/grpc/greeter_service.h create mode 100644 src/examples/grpc/grpc_client.cpp create mode 100644 src/examples/grpc/grpc_server.cpp create mode 100644 src/examples/grpc/proto/helloworld.proto create mode 100644 src/tests/CMakeLists.txt create mode 100644 src/tests/benchmark/CMakeLists.txt create mode 100644 src/tests/benchmark/demo_benchmark.cpp create mode 100644 src/tests/function/CMakeLists.txt create mode 100644 src/tests/function/function_test.cpp create mode 100644 thirdparty/.gitignore create mode 100644 thirdparty/CMakeLists.txt diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..b9804ed --- /dev/null +++ b/.clang-format @@ -0,0 +1,212 @@ +--- +Language: Cpp +# BasedOnStyle: Google +AccessModifierOffset: -1 +AlignAfterOpenBracket: Align +AlignArrayOfStructures: None +AlignConsecutiveMacros: None +AlignConsecutiveAssignments: None +AlignConsecutiveBitFields: None +AlignConsecutiveDeclarations: None +AlignEscapedNewlines: Left +AlignOperands: Align +AlignTrailingComments: true +AllowAllArgumentsOnNextLine: true +AllowAllConstructorInitializersOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortEnumsOnASingleLine: true +AllowShortBlocksOnASingleLine: Never +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: Empty +AllowShortLambdasOnASingleLine: All +AllowShortIfStatementsOnASingleLine: WithoutElse +AllowShortLoopsOnASingleLine: true +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: true +AlwaysBreakTemplateDeclarations: Yes +AttributeMacros: + - __capability +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: Never + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + BeforeLambdaBody: false + BeforeWhile: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakBeforeBinaryOperators: None +BreakBeforeConceptDeclarations: true +BreakBeforeBraces: Attach +BreakBeforeInheritanceComma: false +BreakInheritanceList: BeforeColon +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: BeforeColon +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: true +ColumnLimit: 80 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: true +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DeriveLineEnding: true +DerivePointerAlignment: true +DisableFormat: false +EmptyLineAfterAccessModifier: Never +EmptyLineBeforeAccessModifier: LogicalBlock +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IfMacros: + - KJ_IF_MAYBE +IncludeBlocks: Regroup +IncludeCategories: + - Regex: '^' + Priority: 2 + SortPriority: 0 + CaseSensitive: false + - Regex: '^<.*\.h>' + Priority: 1 + SortPriority: 0 + CaseSensitive: false + - Regex: '^<.*' + Priority: 2 + SortPriority: 0 + CaseSensitive: false + - Regex: '.*' + Priority: 3 + SortPriority: 0 + CaseSensitive: false +IncludeIsMainRegex: '([-_](test|unittest))?$' +IncludeIsMainSourceRegex: '' +IndentAccessModifiers: false +IndentCaseLabels: true +IndentCaseBlocks: false +IndentGotoLabels: true +IndentPPDirectives: None +IndentExternBlock: AfterExternBlock +IndentRequires: false +IndentWidth: 2 +IndentWrappedFunctionNames: false +InsertTrailingCommas: None +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: false +LambdaBodyIndentation: Signature +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBinPackProtocolList: Never +ObjCBlockIndentWidth: 2 +ObjCBreakBeforeNestedBlockParam: true +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 1 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 200 +PenaltyIndentedWhitespace: 0 +PointerAlignment: Left +PPIndentWidth: -1 +RawStringFormats: + - Language: Cpp + Delimiters: + - cc + - CC + - cpp + - Cpp + - CPP + - 'c++' + - 'C++' + CanonicalDelimiter: '' + BasedOnStyle: google + - Language: TextProto + Delimiters: + - pb + - PB + - proto + - PROTO + EnclosingFunctions: + - EqualsProto + - EquivToProto + - PARSE_PARTIAL_TEXT_PROTO + - PARSE_TEST_PROTO + - PARSE_TEXT_PROTO + - ParseTextOrDie + - ParseTextProtoOrDie + - ParseTestProto + - ParsePartialTestProto + CanonicalDelimiter: pb + BasedOnStyle: google +ReferenceAlignment: Pointer +ReflowComments: true +ShortNamespaceLines: 1 +SortIncludes: CaseSensitive +SortJavaStaticImport: Before +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeCaseColon: false +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceAroundPointerQualifiers: Default +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyBlock: false +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 2 +SpacesInAngles: Never +SpacesInConditionalStatement: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInLineCommentPrefix: + Minimum: 1 + Maximum: -1 +SpacesInParentheses: false +SpacesInSquareBrackets: false +SpaceBeforeSquareBrackets: false +BitFieldColonSpacing: Both +Standard: Auto +StatementAttributeLikeMacros: + - Q_EMIT +StatementMacros: + - Q_UNUSED + - QT_REQUIRE_VERSION +TabWidth: 8 +UseCRLF: false +UseTab: Never +WhitespaceSensitiveMacros: + - STRINGIZE + - PP_STRINGIZE + - BOOST_PP_STRINGIZE + - NS_SWIFT_NAME + - CF_SWIFT_NAME +... + diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 0000000..2f8a05e --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,82 @@ +--- +Checks: ' + bugprone-*, + -bugprone-branch-clone, + -bugprone-easily-swappable-parameters, + cert-*, + clang-analyzer-*, + clang-diagnostic-*, + cppcoreguidelines-*, + -cppcoreguidelines-avoid-*, + -cppcoreguidelines-pro-type-const-cast, + -cppcoreguidelines-special-member-functions, + hicpp-*, + -hicpp-avoid-c-arrays, + -hicpp-special-member-functions, + misc-*, + -misc-const-correctness, + -misc-no-recursion, + -misc-include-cleaner, + modernize-*, + -modernize-avoid-c-arrays, + -modernize-use-nodiscard, + -modernize-use-trailing-return-type, + performance-*, + -performance-avoid-endl, + readability-*, + -readability-convert-member-functions-to-static, + -readability-identifier-length, + -readability-isolate-declaration, + -readability-magic-numbers, + -readability-redundant-access-specifiers, + ' +WarningsAsErrors: ' + bugprone-*, + cert-*, + misc-*, + performance-*, + ' +HeaderFilterRegex: '^((?!(\.pb\.h|boost)).)*$' +AnalyzeTemporaryDtors: false +FormatStyle: file +User: root +CheckOptions: + - key: modernize-loop-convert.MinConfidence + value: reasonable + - key: modernize-loop-convert.MaxCopySize + value: '16' + - key: modernize-loop-convert.NamingStyle + value: CamelCase + - key: modernize-replace-auto-ptr.IncludeStyle + value: llvm + - key: modernize-pass-by-value.IncludeStyle + value: llvm + - key: modernize-use-nullptr.NullMacros + value: 'NULL' + - key: cert-str34-c.DiagnoseSignedUnsignedCharComparisons + value: 'false' + - key: cert-oop54-cpp.WarnOnlyIfThisHasSuspiciousField + value: 'false' + - key: cert-dcl16-c.NewSuffixes + value: 'L;LL;LU;LLU' + - key: google-readability-namespace-comments.ShortNamespaceLines + value: '10' + - key: google-readability-braces-around-statements.ShortStatementLines + value: '1' + - key: google-readability-namespace-comments.SpacesBeforeComments + value: '2' + - key: google-readability-function-size.StatementThreshold + value: '800' + - key: misc-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic + value: 'true' + - key: cppcoreguidelines-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic + value: 'true' + - key: cppcoreguidelines-explicit-virtual-functions.IgnoreDestructors + value: 'true' + - key: llvm-qualified-auto.AddConstToQualified + value: 'false' + - key: llvm-else-after-return.WarnOnUnfixable + value: 'false' + - key: llvm-else-after-return.WarnOnConditionVariables + value: 'false' +... diff --git a/.clang-tidy-ignore b/.clang-tidy-ignore new file mode 100644 index 0000000..940d5db --- /dev/null +++ b/.clang-tidy-ignore @@ -0,0 +1,12 @@ +# Note: Header files included by non-ignored source files will still be analyzed. +# In that case, use the HeaderFilterRegex field in .clang-tidy + +# Ignore all protobuf/gRPC generated source files +*.pb.cc +*.pb.h + +# ignore third party source files +/thirdparty/* + +# ignore generated files +/cmake-build-* diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml new file mode 100644 index 0000000..4a01747 --- /dev/null +++ b/.github/workflows/check.yml @@ -0,0 +1,104 @@ +name: Check + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + + workflow_dispatch: + +defaults: + run: + shell: bash -ileo pipefail {0} + +jobs: + check: + strategy: + fail-fast: false + matrix: + include: + - address_check: ON + undefined_check: ON + thread_check: OFF + coverage: OFF + - address_check: OFF + undefined_check: OFF + thread_check: ON + coverage: OFF + - address_check: OFF + undefined_check: OFF + thread_check: OFF + coverage: ON + + name: "Check - Address: ${{ matrix.address_check }} Thread: ${{ matrix.thread_check }} Coverage: ${{ matrix.coverage }}" + + runs-on: ubuntu-latest + container: + image: mayjojo/cxx-cmake-project-template:latest + + steps: + - uses: actions/checkout@v4 + + - name: Build + run: | + set -eux + CPUS=$(grep -c ^processor /proc/cpuinfo) + + mkdir ${GITHUB_WORKSPACE}/build && cd ${GITHUB_WORKSPACE}/build + cmake -DCMAKE_TOOLCHAIN_FILE=/opt/rh/gcc-toolset-13/devel-toolchain.cmake \ + -DCMAKE_BUILD_TYPE=Debug \ + -DDEMO_ENABLE_TEST=ON \ + -DDEMO_ENABLE_EXAMPLE=ON \ + -DDEMO_ENABLE_CLANG_TIDY=ON \ + -DDEMO_ENABLE_COVERAGE=${{ matrix.coverage }} \ + -DDEMO_ENABLE_SANITIZERS=ON \ + -DSANITIZE_ADDRESS=${{ matrix.address_check }} \ + -DSANITIZE_UNDEFINED=${{ matrix.undefined_check }} \ + -DSANITIZE_THREAD=${{ matrix.thread_check }} \ + ${GITHUB_WORKSPACE} + + make -j ${CPUS} + make check_public_header + make install + + cpack -G RPM + cpack -G DEB + + - name: Test + run: | + set -eux + CPUS=$(grep -c ^processor /proc/cpuinfo) + + cd ${GITHUB_WORKSPACE}/build + ctest --force-new-ctest-process --verbose -j ${CPUS} + + if [ "${{ matrix.coverage }}" = "ON" ]; then + make coverage-xml + fi + + - name: Code Coverage Summary Report + if: matrix.coverage == 'ON' + uses: irongut/CodeCoverageSummary@v1.3.0 + with: + filename: build/coverage-xml.xml + badge: true + format: markdown + output: both + + - name: Add Coverage PR Comment + uses: marocchino/sticky-pull-request-comment@v2 + if: ${{ (github.event_name == 'pull_request') && (matrix.coverage == 'ON') }} + with: + recreate: true + path: code-coverage-results.md + + - run: git config --global --add safe.directory "$GITHUB_WORKSPACE" + + - name: Upload coverage reports to Codecov + uses: codecov/codecov-action@v4.0.1 + if: matrix.coverage == 'ON' + with: + token: ${{ secrets.CODECOV_TOKEN }} + slug: wangzw/cxx-cmake-project-template + files: build/coverage-xml.xml diff --git a/.github/workflows/image.yml b/.github/workflows/image.yml new file mode 100644 index 0000000..b3791cf --- /dev/null +++ b/.github/workflows/image.yml @@ -0,0 +1,40 @@ +name: Image + +on: + push: + tags: + - 'v*' + + workflow_dispatch: + +jobs: + docker: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Docker meta + id: meta + uses: docker/metadata-action@v5 + with: + images: mayjojo/cxx-cmake-project-template + tags: | + type=ref,event=branch + type=ref,event=pr + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + + - name: Login to DockerHub + if: github.event_name != 'pull_request' + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Build and push + uses: docker/build-push-action@v5 + with: + context: . + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7ce1584 --- /dev/null +++ b/.gitignore @@ -0,0 +1,38 @@ +# 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 +/.venv/ +/.idea/ +/.vagrant/ +/cmake-build-*/ +/.clion.source.upload.marker +/src/demo/common/version.h diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..212968d --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,42 @@ +cmake_minimum_required(VERSION 3.27) + +list(APPEND CMAKE_MODULE_PATH + ${CMAKE_SOURCE_DIR}/cmake + ${CMAKE_SOURCE_DIR}/cmake/sanitizers/cmake +) + +project(demo VERSION 0.0.1 LANGUAGES C CXX) + +set(DEMO_VERSION_STRING ${PROJECT_VERSION}) +string(REPLACE "." ";" VERSION_LIST ${DEMO_VERSION_STRING}) +list(GET VERSION_LIST 0 DEMO_VERSION_MAJOR) +list(GET VERSION_LIST 1 DEMO_VERSION_MINOR) +list(GET VERSION_LIST 2 DEMO_VERSION_PATCH) + +set(PROJECT_CONTACT "release@example.com") +set(PROJECT_DESCRIPTION "Example C++ project") +set(PROJECT_DESCRIPTION_LONG "Example C++ project") +set(PROJECT_LICENSE "Apache License 2.0") +set(PROJECT_PACKAGE_GROUP "development") +set(PROJECT_URL "https://www.example.com") +set(PROJECT_VENDOR "example") + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED True) + +set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -O0") +set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0") + +set(CMAKE_VERBOSE_MAKEFILE ON) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +set(CMAKE_FIND_PACKAGE_PREFER_CONFIG ON) +set(CMAKE_POSITION_INDEPENDENT_CODE ON) + +include(options) +include(build) +include(docs) + +add_subdirectory(src) + +include(install) +include(package) diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..0254c26 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,59 @@ +FROM oraclelinux:9 as base + +RUN yum install -y make which git flex bison rpm-build rpm-sign sudo tmux \ + gcc-toolset-13 gcc-toolset-13-libasan-devel gcc-toolset-13-libasan-devel gcc-toolset-13-liblsan-devel \ + gcc-toolset-13-libtsan-devel gcc-toolset-13-libubsan-devel gcc-toolset-13-libatomic-devel \ + clang-tools-extra \ + && yum clean all \ + && ln -s /opt/rh/gcc-toolset-13/enable /etc/profile.d/gcc-toolset.sh + +RUN curl -sSfL -o cmake-3.29.2-linux.sh \ + https://github.com/Kitware/CMake/releases/download/v3.29.2/cmake-3.29.2-linux-$(uname -m).sh \ + && bash cmake-3.29.2-linux.sh -- --prefix=/usr --skip-license \ + && /usr/bin/rm -f cmake-3.29.2-linux.sh + +RUN <>/opt/rh/gcc-toolset-13/devel-toolchain.cmake +set(DEVELOP_COMPILER_ROOT /opt/rh/gcc-toolset-13/root) + +set(CMAKE_C_COMPILER /opt/rh/gcc-toolset-13/root/usr/bin/gcc) +set(CMAKE_C_COMPILER_AR /opt/rh/gcc-toolset-13/root/usr/bin/ar) +set(CMAKE_C_COMPILER_RANLIB /opt/rh/gcc-toolset-13/root/usr/bin/ranlib) + +set(CMAKE_CXX_COMPILER /opt/rh/gcc-toolset-13/root/usr/bin/g++) +set(CMAKE_CXX_COMPILER_AR /opt/rh/gcc-toolset-13/root/usr/bin/ar) +set(CMAKE_CXX_COMPILER_RANLIB /opt/rh/gcc-toolset-13/root/usr/bin/ranlib) + +set(CMAKE_AR /opt/rh/gcc-toolset-13/root/usr/bin/ar) +set(CMAKE_RANLIB /opt/rh/gcc-toolset-13/root/usr/bin/ranlib) +set(CMAKE_LINKER /opt/rh/gcc-toolset-13/root/usr/bin/ld) + +set(CMAKE_NM /opt/rh/gcc-toolset-13/root/usr/bin/nm) +set(CMAKE_OBJCOPY /opt/rh/gcc-toolset-13/root/usr/bin/objcopy) +set(CMAKE_OBJDUMP /opt/rh/gcc-toolset-13/root/usr/bin/objdump) +set(CMAKE_ADDR2LINE /opt/rh/gcc-toolset-13/root/usr/bin/addr2line) +set(CMAKE_READELF /opt/rh/gcc-toolset-13/root/usr/bin/readelf) +set(CMAKE_STRIP /opt/rh/gcc-toolset-13/root/usr/bin/strip) + +set(GCOV_PATH /opt/rh/gcc-toolset-13/root/usr/bin/gcov) +set(CPPFILT_PATH /opt/rh/gcc-toolset-13/root/usr/bin/c++filt) + +EOF + + +FROM base as build + +COPY thirdparty/CMakeLists.txt /tmp/CMakeLists.txt + +RUN mkdir -p /tmp/build \ + && cd /tmp/build \ + && cmake -DCMAKE_TOOLCHAIN_FILE=/opt/rh/gcc-toolset-13/devel-toolchain.cmake .. \ + && make -j$(nproc --all) + +FROM base + +COPY --from=build /usr/local/ /usr/local/ + +RUN yum install -y python3-pip python-unversioned-command \ + && yum clean all + +RUN pip3 install --no-cache-dir gcovr diff --git a/README.md b/README.md new file mode 100644 index 0000000..9ec6e6a --- /dev/null +++ b/README.md @@ -0,0 +1,9 @@ +# Demo {#mainpage} + +## Motivation + +## Features + +## Installation + +## Examples diff --git a/check_header.py b/check_header.py new file mode 100755 index 0000000..13aa044 --- /dev/null +++ b/check_header.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python3 + +import sys +import os + + +def check_single_include(include_base, installed_headers, required_headers, errors, file, line): + line = line[len("#include"):].strip() + if len(line) == 0 or line[0] != '"' or line[-1] != '"': + return + + line = line.strip('"') + path = os.path.join(include_base, line) + # included header does not exist + if (not os.path.exists(path)) or (not os.path.isfile(path)): + errors.add('header "{}" in file "{}" does not exist'.format( + line, os.path.relpath(file, include_base))) + if path not in installed_headers: + errors.add('header "{}" in file "{}" does not included in public headers'.format( + line, os.path.relpath(file, include_base))) + required_headers.append(path) + + +def check_single_header(include_base, installed_headers, required_headers, errors, file): + with open(file, 'r') as content: + for l in content: + line = l.strip() + if line.startswith("#include"): + check_single_include(include_base, installed_headers, required_headers, errors, file, line) + + +def main(): + if len(sys.argv) < 2: + sys.exit(-1) + + include_base = sys.argv[1] + installed_headers = sys.argv[2:] + errors = set() + loop = True + + while loop: + loop = False + required_headers = [] + + for file in installed_headers: + check_single_header(include_base, installed_headers, required_headers, errors, file) + for file in installed_headers: + if "/detail/" in file and file not in required_headers: + installed_headers.remove(file) + loop = True + errors.add('internal header "{}" should not be installed'.format( + os.path.relpath(file, include_base))) + + if errors: + for err in errors: + print("ERROR:", err, file=sys.stderr) + exit(-1) + + +if __name__ == '__main__': + main() diff --git a/clang-tidy-wrap b/clang-tidy-wrap new file mode 100755 index 0000000..f0b13a5 --- /dev/null +++ b/clang-tidy-wrap @@ -0,0 +1,72 @@ +#!/usr/bin/env python3 + +import fnmatch +import subprocess +import sys +from os import path + +DEFAULT_CLANG_TIDY_IGNORE = ".clang-tidy-ignore" +DEFAULT_CLANG_TIDY_CONFIG = ".clang-tidy" + + +def filter_file(file): + """Filter out all files specified via globs in DEFAULT_CLANG_TIDY_IGNORE. + """ + ignore_file = path.join(path.dirname(sys.argv[0]), DEFAULT_CLANG_TIDY_IGNORE); + + globs = list() + with open(ignore_file, 'r') as tidy_ignore: + for l in tidy_ignore: + line = l.strip() + # Tolerate comments and empty lines + if not line or line.startswith('#'): + continue + globs.append(line) + + for g in globs: + if fnmatch.fnmatch(file, g): + return True + + return False + + +def filter_args(args): + ignored_flags = ["-fcoroutines", "-flto=auto", "-fno-fat-lto-objects", "-fprofile-abs-path"] + new_arg = [arg for arg in args if + arg not in ignored_flags and not arg.startswith("EXTRA_CLANG_TIDY_COMPILER_FLAGS")] + extra_compiler_flags = [arg[len("EXTRA_CLANG_TIDY_COMPILER_FLAGS="):] for arg in args if + arg.startswith("EXTRA_CLANG_TIDY_COMPILER_FLAGS")] + for arg in new_arg: + if arg.endswith(".h") or arg.endswith(".cc") or arg.endswith(".cpp"): + if filter_file(arg): + return [] + + return new_arg + extra_compiler_flags + + +def invoke_clang_tidy(args): + try: + config = "--config-file=" + path.join(path.dirname(sys.argv[0]), DEFAULT_CLANG_TIDY_CONFIG) + cmds = " ".join([args[0], config] + args[1:]) + print(cmds) + output = subprocess.run(args=cmds, shell=True) + sys.exit(output.returncode) + except FileNotFoundError as e: + print(e, file=sys.stderr) + sys.exit(e.errno) + except Exception as e: + print(e, file=sys.stderr) + sys.exit(-1) + + +def main(): + args = filter_args(sys.argv[1:]) + + if not args: + sys.exit(0) + + invoke_clang_tidy(args) + + +if __name__ == '__main__': + main() diff --git a/cmake/build.cmake b/cmake/build.cmake new file mode 100644 index 0000000..58febe6 --- /dev/null +++ b/cmake/build.cmake @@ -0,0 +1,189 @@ +# setup sanitizers and coverage +if (DEMO_ENABLE_SANITIZERS) + find_package(Sanitizers REQUIRED) +endif () + +if (DEMO_ENABLE_COVERAGE) + include(coverage) + + setup_target_for_coverage_gcovr_html( + NAME coverage + BASE_DIRECTORY "${CMAKE_SOURCE_DIR}" + EXCLUDE ${CMAKE_BINARY_DIR}/* ${CMAKE_SOURCE_DIR}/src/tests/* ${CMAKE_SOURCE_DIR}/\\.*_test.cpp + ) + setup_target_for_coverage_gcovr_xml( + NAME coverage-xml + BASE_DIRECTORY "${CMAKE_SOURCE_DIR}" + EXCLUDE ${CMAKE_BINARY_DIR}/* ${CMAKE_SOURCE_DIR}/src/tests/* ${CMAKE_SOURCE_DIR}/\\.*_test.cpp + ) + + set(DEMO_COVERAGE_TARGETS coverage coverage-xml) +endif () + +# setup clang-tidy +if (DEMO_ENABLE_CLANG_TIDY) + find_program(CLANG_TIDY_EXE NAMES "clang-tidy" REQUIRED) + list(TRANSFORM EXTRA_CLANG_TIDY_COMPILER_FLAGS PREPEND "EXTRA_CLANG_TIDY_COMPILER_FLAGS=") + set(CLANG_TIDY_COMMAND "${CMAKE_SOURCE_DIR}/clang-tidy-wrap" "${CLANG_TIDY_EXE}" ${EXTRA_CLANG_TIDY_COMPILER_FLAGS}) +endif () + +# function used to add an demo library with clang-tidy, sanitizers and coverage configure +function(demo_add_library) + cmake_parse_arguments(PARSE_ARGV 0 AAL "" "TARGET" "HEADERS;FILES") + add_library(${AAL_TARGET} STATIC) + + target_sources(${AAL_TARGET} + PUBLIC FILE_SET HEADERS + BASE_DIRS ${CMAKE_SOURCE_DIR}/src + FILES + ${AAL_HEADERS} + PRIVATE + ${AAL_FILES} + ) + + set_target_properties(${AAL_TARGET} PROPERTIES LINKER_LANGUAGE CXX) + + target_include_directories(${AAL_TARGET} + PUBLIC + "$" + "$" + ) + + if (DEMO_ENABLE_CLANG_TIDY) + set_target_properties(${AAL_TARGET} PROPERTIES CXX_CLANG_TIDY "${CLANG_TIDY_COMMAND}") + endif () + + if (DEMO_ENABLE_SANITIZERS AND AAL_FILES) + add_sanitizers(${AAL_TARGET}) + endif () + + if (DEMO_ENABLE_COVERAGE) + append_coverage_compiler_flags_to_target(${AAL_TARGET}) + + foreach (COVERAGE_TARGET IN LISTS DEMO_COVERAGE_TARGETS) + add_dependencies(${COVERAGE_TARGET} ${AAL_TARGET}) + endforeach () + endif () + + install(TARGETS ${AAL_TARGET} + EXPORT ${PROJECT_NAME}-targets + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + FILE_SET HEADERS DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + ) +endfunction() + +function(demo_add_executable) + cmake_parse_arguments(PARSE_ARGV 0 AAL "" "TARGET" "HEADERS;FILES") + add_executable(${AAL_TARGET}) + + target_sources(${AAL_TARGET} + PUBLIC FILE_SET HEADERS + BASE_DIRS ${CMAKE_SOURCE_DIR}/src + FILES + ${AAL_HEADERS} + PRIVATE + ${AAL_FILES} + ) + + set_target_properties(${AAL_TARGET} PROPERTIES LINKER_LANGUAGE CXX) + + target_include_directories(${AAL_TARGET} + PUBLIC + "$" + "$" + ) + + if (DEMO_ENABLE_CLANG_TIDY) + set_target_properties(${AAL_TARGET} PROPERTIES CXX_CLANG_TIDY "${CLANG_TIDY_COMMAND}") + endif () + + if (DEMO_ENABLE_SANITIZERS AND AAL_FILES) + add_sanitizers(${AAL_TARGET}) + endif () + + if (DEMO_ENABLE_COVERAGE) + append_coverage_compiler_flags_to_target(${AAL_TARGET}) + + foreach (COVERAGE_TARGET IN LISTS DEMO_COVERAGE_TARGETS) + add_dependencies(${COVERAGE_TARGET} ${AAL_TARGET}) + endforeach () + endif () + + install(TARGETS ${AAL_TARGET} + EXPORT ${PROJECT_NAME}-targets + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + FILE_SET HEADERS DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + ) +endfunction() + +function(demo_add_example) + cmake_parse_arguments(PARSE_ARGV 0 AAL "" "TARGET" "FILES") + add_executable(${AAL_TARGET} ${AAL_FILES}) + + if (DEMO_ENABLE_SANITIZERS AND AAL_FILES) + add_sanitizers(${AAL_TARGET}) + endif () +endfunction() + +if (DEMO_ENABLE_TEST) + # setup google test and ctest + find_package(GTest REQUIRED) + include(GoogleTest) + set_property(GLOBAL PROPERTY CTEST_TARGETS_ADDED 1) + include(CTest) + + # add an demo unit test with sanitizers and coverage configure + function(demo_add_test) + cmake_parse_arguments(PARSE_ARGV 0 AAL "" "TARGET" "FILES") + add_executable(${AAL_TARGET} ${AAL_FILES}) + set_target_properties(${AAL_TARGET} PROPERTIES LINKER_LANGUAGE CXX) + target_link_libraries(${AAL_TARGET} PRIVATE GTest::gtest_main GTest::gmock) + gtest_discover_tests(${AAL_TARGET}) + + if (DEMO_ENABLE_SANITIZERS) + add_sanitizers(${AAL_TARGET}) + endif () + + if (DEMO_ENABLE_COVERAGE) + append_coverage_compiler_flags_to_target(${AAL_TARGET}) + + foreach (COVERAGE_TARGET IN LISTS DEMO_COVERAGE_TARGETS) + add_dependencies(${COVERAGE_TARGET} ${AAL_TARGET}) + endforeach () + endif () + endfunction() +endif () + +#setup benchmark test +if (DEMO_ENABLE_BENCHMARK) + if (CMAKE_BUILD_TYPE STREQUAL "Debug") + message(WARNING "Running benchmark with Debug build may be misleading") + endif () + + find_package(benchmark REQUIRED) + # target used to run all benchmark + add_custom_target(benchmark) + + function(demo_add_benchmark) + cmake_parse_arguments(PARSE_ARGV 0 AAL "" "TARGET" "FILES") + add_executable(${AAL_TARGET} ${AAL_FILES}) + target_link_libraries(${AAL_TARGET} PRIVATE benchmark::benchmark_main) + + add_custom_target(run_${AAL_TARGET} + COMMAND $ + --benchmark_out=${AAL_TARGET}.json + --benchmark_out_format=json + # Set output files as GENERATED (will be removed on 'make clean') + DEPENDS ${AAL_TARGET} + BYPRODUCTS + ${AAL_TARGET}.json + WORKING_DIRECTORY ${PROJECT_BINARY_DIR} + VERBATIM # Protect arguments to commands + COMMENT "Run benchmark ${AAL_TARGET} and output result as json format." + ) + + add_dependencies(benchmark run_${AAL_TARGET}) + endfunction() +endif () diff --git a/cmake/coverage.cmake b/cmake/coverage.cmake new file mode 100644 index 0000000..1ccc142 --- /dev/null +++ b/cmake/coverage.cmake @@ -0,0 +1,801 @@ +# Copyright (c) 2012 - 2017, Lars Bilke +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# 3. Neither the name of the copyright holder nor the names of its contributors +# may be used to endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# CHANGES: +# +# 2012-01-31, Lars Bilke +# - Enable Code Coverage +# +# 2013-09-17, Joakim Söderberg +# - Added support for Clang. +# - Some additional usage instructions. +# +# 2016-02-03, Lars Bilke +# - Refactored functions to use named parameters +# +# 2017-06-02, Lars Bilke +# - Merged with modified version from github.com/ufz/ogs +# +# 2019-05-06, Anatolii Kurotych +# - Remove unnecessary --coverage flag +# +# 2019-12-13, FeRD (Frank Dana) +# - Deprecate COVERAGE_LCOVR_EXCLUDES and COVERAGE_GCOVR_EXCLUDES lists in favor +# of tool-agnostic COVERAGE_EXCLUDES variable, or EXCLUDE setup arguments. +# - CMake 3.4+: All excludes can be specified relative to BASE_DIRECTORY +# - All setup functions: accept BASE_DIRECTORY, EXCLUDE list +# - Set lcov basedir with -b argument +# - Add automatic --demangle-cpp in lcovr, if 'c++filt' is available (can be +# overridden with NO_DEMANGLE option in setup_target_for_coverage_lcovr().) +# - Delete output dir, .info file on 'make clean' +# - Remove Python detection, since version mismatches will break gcovr +# - Minor cleanup (lowercase function names, update examples...) +# +# 2019-12-19, FeRD (Frank Dana) +# - Rename Lcov outputs, make filtered file canonical, fix cleanup for targets +# +# 2020-01-19, Bob Apthorpe +# - Added gfortran support +# +# 2020-02-17, FeRD (Frank Dana) +# - Make all add_custom_target()s VERBATIM to auto-escape wildcard characters +# in EXCLUDEs, and remove manual escaping from gcovr targets +# +# 2021-01-19, Robin Mueller +# - Add CODE_COVERAGE_VERBOSE option which will allow to print out commands which are run +# - Added the option for users to set the GCOVR_ADDITIONAL_ARGS variable to supply additional +# flags to the gcovr command +# +# 2020-05-04, Mihchael Davis +# - Add -fprofile-abs-path to make gcno files contain absolute paths +# - Fix BASE_DIRECTORY not working when defined +# - Change BYPRODUCT from folder to index.html to stop ninja from complaining about double defines +# +# 2021-05-10, Martin Stump +# - Check if the generator is multi-config before warning about non-Debug builds +# +# 2022-02-22, Marko Wehle +# - Change gcovr output from -o for --xml and --html output respectively. +# This will allow for Multiple Output Formats at the same time by making use of GCOVR_ADDITIONAL_ARGS, e.g. GCOVR_ADDITIONAL_ARGS "--txt". +# +# 2022-09-28, Sebastian Mueller +# - fix append_coverage_compiler_flags_to_target to correctly add flags +# - replace "-fprofile-arcs -ftest-coverage" with "--coverage" (equivalent) +# +# USAGE: +# +# 1. Copy this file into your cmake modules path. +# +# 2. Add the following line to your CMakeLists.txt (best inside an if-condition +# using a CMake option() to enable it just optionally): +# include(CodeCoverage) +# +# 3. Append necessary compiler flags for all supported source files: +# append_coverage_compiler_flags() +# Or for specific target: +# append_coverage_compiler_flags_to_target(YOUR_TARGET_NAME) +# +# 3.a (OPTIONAL) Set appropriate optimization flags, e.g. -O0, -O1 or -Og +# +# 4. If you need to exclude additional directories from the report, specify them +# using full paths in the COVERAGE_EXCLUDES variable before calling +# setup_target_for_coverage_*(). +# Example: +# set(COVERAGE_EXCLUDES +# '${PROJECT_SOURCE_DIR}/src/dir1/*' +# '/path/to/my/src/dir2/*') +# Or, use the EXCLUDE argument to setup_target_for_coverage_*(). +# Example: +# setup_target_for_coverage_lcov( +# NAME coverage +# EXECUTABLE testrunner +# EXCLUDE "${PROJECT_SOURCE_DIR}/src/dir1/*" "/path/to/my/src/dir2/*") +# +# 4.a NOTE: With CMake 3.4+, COVERAGE_EXCLUDES or EXCLUDE can also be set +# relative to the BASE_DIRECTORY (default: PROJECT_SOURCE_DIR) +# Example: +# set(COVERAGE_EXCLUDES "dir1/*") +# setup_target_for_coverage_gcovr_html( +# NAME coverage +# EXECUTABLE testrunner +# BASE_DIRECTORY "${PROJECT_SOURCE_DIR}/src" +# EXCLUDE "dir2/*") +# +# 5. Use the functions described below to create a custom make target which +# runs your test executable and produces a code coverage report. +# +# 6. Build a Debug build: +# cmake -DCMAKE_BUILD_TYPE=Debug .. +# make +# make my_coverage_target +# + +include(CMakeParseArguments) + +option(CODE_COVERAGE_VERBOSE "Verbose information" FALSE) + +# Check prereqs + +if (NOT GCOV_PATH) + find_program(GCOV_PATH gcov REQUIRED) +endif () + +if (NOT LLVM_COV_PATH) + find_program(LLVM_COV_PATH llvm-cov) + + if (NOT LLVM_COV_PATH AND ${CMAKE_SYSTEM_NAME} MATCHES "Darwin") + set(LLVM_COV_PATH ${GCOV_PATH}) + endif () +endif () + +if (NOT LCOV_PATH) + find_program(LCOV_PATH NAMES lcov lcov.bat lcov.exe lcov.perl) +endif () + +if (NOT FASTCOV_PATH) + find_program(FASTCOV_PATH NAMES fastcov fastcov.py) +endif () + +if (NOT GENHTML_PATH) + find_program(GENHTML_PATH NAMES genhtml genhtml.perl genhtml.bat) +endif () + +if (NOT GCOVR_PATH) + find_program(GCOVR_PATH gcovr PATHS ${CMAKE_SOURCE_DIR}/scripts/test) +endif () + +if (NOT CPPFILT_PATH) + find_program(CPPFILT_PATH NAMES c++filt) +endif () + +# Check supported compiler (Clang, GNU and Flang) +get_property(LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES) +foreach (LANG ${LANGUAGES}) + if ("${CMAKE_${LANG}_COMPILER_ID}" MATCHES "(Apple)?[Cc]lang") + if ("${CMAKE_${LANG}_COMPILER_VERSION}" VERSION_LESS 3) + message(FATAL_ERROR "Clang version must be 3.0.0 or greater! Aborting...") + endif () + elseif (NOT "${CMAKE_${LANG}_COMPILER_ID}" MATCHES "GNU" + AND NOT "${CMAKE_${LANG}_COMPILER_ID}" MATCHES "(LLVM)?[Ff]lang") + message(FATAL_ERROR "Compiler is not GNU or Flang! Aborting...") + endif () +endforeach () + +set(COVERAGE_COMPILER_FLAGS "-g --coverage" CACHE INTERNAL "") +if (CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Clang)") + include(CheckCXXCompilerFlag) + check_cxx_compiler_flag(-fprofile-abs-path HAVE_fprofile_abs_path) + if (HAVE_fprofile_abs_path) + set(COVERAGE_COMPILER_FLAGS "${COVERAGE_COMPILER_FLAGS} -fprofile-abs-path") + endif () +endif () + +set(CMAKE_Fortran_FLAGS_COVERAGE + ${COVERAGE_COMPILER_FLAGS} + CACHE STRING "Flags used by the Fortran compiler during coverage builds." + FORCE) +set(CMAKE_CXX_FLAGS_COVERAGE + ${COVERAGE_COMPILER_FLAGS} + CACHE STRING "Flags used by the C++ compiler during coverage builds." + FORCE) +set(CMAKE_C_FLAGS_COVERAGE + ${COVERAGE_COMPILER_FLAGS} + CACHE STRING "Flags used by the C compiler during coverage builds." + FORCE) +set(CMAKE_EXE_LINKER_FLAGS_COVERAGE + "" + CACHE STRING "Flags used for linking binaries during coverage builds." + FORCE) +set(CMAKE_SHARED_LINKER_FLAGS_COVERAGE + "" + CACHE STRING "Flags used by the shared libraries linker during coverage builds." + FORCE) +mark_as_advanced( + CMAKE_Fortran_FLAGS_COVERAGE + CMAKE_CXX_FLAGS_COVERAGE + CMAKE_C_FLAGS_COVERAGE + CMAKE_EXE_LINKER_FLAGS_COVERAGE + CMAKE_SHARED_LINKER_FLAGS_COVERAGE) + +get_property(GENERATOR_IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) +if (NOT (CMAKE_BUILD_TYPE STREQUAL "Debug" OR GENERATOR_IS_MULTI_CONFIG)) + message(FATAL_ERROR "Code coverage results with an optimised (non-Debug) build may be misleading") +endif () # NOT (CMAKE_BUILD_TYPE STREQUAL "Debug" OR GENERATOR_IS_MULTI_CONFIG) + +if (CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_Fortran_COMPILER_ID STREQUAL "GNU") + link_libraries(gcov) +endif () + +# Defines a target for running and collection code coverage information +# Builds dependencies, runs the given executable and outputs reports. +# NOTE! The executable should always have a ZERO as exit code otherwise +# the coverage generation will not complete. +# +# setup_target_for_coverage_lcov( +# NAME testrunner_coverage # New target name +# EXECUTABLE testrunner -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR +# DEPENDENCIES testrunner # Dependencies to build first +# BASE_DIRECTORY "../" # Base directory for report +# # (defaults to PROJECT_SOURCE_DIR) +# EXCLUDE "src/dir1/*" "src/dir2/*" # Patterns to exclude (can be relative +# # to BASE_DIRECTORY, with CMake 3.4+) +# NO_DEMANGLE # Don't demangle C++ symbols +# # even if c++filt is found +# ) +function(setup_target_for_coverage_lcov) + + set(options NO_DEMANGLE SONARQUBE) + set(oneValueArgs BASE_DIRECTORY NAME) + set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES LCOV_ARGS GENHTML_ARGS) + cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if (NOT LCOV_PATH) + message(FATAL_ERROR "lcov not found! Aborting...") + endif () # NOT LCOV_PATH + + if (NOT GENHTML_PATH) + message(FATAL_ERROR "genhtml not found! Aborting...") + endif () # NOT GENHTML_PATH + + # Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR + if (DEFINED Coverage_BASE_DIRECTORY) + get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE) + else () + set(BASEDIR ${PROJECT_SOURCE_DIR}) + endif () + + # Collect excludes (CMake 3.4+: Also compute absolute paths) + set(LCOV_EXCLUDES "") + foreach (EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_LCOV_EXCLUDES}) + if (CMAKE_VERSION VERSION_GREATER 3.4) + get_filename_component(EXCLUDE ${EXCLUDE} ABSOLUTE BASE_DIR ${BASEDIR}) + endif () + list(APPEND LCOV_EXCLUDES "${EXCLUDE}") + endforeach () + list(REMOVE_DUPLICATES LCOV_EXCLUDES) + + # Conditional arguments + if (CPPFILT_PATH AND NOT ${Coverage_NO_DEMANGLE}) + set(GENHTML_EXTRA_ARGS "--demangle-cpp") + endif () + + # Setting up commands which will be run to generate coverage data. + # Cleanup lcov + set(LCOV_CLEAN_CMD + ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} -directory . + -b ${BASEDIR} --zerocounters + ) + # Create baseline to make sure untouched files show up in the report + set(LCOV_BASELINE_CMD + ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} -c -i -d . -b + ${BASEDIR} -o ${Coverage_NAME}.base + ) + # Run tests + set(LCOV_EXEC_TESTS_CMD ${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS}) + + # Capturing lcov counters and generating report + set(LCOV_CAPTURE_CMD + ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} --directory . -b + ${BASEDIR} --capture --output-file ${Coverage_NAME}.capture + ) + # add baseline counters + set(LCOV_BASELINE_COUNT_CMD + ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} -a ${Coverage_NAME}.base + -a ${Coverage_NAME}.capture --output-file ${Coverage_NAME}.total + ) + # filter collected data to final coverage report + set(LCOV_FILTER_CMD + ${LCOV_PATH} ${Coverage_LCOV_ARGS} --gcov-tool ${GCOV_PATH} --remove + ${Coverage_NAME}.total ${LCOV_EXCLUDES} --output-file ${Coverage_NAME}.info + ) + # Generate HTML output + set(LCOV_GEN_HTML_CMD + ${GENHTML_PATH} ${GENHTML_EXTRA_ARGS} ${Coverage_GENHTML_ARGS} -o + ${Coverage_NAME} ${Coverage_NAME}.info + ) + if (${Coverage_SONARQUBE}) + # Generate SonarQube output + set(GCOVR_XML_CMD + ${GCOVR_PATH} --sonarqube ${Coverage_NAME}_sonarqube.xml -r ${BASEDIR} ${GCOVR_ADDITIONAL_ARGS} + ${GCOVR_EXCLUDE_ARGS} --object-directory=${PROJECT_BINARY_DIR} + ) + set(GCOVR_XML_CMD_COMMAND + COMMAND ${GCOVR_XML_CMD} + ) + set(GCOVR_XML_CMD_BYPRODUCTS ${Coverage_NAME}_sonarqube.xml) + set(GCOVR_XML_CMD_COMMENT COMMENT "SonarQube code coverage info report saved in ${Coverage_NAME}_sonarqube.xml.") + endif () + + + if (CODE_COVERAGE_VERBOSE) + message(STATUS "Executed command report") + message(STATUS "Command to clean up lcov: ") + string(REPLACE ";" " " LCOV_CLEAN_CMD_SPACED "${LCOV_CLEAN_CMD}") + message(STATUS "${LCOV_CLEAN_CMD_SPACED}") + + message(STATUS "Command to create baseline: ") + string(REPLACE ";" " " LCOV_BASELINE_CMD_SPACED "${LCOV_BASELINE_CMD}") + message(STATUS "${LCOV_BASELINE_CMD_SPACED}") + + message(STATUS "Command to run the tests: ") + string(REPLACE ";" " " LCOV_EXEC_TESTS_CMD_SPACED "${LCOV_EXEC_TESTS_CMD}") + message(STATUS "${LCOV_EXEC_TESTS_CMD_SPACED}") + + message(STATUS "Command to capture counters and generate report: ") + string(REPLACE ";" " " LCOV_CAPTURE_CMD_SPACED "${LCOV_CAPTURE_CMD}") + message(STATUS "${LCOV_CAPTURE_CMD_SPACED}") + + message(STATUS "Command to add baseline counters: ") + string(REPLACE ";" " " LCOV_BASELINE_COUNT_CMD_SPACED "${LCOV_BASELINE_COUNT_CMD}") + message(STATUS "${LCOV_BASELINE_COUNT_CMD_SPACED}") + + message(STATUS "Command to filter collected data: ") + string(REPLACE ";" " " LCOV_FILTER_CMD_SPACED "${LCOV_FILTER_CMD}") + message(STATUS "${LCOV_FILTER_CMD_SPACED}") + + message(STATUS "Command to generate lcov HTML output: ") + string(REPLACE ";" " " LCOV_GEN_HTML_CMD_SPACED "${LCOV_GEN_HTML_CMD}") + message(STATUS "${LCOV_GEN_HTML_CMD_SPACED}") + + if (${Coverage_SONARQUBE}) + message(STATUS "Command to generate SonarQube XML output: ") + string(REPLACE ";" " " GCOVR_XML_CMD_SPACED "${GCOVR_XML_CMD}") + message(STATUS "${GCOVR_XML_CMD_SPACED}") + endif () + endif () + + # Setup target + add_custom_target(${Coverage_NAME} + COMMAND ${LCOV_CLEAN_CMD} + COMMAND ${LCOV_BASELINE_CMD} + COMMAND ${LCOV_EXEC_TESTS_CMD} + COMMAND ${LCOV_CAPTURE_CMD} + COMMAND ${LCOV_BASELINE_COUNT_CMD} + COMMAND ${LCOV_FILTER_CMD} + COMMAND ${LCOV_GEN_HTML_CMD} + ${GCOVR_XML_CMD_COMMAND} + + # Set output files as GENERATED (will be removed on 'make clean') + BYPRODUCTS + ${Coverage_NAME}.base + ${Coverage_NAME}.capture + ${Coverage_NAME}.total + ${Coverage_NAME}.info + ${GCOVR_XML_CMD_BYPRODUCTS} + ${Coverage_NAME}/index.html + WORKING_DIRECTORY ${PROJECT_BINARY_DIR} + DEPENDS ${Coverage_DEPENDENCIES} + VERBATIM # Protect arguments to commands + COMMENT "Resetting code coverage counters to zero.\nProcessing code coverage counters and generating report." + ) + + # Show where to find the lcov info report + add_custom_command(TARGET ${Coverage_NAME} POST_BUILD + COMMAND ; + COMMENT "Lcov code coverage info report saved in ${Coverage_NAME}.info." + ${GCOVR_XML_CMD_COMMENT} + ) + + # Show info where to find the report + add_custom_command(TARGET ${Coverage_NAME} POST_BUILD + COMMAND ; + COMMENT "Open ./${Coverage_NAME}/index.html in your browser to view the coverage report." + ) + +endfunction() # setup_target_for_coverage_lcov + +# Check and define GCOV_EXECUTABLE +function(check_gcov_executable) + # Clang not on macos + if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + if (NOT LLVM_COV_PATH) + message(FATAL_ERROR "llvm-cov not found! Aborting...") + endif () # NOT LLVM_COV_PATH + + set(GCOV_EXECUTABLE "${LLVM_COV_PATH} gcov" PARENT_SCOPE) + else () + set(GCOV_EXECUTABLE "${GCOV_PATH}" PARENT_SCOPE) + endif () +endfunction() + +# Defines a target for running and collection code coverage information +# Builds dependencies, runs the given executable and outputs reports. +# NOTE! The executable should always have a ZERO as exit code otherwise +# the coverage generation will not complete. +# +# setup_target_for_coverage_gcovr_xml( +# NAME ctest_coverage # New target name +# EXECUTABLE ctest -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR +# DEPENDENCIES executable_target # Dependencies to build first +# BASE_DIRECTORY "../" # Base directory for report +# # (defaults to PROJECT_SOURCE_DIR) +# EXCLUDE "src/dir1/*" "src/dir2/*" # Patterns to exclude (can be relative +# # to BASE_DIRECTORY, with CMake 3.4+) +# ) +# The user can set the variable GCOVR_ADDITIONAL_ARGS to supply additional flags to the +# GCVOR command. +function(setup_target_for_coverage_gcovr_xml) + + set(options NONE) + set(oneValueArgs BASE_DIRECTORY NAME) + set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES) + cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if (NOT GCOVR_PATH) + message(FATAL_ERROR "gcovr not found! Aborting...") + endif () # NOT GCOVR_PATH + + check_gcov_executable() + + # Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR + if (DEFINED Coverage_BASE_DIRECTORY) + get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE) + else () + set(BASEDIR ${PROJECT_SOURCE_DIR}) + endif () + + # Collect excludes (CMake 3.4+: Also compute absolute paths) + set(GCOVR_EXCLUDES "") + foreach (EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_GCOVR_EXCLUDES}) + if (CMAKE_VERSION VERSION_GREATER 3.4) + get_filename_component(EXCLUDE ${EXCLUDE} ABSOLUTE BASE_DIR ${BASEDIR}) + endif () + list(APPEND GCOVR_EXCLUDES "${EXCLUDE}") + endforeach () + list(REMOVE_DUPLICATES GCOVR_EXCLUDES) + + # Combine excludes to several -e arguments + set(GCOVR_EXCLUDE_ARGS "") + foreach (EXCLUDE ${GCOVR_EXCLUDES}) + list(APPEND GCOVR_EXCLUDE_ARGS "-e") + list(APPEND GCOVR_EXCLUDE_ARGS "${EXCLUDE}") + endforeach () + + # Set up commands which will be run to generate coverage data + # Run tests + set(GCOVR_XML_EXEC_TESTS_CMD ${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS}) + + # Running gcovr + set(GCOVR_XML_CMD + ${GCOVR_PATH} --gcov-executable=${GCOV_EXECUTABLE} --gcov-ignore-parse-errors --xml ${Coverage_NAME}.xml -r ${BASEDIR} ${GCOVR_ADDITIONAL_ARGS} + ${GCOVR_EXCLUDE_ARGS} --object-directory=${PROJECT_BINARY_DIR} + ) + + if (CODE_COVERAGE_VERBOSE) + message(STATUS "Executed command report") + + message(STATUS "Command to run tests: ") + string(REPLACE ";" " " GCOVR_XML_EXEC_TESTS_CMD_SPACED "${GCOVR_XML_EXEC_TESTS_CMD}") + message(STATUS "${GCOVR_XML_EXEC_TESTS_CMD_SPACED}") + + message(STATUS "Command to generate gcovr XML coverage data: ") + string(REPLACE ";" " " GCOVR_XML_CMD_SPACED "${GCOVR_XML_CMD}") + message(STATUS "${GCOVR_XML_CMD_SPACED}") + endif () + + add_custom_target(${Coverage_NAME} + COMMAND ${GCOVR_XML_EXEC_TESTS_CMD} + COMMAND ${GCOVR_XML_CMD} + + BYPRODUCTS ${Coverage_NAME}.xml + WORKING_DIRECTORY ${PROJECT_BINARY_DIR} + DEPENDS ${Coverage_DEPENDENCIES} + VERBATIM # Protect arguments to commands + COMMENT "Running gcovr to produce Cobertura code coverage report." + ) + + # Show info where to find the report + add_custom_command(TARGET ${Coverage_NAME} POST_BUILD + COMMAND ; + COMMENT "Cobertura code coverage report saved in ${Coverage_NAME}.xml." + ) +endfunction() # setup_target_for_coverage_gcovr_xml + +# Defines a target for running and collection code coverage information +# Builds dependencies, runs the given executable and outputs reports. +# NOTE! The executable should always have a ZERO as exit code otherwise +# the coverage generation will not complete. +# +# setup_target_for_coverage_gcovr_html( +# NAME ctest_coverage # New target name +# EXECUTABLE ctest -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR +# DEPENDENCIES executable_target # Dependencies to build first +# BASE_DIRECTORY "../" # Base directory for report +# # (defaults to PROJECT_SOURCE_DIR) +# EXCLUDE "src/dir1/*" "src/dir2/*" # Patterns to exclude (can be relative +# # to BASE_DIRECTORY, with CMake 3.4+) +# ) +# The user can set the variable GCOVR_ADDITIONAL_ARGS to supply additional flags to the +# GCVOR command. +function(setup_target_for_coverage_gcovr_html) + + set(options NONE) + set(oneValueArgs BASE_DIRECTORY NAME) + set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES) + cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if (NOT GCOVR_PATH) + message(FATAL_ERROR "gcovr not found! Aborting...") + endif () # NOT GCOVR_PATH + + check_gcov_executable() + + # Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR + if (DEFINED Coverage_BASE_DIRECTORY) + get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE) + else () + set(BASEDIR ${PROJECT_SOURCE_DIR}) + endif () + + # Collect excludes (CMake 3.4+: Also compute absolute paths) + set(GCOVR_EXCLUDES "") + foreach (EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_GCOVR_EXCLUDES}) + if (CMAKE_VERSION VERSION_GREATER 3.4) + get_filename_component(EXCLUDE ${EXCLUDE} ABSOLUTE BASE_DIR ${BASEDIR}) + endif () + list(APPEND GCOVR_EXCLUDES "${EXCLUDE}") + endforeach () + list(REMOVE_DUPLICATES GCOVR_EXCLUDES) + + # Combine excludes to several -e arguments + set(GCOVR_EXCLUDE_ARGS "") + foreach (EXCLUDE ${GCOVR_EXCLUDES}) + list(APPEND GCOVR_EXCLUDE_ARGS "-e") + list(APPEND GCOVR_EXCLUDE_ARGS "${EXCLUDE}") + endforeach () + + # Set up commands which will be run to generate coverage data + # Run tests + set(GCOVR_HTML_EXEC_TESTS_CMD ${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS}) + # Create folder + set(GCOVR_HTML_FOLDER_CMD + ${CMAKE_COMMAND} -E make_directory ${PROJECT_BINARY_DIR}/${Coverage_NAME} + ) + + # Running gcovr + set(GCOVR_HTML_CMD + ${GCOVR_PATH} --gcov-executable=${GCOV_EXECUTABLE} --gcov-ignore-parse-errors --html ${Coverage_NAME}/index.html --html-details -r ${BASEDIR} ${GCOVR_ADDITIONAL_ARGS} + ${GCOVR_EXCLUDE_ARGS} --object-directory=${PROJECT_BINARY_DIR} + ) + + if (CODE_COVERAGE_VERBOSE) + message(STATUS "Executed command report") + + message(STATUS "Command to run tests: ") + string(REPLACE ";" " " GCOVR_HTML_EXEC_TESTS_CMD_SPACED "${GCOVR_HTML_EXEC_TESTS_CMD}") + message(STATUS "${GCOVR_HTML_EXEC_TESTS_CMD_SPACED}") + + message(STATUS "Command to create a folder: ") + string(REPLACE ";" " " GCOVR_HTML_FOLDER_CMD_SPACED "${GCOVR_HTML_FOLDER_CMD}") + message(STATUS "${GCOVR_HTML_FOLDER_CMD_SPACED}") + + message(STATUS "Command to generate gcovr HTML coverage data: ") + string(REPLACE ";" " " GCOVR_HTML_CMD_SPACED "${GCOVR_HTML_CMD}") + message(STATUS "${GCOVR_HTML_CMD_SPACED}") + endif () + + add_custom_target(${Coverage_NAME} + COMMAND ${GCOVR_HTML_EXEC_TESTS_CMD} + COMMAND ${GCOVR_HTML_FOLDER_CMD} + COMMAND ${GCOVR_HTML_CMD} + + BYPRODUCTS ${PROJECT_BINARY_DIR}/${Coverage_NAME}/index.html # report directory + WORKING_DIRECTORY ${PROJECT_BINARY_DIR} + DEPENDS ${Coverage_DEPENDENCIES} + VERBATIM # Protect arguments to commands + COMMENT "Running gcovr to produce HTML code coverage report." + ) + + # Show info where to find the report + add_custom_command(TARGET ${Coverage_NAME} POST_BUILD + COMMAND ; + COMMENT "Open ./${Coverage_NAME}/index.html in your browser to view the coverage report." + ) + +endfunction() # setup_target_for_coverage_gcovr_html + +# Defines a target for running and collection code coverage information +# Builds dependencies, runs the given executable and outputs reports. +# NOTE! The executable should always have a ZERO as exit code otherwise +# the coverage generation will not complete. +# +# setup_target_for_coverage_fastcov( +# NAME testrunner_coverage # New target name +# EXECUTABLE testrunner -j ${PROCESSOR_COUNT} # Executable in PROJECT_BINARY_DIR +# DEPENDENCIES testrunner # Dependencies to build first +# BASE_DIRECTORY "../" # Base directory for report +# # (defaults to PROJECT_SOURCE_DIR) +# EXCLUDE "src/dir1/" "src/dir2/" # Patterns to exclude. +# NO_DEMANGLE # Don't demangle C++ symbols +# # even if c++filt is found +# SKIP_HTML # Don't create html report +# POST_CMD perl -i -pe s!${PROJECT_SOURCE_DIR}/!!g ctest_coverage.json # E.g. for stripping source dir from file paths +# ) +function(setup_target_for_coverage_fastcov) + + set(options NO_DEMANGLE SKIP_HTML) + set(oneValueArgs BASE_DIRECTORY NAME) + set(multiValueArgs EXCLUDE EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES FASTCOV_ARGS GENHTML_ARGS POST_CMD) + cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if (NOT FASTCOV_PATH) + message(FATAL_ERROR "fastcov not found! Aborting...") + endif () + + if (NOT Coverage_SKIP_HTML AND NOT GENHTML_PATH) + message(FATAL_ERROR "genhtml not found! Aborting...") + endif () + + # Set base directory (as absolute path), or default to PROJECT_SOURCE_DIR + if (Coverage_BASE_DIRECTORY) + get_filename_component(BASEDIR ${Coverage_BASE_DIRECTORY} ABSOLUTE) + else () + set(BASEDIR ${PROJECT_SOURCE_DIR}) + endif () + + # Collect excludes (Patterns, not paths, for fastcov) + set(FASTCOV_EXCLUDES "") + foreach (EXCLUDE ${Coverage_EXCLUDE} ${COVERAGE_EXCLUDES} ${COVERAGE_FASTCOV_EXCLUDES}) + list(APPEND FASTCOV_EXCLUDES "${EXCLUDE}") + endforeach () + list(REMOVE_DUPLICATES FASTCOV_EXCLUDES) + + # Conditional arguments + if (CPPFILT_PATH AND NOT ${Coverage_NO_DEMANGLE}) + set(GENHTML_EXTRA_ARGS "--demangle-cpp") + endif () + + # Set up commands which will be run to generate coverage data + set(FASTCOV_EXEC_TESTS_CMD ${Coverage_EXECUTABLE} ${Coverage_EXECUTABLE_ARGS}) + + set(FASTCOV_CAPTURE_CMD ${FASTCOV_PATH} ${Coverage_FASTCOV_ARGS} --gcov ${GCOV_PATH} + --search-directory ${BASEDIR} + --process-gcno + --output ${Coverage_NAME}.json + --exclude ${FASTCOV_EXCLUDES} + ) + + set(FASTCOV_CONVERT_CMD ${FASTCOV_PATH} + -C ${Coverage_NAME}.json --lcov --output ${Coverage_NAME}.info + ) + + if (Coverage_SKIP_HTML) + set(FASTCOV_HTML_CMD ";") + else () + set(FASTCOV_HTML_CMD ${GENHTML_PATH} ${GENHTML_EXTRA_ARGS} ${Coverage_GENHTML_ARGS} + -o ${Coverage_NAME} ${Coverage_NAME}.info + ) + endif () + + set(FASTCOV_POST_CMD ";") + if (Coverage_POST_CMD) + set(FASTCOV_POST_CMD ${Coverage_POST_CMD}) + endif () + + if (CODE_COVERAGE_VERBOSE) + message(STATUS "Code coverage commands for target ${Coverage_NAME} (fastcov):") + + message(" Running tests:") + string(REPLACE ";" " " FASTCOV_EXEC_TESTS_CMD_SPACED "${FASTCOV_EXEC_TESTS_CMD}") + message(" ${FASTCOV_EXEC_TESTS_CMD_SPACED}") + + message(" Capturing fastcov counters and generating report:") + string(REPLACE ";" " " FASTCOV_CAPTURE_CMD_SPACED "${FASTCOV_CAPTURE_CMD}") + message(" ${FASTCOV_CAPTURE_CMD_SPACED}") + + message(" Converting fastcov .json to lcov .info:") + string(REPLACE ";" " " FASTCOV_CONVERT_CMD_SPACED "${FASTCOV_CONVERT_CMD}") + message(" ${FASTCOV_CONVERT_CMD_SPACED}") + + if (NOT Coverage_SKIP_HTML) + message(" Generating HTML report: ") + string(REPLACE ";" " " FASTCOV_HTML_CMD_SPACED "${FASTCOV_HTML_CMD}") + message(" ${FASTCOV_HTML_CMD_SPACED}") + endif () + if (Coverage_POST_CMD) + message(" Running post command: ") + string(REPLACE ";" " " FASTCOV_POST_CMD_SPACED "${FASTCOV_POST_CMD}") + message(" ${FASTCOV_POST_CMD_SPACED}") + endif () + endif () + + # Setup target + add_custom_target(${Coverage_NAME} + + # Cleanup fastcov + COMMAND ${FASTCOV_PATH} ${Coverage_FASTCOV_ARGS} --gcov ${GCOV_PATH} + --search-directory ${BASEDIR} + --zerocounters + + COMMAND ${FASTCOV_EXEC_TESTS_CMD} + COMMAND ${FASTCOV_CAPTURE_CMD} + COMMAND ${FASTCOV_CONVERT_CMD} + COMMAND ${FASTCOV_HTML_CMD} + COMMAND ${FASTCOV_POST_CMD} + + # Set output files as GENERATED (will be removed on 'make clean') + BYPRODUCTS + ${Coverage_NAME}.info + ${Coverage_NAME}.json + ${Coverage_NAME}/index.html # report directory + + WORKING_DIRECTORY ${PROJECT_BINARY_DIR} + DEPENDS ${Coverage_DEPENDENCIES} + VERBATIM # Protect arguments to commands + COMMENT "Resetting code coverage counters to zero. Processing code coverage counters and generating report." + ) + + set(INFO_MSG "fastcov code coverage info report saved in ${Coverage_NAME}.info and ${Coverage_NAME}.json.") + if (NOT Coverage_SKIP_HTML) + string(APPEND INFO_MSG " Open ${PROJECT_BINARY_DIR}/${Coverage_NAME}/index.html in your browser to view the coverage report.") + endif () + # Show where to find the fastcov info report + add_custom_command(TARGET ${Coverage_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E echo ${INFO_MSG} + ) + +endfunction() # setup_target_for_coverage_fastcov + +function(append_coverage_compiler_flags) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE) + set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE) + message(STATUS "Appending code coverage compiler flags: ${COVERAGE_COMPILER_FLAGS}") +endfunction() # append_coverage_compiler_flags + +# Setup coverage for specific library +function(append_coverage_compiler_flags_to_target name) + separate_arguments(_flag_list NATIVE_COMMAND "${COVERAGE_COMPILER_FLAGS}") + target_compile_options(${name} PRIVATE ${_flag_list}) + if (CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_Fortran_COMPILER_ID STREQUAL "GNU") + target_link_libraries(${name} PRIVATE gcov) + endif () + + target_link_options(${name} PRIVATE --coverage) + + get_target_property(TARGET_SOURCES ${name} SOURCES) + + if (TARGET_SOURCES) + set(GCDA_TARGET_SOURCES ${TARGET_SOURCES}) + list(TRANSFORM GCDA_TARGET_SOURCES APPEND ".gcda") + list(TRANSFORM GCDA_TARGET_SOURCES PREPEND "CMakeFiles/${name}.dir/") + + set(GCNO_TARGET_SOURCES ${TARGET_SOURCES}) + list(TRANSFORM GCNO_TARGET_SOURCES APPEND ".gcno") + list(TRANSFORM GCNO_TARGET_SOURCES PREPEND "CMakeFiles/${name}.dir/") + + set_property( + TARGET ${name} + APPEND + PROPERTY + ADDITIONAL_CLEAN_FILES + ${GCDA_TARGET_SOURCES} + ${GCNO_TARGET_SOURCES} + ) + endif () +endfunction() diff --git a/cmake/demoConfig.cmake.in b/cmake/demoConfig.cmake.in new file mode 100644 index 0000000..ddb2387 --- /dev/null +++ b/cmake/demoConfig.cmake.in @@ -0,0 +1,4 @@ +@PACKAGE_INIT@ +include(CMakeFindDependencyMacro) + +include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake") diff --git a/cmake/docs.cmake b/cmake/docs.cmake new file mode 100644 index 0000000..e196fa7 --- /dev/null +++ b/cmake/docs.cmake @@ -0,0 +1,37 @@ +if (DEMO_ENABLE_DOCUMENT) + FIND_PACKAGE(Doxygen REQUIRED) + + set(DOXYGEN_PROJECT_NAME "Demo") + set(DOXYGEN_EXCLUDE_PATTERNS "*_test.cpp" "*/detail/*" ${CMAKE_SOURCE_DIR}/docs/theme) + set(DOXYGEN_EXCLUDE_SYMBOLS "*::detail::*" "std::*") + set(DOXYGEN_GENERATE_TREEVIEW YES) + set(DOXYGEN_DISABLE_INDEX NO) + set(DOXYGEN_FULL_SIDEBAR NO) + set(DOXYGEN_GENERATE_XML YES) + set(DOXYGEN_STRIP_FROM_PATH ${CMAKE_SOURCE_DIR}) + + set(DOXYGEN_HTML_COLORSTYLE LIGHT) + set(DOXYGEN_HTML_HEADER ${CMAKE_SOURCE_DIR}/docs/theme/header.html) + set(DOXYGEN_HTML_EXTRA_STYLESHEET + ${CMAKE_SOURCE_DIR}/docs/theme/doxygen-awesome.css + ${CMAKE_SOURCE_DIR}/docs/theme/doxygen-awesome-sidebar-only.css + ${CMAKE_SOURCE_DIR}/docs/theme/doxygen-awesome-sidebar-only-darkmode-toggle.css + ) + set(DOXYGEN_HTML_EXTRA_FILES + ${CMAKE_SOURCE_DIR}/docs/theme/doxygen-awesome-darkmode-toggle.js + ${CMAKE_SOURCE_DIR}/docs/theme/doxygen-awesome-fragment-copy-button.js + ${CMAKE_SOURCE_DIR}/docs/theme/doxygen-awesome-interactive-toc.js + ${CMAKE_SOURCE_DIR}/docs/theme/doxygen-awesome-paragraph-link.js + ${CMAKE_SOURCE_DIR}/docs/theme/doxygen-awesome-tabs.js + ) + set(DOXYGEN_LAYOUT_FILE ${CMAKE_SOURCE_DIR}/docs/theme/DoxygenLayout.xml) + set(DOXYGEN_USE_MDFILE_AS_MAINPAGE ${CMAKE_SOURCE_DIR}/README.md) + + doxygen_add_docs(docs + ${CMAKE_SOURCE_DIR}/README.md + ${CMAKE_SOURCE_DIR}/docs + ${CMAKE_SOURCE_DIR}/src/demo + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + COMMENT "Generate doxygen document." + ) +endif () diff --git a/cmake/install.cmake b/cmake/install.cmake new file mode 100644 index 0000000..f47f1f5 --- /dev/null +++ b/cmake/install.cmake @@ -0,0 +1,65 @@ +include(GNUInstallDirs) +include(CMakePackageConfigHelpers) + +write_basic_package_version_file( + "${PROJECT_NAME}ConfigVersion.cmake" + VERSION ${PROJECT_VERSION} + COMPATIBILITY AnyNewerVersion +) + +configure_package_config_file( + "${CMAKE_SOURCE_DIR}/cmake/${PROJECT_NAME}Config.cmake.in" + "${CMAKE_BINARY_DIR}/${PROJECT_NAME}Config.cmake" + INSTALL_DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/cmake/${PROJECT_NAME} +) + +install(FILES + "${CMAKE_BINARY_DIR}/${PROJECT_NAME}Config.cmake" + "${CMAKE_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake" + DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/cmake/${PROJECT_NAME} +) + +install(EXPORT ${PROJECT_NAME}-targets + FILE ${PROJECT_NAME}Targets.cmake + NAMESPACE ${PROJECT_NAME}:: + DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/cmake/${PROJECT_NAME} +) + +export(EXPORT ${PROJECT_NAME}-targets + FILE "${CMAKE_CURRENT_BINARY_DIR}/cmake/${PROJECT_NAME}Targets.cmake" + NAMESPACE ${PROJECT_NAME}:: +) + + +function(_demo_list_public_header DIR HEADERS) + set(CURRENT_HEADERS) + get_property(TGTS DIRECTORY "${DIR}" PROPERTY BUILDSYSTEM_TARGETS) + foreach (TGT IN LISTS TGTS) + get_target_property(HEADER_SOURCES ${TGT} HEADER_SET_HEADERS) + if (HEADER_SOURCES) + list(APPEND CURRENT_HEADERS ${HEADER_SOURCES}) + endif () + endforeach () + + get_property(SUBDIRS DIRECTORY "${DIR}" PROPERTY SUBDIRECTORIES) + foreach (SUBDIR IN LISTS SUBDIRS) + _demo_list_public_header("${SUBDIR}" SUBDIR_HEADERS) + list(APPEND CURRENT_HEADERS ${SUBDIR_HEADERS}) + endforeach () + + set(${HEADERS} ${CURRENT_HEADERS} PARENT_SCOPE) +endfunction() + +function(demo_check_public_header) + _demo_list_public_header(${CMAKE_SOURCE_DIR} HEADERS) + add_custom_target(check_public_header + COMMAND ${PROJECT_SOURCE_DIR}/check_header.py + ${PROJECT_SOURCE_DIR}/src + ${HEADERS} + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + VERBATIM # Protect arguments to commands + COMMENT "Check integrity of installed headers." + ) +endfunction() + +demo_check_public_header() diff --git a/cmake/options.cmake b/cmake/options.cmake new file mode 100644 index 0000000..0089953 --- /dev/null +++ b/cmake/options.cmake @@ -0,0 +1,16 @@ +set(DEMO_BUILD_NUMBER "dev" CACHE STRING "build number") + +option(DEMO_ENABLE_TEST "enable test" OFF) +option(DEMO_ENABLE_BENCHMARK "enable test" OFF) +option(DEMO_ENABLE_EXAMPLE "enable test" OFF) +option(DEMO_ENABLE_DOCUMENT "enable document" OFF) +option(DEMO_ENABLE_COVERAGE "enable code coverage" OFF) +option(DEMO_ENABLE_CLANG_TIDY "enable clang-tidy check" OFF) +option(DEMO_ENABLE_SANITIZERS "enable sanitizer" OFF) + +option(SANITIZE_ADDRESS "enable sanitizer address" ON) +option(SANITIZE_MEMORY "enable sanitizer memory" OFF) # only with clang +option(SANITIZE_THREAD "enable sanitizer thread" OFF) # conflict with others +option(SANITIZE_UNDEFINED "enable sanitizer undefined" ON) + +option(DEMO_WITH_GRPC "build grpc" OFF) diff --git a/cmake/package.cmake b/cmake/package.cmake new file mode 100644 index 0000000..169de0a --- /dev/null +++ b/cmake/package.cmake @@ -0,0 +1,33 @@ +set(CPACK_PACKAGE_NAME ${CMAKE_PROJECT_NAME}) +set(CPACK_PACKAGE_VENDOR ${PROJECT_VENDOR}) +set(CPACK_PACKAGE_CONTACT ${PROJECT_CONTACT}) +set(CPACK_PACKAGE_DESCRIPTION_SUMMARY ${PROJECT_DESCRIPTION}) + +set(CPACK_PACKAGE_VERSION ${DEMO_VERSION_STRING}) +set(CPACK_PACKAGE_VERSION_MAJOR ${DEMO_VERSION_MAJOR}) +set(CPACK_PACKAGE_VERSION_MINOR ${DEMO_VERSION_MINOR}) +set(CPACK_PACKAGE_VERSION_PATCH ${DEMO_VERSION_PATCH}) + +set(CPACK_VERBATIM_VARIABLES YES) + +set(CPACK_RPM_PACKAGE_GROUP ${PROJECT_PACKAGE_GROUP}) +set(CPACK_RPM_PACKAGE_URL ${PROJECT_URL}) +set(CPACK_RPM_PACKAGE_DESCRIPTION ${PROJECT_DESCRIPTION_LONG}) +set(CPACK_RPM_PACKAGE_LICENSE ${PROJECT_LICENSE}) +set(CPACK_RPM_SPEC_MORE_DEFINE "%define _build_id_links none") +set(CPACK_RPM_COMPRESSION_TYPE "lzma") + +set(CPACK_PACKAGE_RELEASE 1) +set(CPACK_PACKAGE_CHECKSUM "MD5") + +set(CPACK_PACKAGE_FILE_NAME ${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}_${DEMO_BUILD_NUMBER}-${CPACK_PACKAGE_RELEASE}.${CMAKE_SYSTEM_PROCESSOR}) +set(CPACK_SOURCE_PACKAGE_FILE_NAME ${CPACK_PACKAGE_NAME}-${DEMO_VERSION_STRING}) + +set(CPACK_GENERATOR RPM) +set(CPACK_SOURCE_GENERATOR "TGZ") + +set(CPACK_SOURCE_IGNORE_FILES "/\\\\.gitattributes;/\\\\.vagrant;/\\\\.DS_Store;/CVS/;/\\\\.git/;\\\\.swp$;~$;\\\\.\\\\#;/\\\\#") +list(APPEND CPACK_SOURCE_IGNORE_FILES "/\\\\.clion.source.upload.marker") +list(APPEND CPACK_SOURCE_IGNORE_FILES "${CMAKE_CURRENT_BINARY_DIR}") + +include(CPack) diff --git a/cmake/sanitizers/.gitignore b/cmake/sanitizers/.gitignore new file mode 100644 index 0000000..c4a70a6 --- /dev/null +++ b/cmake/sanitizers/.gitignore @@ -0,0 +1,3 @@ +# out-of-source build top-level folders. +build/ +_build/ diff --git a/cmake/sanitizers/CMakeLists.txt b/cmake/sanitizers/CMakeLists.txt new file mode 100644 index 0000000..d249a4d --- /dev/null +++ b/cmake/sanitizers/CMakeLists.txt @@ -0,0 +1,51 @@ +# This file is part of CMake-sanitizers. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# +# +# Copyright (c) +# 2013-2015 Matt Arsenault +# 2015 RWTH Aachen University, Federal Republic of Germany +# + + +# +# project information +# + +# minimum required cmake version +cmake_minimum_required(VERSION 2.8) + +# project name +project("CMake-sanitizers") + + + +# +# cmake configuration +# +set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH}) + + + +# +# add tests +# +enable_testing() +add_subdirectory(tests) diff --git a/cmake/sanitizers/LICENSE b/cmake/sanitizers/LICENSE new file mode 100644 index 0000000..2520efd --- /dev/null +++ b/cmake/sanitizers/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) + 2013 Matthew Arsenault + 2015-2016 RWTH Aachen University, Federal Republic of Germany + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/cmake/sanitizers/README.md b/cmake/sanitizers/README.md new file mode 100644 index 0000000..8df8d17 --- /dev/null +++ b/cmake/sanitizers/README.md @@ -0,0 +1,73 @@ +# sanitizers-cmake + + [![](https://img.shields.io/github/issues-raw/arsenm/sanitizers-cmake.svg?style=flat-square)](https://github.com/arsenm/sanitizers-cmake/issues) +[![MIT](http://img.shields.io/badge/license-MIT-blue.svg?style=flat-square)](LICENSE) + +CMake module to enable sanitizers for binary targets. + + +## Include into your project + +To use [FindSanitizers.cmake](cmake/FindSanitizers.cmake), simply add this repository as git submodule into your own repository +```Shell +mkdir externals +git submodule add git://github.com/arsenm/sanitizers-cmake.git externals/sanitizers-cmake +``` +and adding ```externals/sanitizers-cmake/cmake``` to your ```CMAKE_MODULE_PATH``` +```CMake +set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/externals/sanitizers-cmake/cmake" ${CMAKE_MODULE_PATH}) +``` + +If you don't use git or dislike submodules you can copy the files in [cmake directory](cmake) into your repository. *Be careful and keep updates in mind!* + +Now you can simply run ```find_package``` in your CMake files: +```CMake +find_package(Sanitizers) +``` + + +## Usage + +You can enable the sanitizers with ``SANITIZE_ADDRESS``, ``SANITIZE_MEMORY``, ``SANITIZE_THREAD`` or ``SANITIZE_UNDEFINED`` options in your CMake configuration. You can do this by passing e.g. ``-DSANITIZE_ADDRESS=On`` on your command line or with your graphical interface. + +If sanitizers are supported by your compiler, the specified targets will be build with sanitizer support. If your compiler has no sanitizing capabilities (I asume intel compiler doesn't) you'll get a warning but CMake will continue processing and sanitizing will simply just be ignored. + +#### Compiler issues + +Different compilers may be using different implementations for sanitizers. If you'll try to sanitize targets with C and Fortran code but don't use gcc & gfortran but clang & gfortran, this will cause linking problems. To avoid this, such problems will be detected and sanitizing will be disabled for these targets. + +Even C only targets may cause problems in certain situations. Some problems have been seen with AddressSanitizer for preloading or dynamic linking. In such cases you may try the ``SANITIZE_LINK_STATIC`` to link sanitizers for gcc static. + + + +## Build targets with sanitizer support + +To enable sanitizer support you simply have to add ``add_sanitizers()`` after defining your target. To provide a sanitizer blacklist file you can use the ``sanitizer_add_blacklist_file()`` function: +```CMake +find_package(Sanitizers) + +sanitizer_add_blacklist_file("blacklist.txt") + +add_executable(some_exe foo.c bar.c) +add_sanitizers(some_exe) + +add_library(some_lib foo.c bar.c) +add_sanitizers(some_lib) +``` + +## Run your application + +The sanitizers check your program, while it's running. In some situations (e.g. LD_PRELOAD your target) it might be required to preload the used AddressSanitizer library first. In this case you may use the ``asan-wrapper`` script defined in ``ASan_WRAPPER`` variable to execute your application with ``${ASan_WRAPPER} myexe arg1 ...``. + + +## Contribute + +Anyone is welcome to contribute. Simply fork this repository, make your changes **in an own branch** and create a pull-request for your change. Please do only one change per pull-request. + +You found a bug? Please fill out an [issue](https://github.com/arsenm/sanitizers-cmake/issues) and include any data to reproduce the bug. + + +#### Contributors + +* [Matt Arsenault](https://github.com/arsenm) +* [Alexander Haase](https://github.com/alehaa) diff --git a/cmake/sanitizers/cmake/FindASan.cmake b/cmake/sanitizers/cmake/FindASan.cmake new file mode 100644 index 0000000..98ea7cb --- /dev/null +++ b/cmake/sanitizers/cmake/FindASan.cmake @@ -0,0 +1,59 @@ +# The MIT License (MIT) +# +# Copyright (c) +# 2013 Matthew Arsenault +# 2015-2016 RWTH Aachen University, Federal Republic of Germany +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +option(SANITIZE_ADDRESS "Enable AddressSanitizer for sanitized targets." Off) + +set(FLAG_CANDIDATES + # Clang 3.2+ use this version. The no-omit-frame-pointer option is optional. + "-g -fsanitize=address -fno-omit-frame-pointer" + "-g -fsanitize=address" + + # Older deprecated flag for ASan + "-g -faddress-sanitizer" +) + + +if (SANITIZE_ADDRESS AND (SANITIZE_THREAD OR SANITIZE_MEMORY)) + message(FATAL_ERROR "AddressSanitizer is not compatible with " + "ThreadSanitizer or MemorySanitizer.") +endif () + + +include(sanitize-helpers) + +if (SANITIZE_ADDRESS) + sanitizer_check_compiler_flags("${FLAG_CANDIDATES}" "AddressSanitizer" + "ASan") + + find_program(ASan_WRAPPER "asan-wrapper" PATHS ${CMAKE_MODULE_PATH}) + mark_as_advanced(ASan_WRAPPER) +endif () + +function (add_sanitize_address TARGET) + if (NOT SANITIZE_ADDRESS) + return() + endif () + + sanitizer_add_flags(${TARGET} "AddressSanitizer" "ASan") +endfunction () diff --git a/cmake/sanitizers/cmake/FindMSan.cmake b/cmake/sanitizers/cmake/FindMSan.cmake new file mode 100644 index 0000000..22d0050 --- /dev/null +++ b/cmake/sanitizers/cmake/FindMSan.cmake @@ -0,0 +1,57 @@ +# The MIT License (MIT) +# +# Copyright (c) +# 2013 Matthew Arsenault +# 2015-2016 RWTH Aachen University, Federal Republic of Germany +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +option(SANITIZE_MEMORY "Enable MemorySanitizer for sanitized targets." Off) + +set(FLAG_CANDIDATES + "-g -fsanitize=memory" +) + + +include(sanitize-helpers) + +if (SANITIZE_MEMORY) + if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Linux") + message(WARNING "MemorySanitizer disabled for target ${TARGET} because " + "MemorySanitizer is supported for Linux systems only.") + set(SANITIZE_MEMORY Off CACHE BOOL + "Enable MemorySanitizer for sanitized targets." FORCE) + elseif (NOT ${CMAKE_SIZEOF_VOID_P} EQUAL 8) + message(WARNING "MemorySanitizer disabled for target ${TARGET} because " + "MemorySanitizer is supported for 64bit systems only.") + set(SANITIZE_MEMORY Off CACHE BOOL + "Enable MemorySanitizer for sanitized targets." FORCE) + else () + sanitizer_check_compiler_flags("${FLAG_CANDIDATES}" "MemorySanitizer" + "MSan") + endif () +endif () + +function (add_sanitize_memory TARGET) + if (NOT SANITIZE_MEMORY) + return() + endif () + + sanitizer_add_flags(${TARGET} "MemorySanitizer" "MSan") +endfunction () diff --git a/cmake/sanitizers/cmake/FindSanitizers.cmake b/cmake/sanitizers/cmake/FindSanitizers.cmake new file mode 100755 index 0000000..101bab8 --- /dev/null +++ b/cmake/sanitizers/cmake/FindSanitizers.cmake @@ -0,0 +1,94 @@ +# The MIT License (MIT) +# +# Copyright (c) +# 2013 Matthew Arsenault +# 2015-2016 RWTH Aachen University, Federal Republic of Germany +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +# If any of the used compiler is a GNU compiler, add a second option to static +# link against the sanitizers. +option(SANITIZE_LINK_STATIC "Try to link static against sanitizers." Off) + + + + +set(FIND_QUIETLY_FLAG "") +if (DEFINED Sanitizers_FIND_QUIETLY) + set(FIND_QUIETLY_FLAG "QUIET") +endif () + +find_package(ASan ${FIND_QUIETLY_FLAG}) +find_package(TSan ${FIND_QUIETLY_FLAG}) +find_package(MSan ${FIND_QUIETLY_FLAG}) +find_package(UBSan ${FIND_QUIETLY_FLAG}) + + + + +function(sanitizer_add_blacklist_file FILE) + if(NOT IS_ABSOLUTE ${FILE}) + set(FILE "${CMAKE_CURRENT_SOURCE_DIR}/${FILE}") + endif() + get_filename_component(FILE "${FILE}" REALPATH) + + sanitizer_check_compiler_flags("-fsanitize-blacklist=${FILE}" + "SanitizerBlacklist" "SanBlist") +endfunction() + +function(add_sanitizers ...) + # If no sanitizer is enabled, return immediately. + if (NOT (SANITIZE_ADDRESS OR SANITIZE_MEMORY OR SANITIZE_THREAD OR + SANITIZE_UNDEFINED)) + return() + endif () + + foreach (TARGET ${ARGV}) + # Check if this target will be compiled by exactly one compiler. Other- + # wise sanitizers can't be used and a warning should be printed once. + get_target_property(TARGET_TYPE ${TARGET} TYPE) + if (TARGET_TYPE STREQUAL "INTERFACE_LIBRARY") + message(WARNING "Can't use any sanitizers for target ${TARGET}, " + "because it is an interface library and cannot be " + "compiled directly.") + return() + endif () + sanitizer_target_compilers(${TARGET} TARGET_COMPILER) + list(LENGTH TARGET_COMPILER NUM_COMPILERS) + if (NUM_COMPILERS GREATER 1) + message(WARNING "Can't use any sanitizers for target ${TARGET}, " + "because it will be compiled by incompatible compilers. " + "Target will be compiled without sanitizers.") + return() + + # If the target is compiled by no or no known compiler, give a warning. + elseif (NUM_COMPILERS EQUAL 0) + message(WARNING "Sanitizers for target ${TARGET} may not be" + " usable, because it uses no or an unknown compiler. " + "This is a false warning for targets using only " + "object lib(s) as input.") + endif () + + # Add sanitizers for target. + add_sanitize_address(${TARGET}) + add_sanitize_thread(${TARGET}) + add_sanitize_memory(${TARGET}) + add_sanitize_undefined(${TARGET}) + endforeach () +endfunction(add_sanitizers) diff --git a/cmake/sanitizers/cmake/FindTSan.cmake b/cmake/sanitizers/cmake/FindTSan.cmake new file mode 100644 index 0000000..3cba3c0 --- /dev/null +++ b/cmake/sanitizers/cmake/FindTSan.cmake @@ -0,0 +1,65 @@ +# The MIT License (MIT) +# +# Copyright (c) +# 2013 Matthew Arsenault +# 2015-2016 RWTH Aachen University, Federal Republic of Germany +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +option(SANITIZE_THREAD "Enable ThreadSanitizer for sanitized targets." Off) + +set(FLAG_CANDIDATES + "-g -fsanitize=thread" +) + + +# ThreadSanitizer is not compatible with MemorySanitizer. +if (SANITIZE_THREAD AND SANITIZE_MEMORY) + message(FATAL_ERROR "ThreadSanitizer is not compatible with " + "MemorySanitizer.") +endif () + + +include(sanitize-helpers) + +if (SANITIZE_THREAD) + if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Linux" AND + NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Darwin") + message(WARNING "ThreadSanitizer disabled for target ${TARGET} because " + "ThreadSanitizer is supported for Linux systems and macOS only.") + set(SANITIZE_THREAD Off CACHE BOOL + "Enable ThreadSanitizer for sanitized targets." FORCE) + elseif (NOT ${CMAKE_SIZEOF_VOID_P} EQUAL 8) + message(WARNING "ThreadSanitizer disabled for target ${TARGET} because " + "ThreadSanitizer is supported for 64bit systems only.") + set(SANITIZE_THREAD Off CACHE BOOL + "Enable ThreadSanitizer for sanitized targets." FORCE) + else () + sanitizer_check_compiler_flags("${FLAG_CANDIDATES}" "ThreadSanitizer" + "TSan") + endif () +endif () + +function (add_sanitize_thread TARGET) + if (NOT SANITIZE_THREAD) + return() + endif () + + sanitizer_add_flags(${TARGET} "ThreadSanitizer" "TSan") +endfunction () diff --git a/cmake/sanitizers/cmake/FindUBSan.cmake b/cmake/sanitizers/cmake/FindUBSan.cmake new file mode 100644 index 0000000..ae103f7 --- /dev/null +++ b/cmake/sanitizers/cmake/FindUBSan.cmake @@ -0,0 +1,46 @@ +# The MIT License (MIT) +# +# Copyright (c) +# 2013 Matthew Arsenault +# 2015-2016 RWTH Aachen University, Federal Republic of Germany +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +option(SANITIZE_UNDEFINED + "Enable UndefinedBehaviorSanitizer for sanitized targets." Off) + +set(FLAG_CANDIDATES + "-g -fsanitize=undefined" +) + + +include(sanitize-helpers) + +if (SANITIZE_UNDEFINED) + sanitizer_check_compiler_flags("${FLAG_CANDIDATES}" + "UndefinedBehaviorSanitizer" "UBSan") +endif () + +function (add_sanitize_undefined TARGET) + if (NOT SANITIZE_UNDEFINED) + return() + endif () + + sanitizer_add_flags(${TARGET} "UndefinedBehaviorSanitizer" "UBSan") +endfunction () diff --git a/cmake/sanitizers/cmake/asan-wrapper b/cmake/sanitizers/cmake/asan-wrapper new file mode 100755 index 0000000..5d54103 --- /dev/null +++ b/cmake/sanitizers/cmake/asan-wrapper @@ -0,0 +1,55 @@ +#!/bin/sh + +# The MIT License (MIT) +# +# Copyright (c) +# 2013 Matthew Arsenault +# 2015-2016 RWTH Aachen University, Federal Republic of Germany +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +# This script is a wrapper for AddressSanitizer. In some special cases you need +# to preload AddressSanitizer to avoid error messages - e.g. if you're +# preloading another library to your application. At the moment this script will +# only do something, if we're running on a Linux platform. OSX might not be +# affected. + + +# Exit immediately, if platform is not Linux. +if [ "$(uname)" != "Linux" ] +then + exec $@ +fi + + +# Get the used libasan of the application ($1). If a libasan was found, it will +# be prepended to LD_PRELOAD. +libasan=$(ldd $1 | grep libasan | sed "s/^[[:space:]]//" | cut -d' ' -f1) +if [ -n "$libasan" ] +then + if [ -n "$LD_PRELOAD" ] + then + export LD_PRELOAD="$libasan:$LD_PRELOAD" + else + export LD_PRELOAD="$libasan" + fi +fi + +# Execute the application. +exec $@ diff --git a/cmake/sanitizers/cmake/sanitize-helpers.cmake b/cmake/sanitizers/cmake/sanitize-helpers.cmake new file mode 100755 index 0000000..d3642af --- /dev/null +++ b/cmake/sanitizers/cmake/sanitize-helpers.cmake @@ -0,0 +1,178 @@ +# The MIT License (MIT) +# +# Copyright (c) +# 2013 Matthew Arsenault +# 2015-2016 RWTH Aachen University, Federal Republic of Germany +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +# Helper function to get the language of a source file. +function (sanitizer_lang_of_source FILE RETURN_VAR) + get_filename_component(LONGEST_EXT "${FILE}" EXT) + # If extension is empty return. This can happen for extensionless headers + if("${LONGEST_EXT}" STREQUAL "") + set(${RETURN_VAR} "" PARENT_SCOPE) + return() + endif() + # Get shortest extension as some files can have dot in their names + string(REGEX REPLACE "^.*(\\.[^.]+)$" "\\1" FILE_EXT ${LONGEST_EXT}) + string(TOLOWER "${FILE_EXT}" FILE_EXT) + string(SUBSTRING "${FILE_EXT}" 1 -1 FILE_EXT) + + get_property(ENABLED_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES) + foreach (LANG ${ENABLED_LANGUAGES}) + list(FIND CMAKE_${LANG}_SOURCE_FILE_EXTENSIONS "${FILE_EXT}" TEMP) + if (NOT ${TEMP} EQUAL -1) + set(${RETURN_VAR} "${LANG}" PARENT_SCOPE) + return() + endif () + endforeach() + + set(${RETURN_VAR} "" PARENT_SCOPE) +endfunction () + + +# Helper function to get compilers used by a target. +function (sanitizer_target_compilers TARGET RETURN_VAR) + # Check if all sources for target use the same compiler. If a target uses + # e.g. C and Fortran mixed and uses different compilers (e.g. clang and + # gfortran) this can trigger huge problems, because different compilers may + # use different implementations for sanitizers. + set(BUFFER "") + get_target_property(TSOURCES ${TARGET} SOURCES) + foreach (FILE ${TSOURCES}) + # If expression was found, FILE is a generator-expression for an object + # library. Object libraries will be ignored. + string(REGEX MATCH "TARGET_OBJECTS:([^ >]+)" _file ${FILE}) + if ("${_file}" STREQUAL "") + sanitizer_lang_of_source(${FILE} LANG) + if (LANG) + list(APPEND BUFFER ${CMAKE_${LANG}_COMPILER_ID}) + endif () + endif () + endforeach () + + list(REMOVE_DUPLICATES BUFFER) + set(${RETURN_VAR} "${BUFFER}" PARENT_SCOPE) +endfunction () + + +# Helper function to check compiler flags for language compiler. +function (sanitizer_check_compiler_flag FLAG LANG VARIABLE) + if (${LANG} STREQUAL "C") + include(CheckCCompilerFlag) + check_c_compiler_flag("${FLAG}" ${VARIABLE}) + + elseif (${LANG} STREQUAL "CXX") + include(CheckCXXCompilerFlag) + check_cxx_compiler_flag("${FLAG}" ${VARIABLE}) + + elseif (${LANG} STREQUAL "Fortran") + # CheckFortranCompilerFlag was introduced in CMake 3.x. To be compatible + # with older Cmake versions, we will check if this module is present + # before we use it. Otherwise we will define Fortran coverage support as + # not available. + include(CheckFortranCompilerFlag OPTIONAL RESULT_VARIABLE INCLUDED) + if (INCLUDED) + check_fortran_compiler_flag("${FLAG}" ${VARIABLE}) + elseif (NOT CMAKE_REQUIRED_QUIET) + message(STATUS "Performing Test ${VARIABLE}") + message(STATUS "Performing Test ${VARIABLE}" + " - Failed (Check not supported)") + endif () + endif() +endfunction () + + +# Helper function to test compiler flags. +function (sanitizer_check_compiler_flags FLAG_CANDIDATES NAME PREFIX) + set(CMAKE_REQUIRED_QUIET ${${PREFIX}_FIND_QUIETLY}) + + get_property(ENABLED_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES) + foreach (LANG ${ENABLED_LANGUAGES}) + # Sanitizer flags are not dependend on language, but the used compiler. + # So instead of searching flags foreach language, search flags foreach + # compiler used. + set(COMPILER ${CMAKE_${LANG}_COMPILER_ID}) + if (NOT DEFINED ${PREFIX}_${COMPILER}_FLAGS) + foreach (FLAG ${FLAG_CANDIDATES}) + if(NOT CMAKE_REQUIRED_QUIET) + message(STATUS "Try ${COMPILER} ${NAME} flag = [${FLAG}]") + endif() + + set(CMAKE_REQUIRED_FLAGS "${FLAG}") + unset(${PREFIX}_FLAG_DETECTED CACHE) + sanitizer_check_compiler_flag("${FLAG}" ${LANG} + ${PREFIX}_FLAG_DETECTED) + + if (${PREFIX}_FLAG_DETECTED) + # If compiler is a GNU compiler, search for static flag, if + # SANITIZE_LINK_STATIC is enabled. + if (SANITIZE_LINK_STATIC AND (${COMPILER} STREQUAL "GNU")) + string(TOLOWER ${PREFIX} PREFIX_lower) + sanitizer_check_compiler_flag( + "-static-lib${PREFIX_lower}" ${LANG} + ${PREFIX}_STATIC_FLAG_DETECTED) + + if (${PREFIX}_STATIC_FLAG_DETECTED) + set(FLAG "-static-lib${PREFIX_lower} ${FLAG}") + endif () + endif () + + set(${PREFIX}_${COMPILER}_FLAGS "${FLAG}" CACHE STRING + "${NAME} flags for ${COMPILER} compiler.") + mark_as_advanced(${PREFIX}_${COMPILER}_FLAGS) + break() + endif () + endforeach () + + if (NOT ${PREFIX}_FLAG_DETECTED AND COMPILER) + set(${PREFIX}_${COMPILER}_FLAGS "" CACHE STRING + "${NAME} flags for ${COMPILER} compiler.") + mark_as_advanced(${PREFIX}_${COMPILER}_FLAGS) + + message(WARNING "${NAME} is not available for ${COMPILER} " + "compiler. Targets using this compiler will be " + "compiled without ${NAME}.") + endif () + endif () + endforeach () +endfunction () + + +# Helper to assign sanitizer flags for TARGET. +function (sanitizer_add_flags TARGET NAME PREFIX) + # Get list of compilers used by target and check, if sanitizer is available + # for this target. Other compiler checks like check for conflicting + # compilers will be done in add_sanitizers function. + sanitizer_target_compilers(${TARGET} TARGET_COMPILER) + list(LENGTH TARGET_COMPILER NUM_COMPILERS) + if ("${${PREFIX}_${TARGET_COMPILER}_FLAGS}" STREQUAL "") + message(FATAL_ERROR "No compiler flags available for sanitizer ${PREFIX}") + return() + endif() + + # Set compile- and link-flags for target. + set_property(TARGET ${TARGET} APPEND_STRING + PROPERTY COMPILE_FLAGS " ${${PREFIX}_${TARGET_COMPILER}_FLAGS}") + set_property(TARGET ${TARGET} APPEND_STRING + PROPERTY COMPILE_FLAGS " ${SanBlist_${TARGET_COMPILER}_FLAGS}") + set_property(TARGET ${TARGET} APPEND_STRING + PROPERTY LINK_FLAGS " ${${PREFIX}_${TARGET_COMPILER}_FLAGS}") +endfunction () diff --git a/cmake/sanitizers/tests/CMakeLists.txt b/cmake/sanitizers/tests/CMakeLists.txt new file mode 100644 index 0000000..8553e63 --- /dev/null +++ b/cmake/sanitizers/tests/CMakeLists.txt @@ -0,0 +1,67 @@ +# This file is part of CMake-sanitizers. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# +# +# Copyright (c) +# 2013-2015 Matt Arsenault +# 2015 RWTH Aachen University, Federal Republic of Germany +# + +# Function to add testcases. +function(add_testcase TESTNAME SOURCEFILES) + # remove ${TESTNAME} from ${ARGV} to use ${ARGV} as ${SOURCEFILES} + list(REMOVE_AT ARGV 0) + + # add a new executable + add_executable(${TESTNAME} ${ARGV}) + + # add a testcase for executable + add_test(${TESTNAME} ${TESTNAME}) +endfunction(add_testcase) + +# Function to add testcases with asan enabled. +function(add_sanitized_testcase TESTNAME SOURCEFILES) + add_testcase(${TESTNAME} ${SOURCEFILES}) + add_sanitizers(${TESTNAME}) +endfunction(add_sanitized_testcase) + + + +set(SANITIZE_ADDRESS TRUE) + +# +# search for sanitizers +# +find_package(Sanitizers) + + +# +# add testcases +# +add_sanitized_testcase("asan_test_cpp" asan_test.cpp) +add_sanitized_testcase("shortest_ext_test_cpp" shortest.ext.test.cpp) + +set_tests_properties( + "asan_test_cpp" + "shortest_ext_test_cpp" +PROPERTIES + WILL_FAIL TRUE +) + diff --git a/cmake/sanitizers/tests/asan_test.cpp b/cmake/sanitizers/tests/asan_test.cpp new file mode 100644 index 0000000..6c0a370 --- /dev/null +++ b/cmake/sanitizers/tests/asan_test.cpp @@ -0,0 +1,40 @@ +/* This file is part of CMake-sanitizers. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * Copyright (c) + * 2013-2015 Matt Arsenault + * 2015 RWTH Aachen University, Federal Republic of Germany + */ + + +int +main(int argc, char **argv) +{ + // Allocate a new array and delete it. + int *array = new int[argc + 1]; + array[argc] = 0; + delete[] array; + + /* Access element of the deleted array. This will cause an memory error with + * address sanitizer. + */ + return array[argc]; +} diff --git a/cmake/sanitizers/tests/shortest.ext.test.cpp b/cmake/sanitizers/tests/shortest.ext.test.cpp new file mode 100644 index 0000000..6c0a370 --- /dev/null +++ b/cmake/sanitizers/tests/shortest.ext.test.cpp @@ -0,0 +1,40 @@ +/* This file is part of CMake-sanitizers. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * + * Copyright (c) + * 2013-2015 Matt Arsenault + * 2015 RWTH Aachen University, Federal Republic of Germany + */ + + +int +main(int argc, char **argv) +{ + // Allocate a new array and delete it. + int *array = new int[argc + 1]; + array[argc] = 0; + delete[] array; + + /* Access element of the deleted array. This will cause an memory error with + * address sanitizer. + */ + return array[argc]; +} diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..eb26552 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,14 @@ +services: + demo: + image: your-dockerhub-user/your-image-name:latest + build: . + command: /usr/sbin/init + hostname: demo + restart: always + privileged: true + tty: true + ports: + - "2022:22" + volumes: + - ~/.ssh/authorized_keys:/root/.ssh/authorized_keys:ro + - .:/root/workspace/demo diff --git a/docs/05-examples.md b/docs/05-examples.md new file mode 100644 index 0000000..3d71acf --- /dev/null +++ b/docs/05-examples.md @@ -0,0 +1,3 @@ +# More Examples + +## xxx \ No newline at end of file diff --git a/docs/06-design.md b/docs/06-design.md new file mode 100644 index 0000000..9727acc --- /dev/null +++ b/docs/06-design.md @@ -0,0 +1,7 @@ +# Design + +[TOC] + +## Motivation + +## Background diff --git a/docs/07-reference.md b/docs/07-reference.md new file mode 100644 index 0000000..39613a8 --- /dev/null +++ b/docs/07-reference.md @@ -0,0 +1,11 @@ +# Reference + +## Core Features + +| Classes | Free Functions | Type Traits | +|-------------------------|----------------|------------------------| +| demo::executor | $250 | demo::awaitable_traits | +| demo::execution_context | $80 | | +| March | $420 | | + + diff --git a/docs/theme/DoxygenLayout.xml b/docs/theme/DoxygenLayout.xml new file mode 100644 index 0000000..8ac0b73 --- /dev/null +++ b/docs/theme/DoxygenLayout.xml @@ -0,0 +1,233 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/theme/doxygen-awesome-darkmode-toggle.js b/docs/theme/doxygen-awesome-darkmode-toggle.js new file mode 100644 index 0000000..40fe2d3 --- /dev/null +++ b/docs/theme/doxygen-awesome-darkmode-toggle.js @@ -0,0 +1,157 @@ +/** + +Doxygen Awesome +https://github.com/jothepro/doxygen-awesome-css + +MIT License + +Copyright (c) 2021 - 2023 jothepro + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +class DoxygenAwesomeDarkModeToggle extends HTMLElement { + // SVG icons from https://fonts.google.com/icons + // Licensed under the Apache 2.0 license: + // https://www.apache.org/licenses/LICENSE-2.0.html + static lightModeIcon = `` + static darkModeIcon = `` + static title = "Toggle Light/Dark Mode" + + static prefersLightModeInDarkModeKey = "prefers-light-mode-in-dark-mode" + static prefersDarkModeInLightModeKey = "prefers-dark-mode-in-light-mode" + + static _staticConstructor = function() { + DoxygenAwesomeDarkModeToggle.enableDarkMode(DoxygenAwesomeDarkModeToggle.userPreference) + // Update the color scheme when the browsers preference changes + // without user interaction on the website. + window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', event => { + DoxygenAwesomeDarkModeToggle.onSystemPreferenceChanged() + }) + // Update the color scheme when the tab is made visible again. + // It is possible that the appearance was changed in another tab + // while this tab was in the background. + document.addEventListener("visibilitychange", visibilityState => { + if (document.visibilityState === 'visible') { + DoxygenAwesomeDarkModeToggle.onSystemPreferenceChanged() + } + }); + }() + + static init() { + $(function() { + $(document).ready(function() { + const toggleButton = document.createElement('doxygen-awesome-dark-mode-toggle') + toggleButton.title = DoxygenAwesomeDarkModeToggle.title + toggleButton.updateIcon() + + window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', event => { + toggleButton.updateIcon() + }) + document.addEventListener("visibilitychange", visibilityState => { + if (document.visibilityState === 'visible') { + toggleButton.updateIcon() + } + }); + + $(document).ready(function(){ + document.getElementById("MSearchBox").parentNode.appendChild(toggleButton) + }) + $(window).resize(function(){ + document.getElementById("MSearchBox").parentNode.appendChild(toggleButton) + }) + }) + }) + } + + constructor() { + super(); + this.onclick=this.toggleDarkMode + } + + /** + * @returns `true` for dark-mode, `false` for light-mode system preference + */ + static get systemPreference() { + return window.matchMedia('(prefers-color-scheme: dark)').matches + } + + /** + * @returns `true` for dark-mode, `false` for light-mode user preference + */ + static get userPreference() { + return (!DoxygenAwesomeDarkModeToggle.systemPreference && localStorage.getItem(DoxygenAwesomeDarkModeToggle.prefersDarkModeInLightModeKey)) || + (DoxygenAwesomeDarkModeToggle.systemPreference && !localStorage.getItem(DoxygenAwesomeDarkModeToggle.prefersLightModeInDarkModeKey)) + } + + static set userPreference(userPreference) { + DoxygenAwesomeDarkModeToggle.darkModeEnabled = userPreference + if(!userPreference) { + if(DoxygenAwesomeDarkModeToggle.systemPreference) { + localStorage.setItem(DoxygenAwesomeDarkModeToggle.prefersLightModeInDarkModeKey, true) + } else { + localStorage.removeItem(DoxygenAwesomeDarkModeToggle.prefersDarkModeInLightModeKey) + } + } else { + if(!DoxygenAwesomeDarkModeToggle.systemPreference) { + localStorage.setItem(DoxygenAwesomeDarkModeToggle.prefersDarkModeInLightModeKey, true) + } else { + localStorage.removeItem(DoxygenAwesomeDarkModeToggle.prefersLightModeInDarkModeKey) + } + } + DoxygenAwesomeDarkModeToggle.onUserPreferenceChanged() + } + + static enableDarkMode(enable) { + if(enable) { + DoxygenAwesomeDarkModeToggle.darkModeEnabled = true + document.documentElement.classList.add("dark-mode") + document.documentElement.classList.remove("light-mode") + } else { + DoxygenAwesomeDarkModeToggle.darkModeEnabled = false + document.documentElement.classList.remove("dark-mode") + document.documentElement.classList.add("light-mode") + } + } + + static onSystemPreferenceChanged() { + DoxygenAwesomeDarkModeToggle.darkModeEnabled = DoxygenAwesomeDarkModeToggle.userPreference + DoxygenAwesomeDarkModeToggle.enableDarkMode(DoxygenAwesomeDarkModeToggle.darkModeEnabled) + } + + static onUserPreferenceChanged() { + DoxygenAwesomeDarkModeToggle.enableDarkMode(DoxygenAwesomeDarkModeToggle.darkModeEnabled) + } + + toggleDarkMode() { + DoxygenAwesomeDarkModeToggle.userPreference = !DoxygenAwesomeDarkModeToggle.userPreference + this.updateIcon() + } + + updateIcon() { + if(DoxygenAwesomeDarkModeToggle.darkModeEnabled) { + this.innerHTML = DoxygenAwesomeDarkModeToggle.darkModeIcon + } else { + this.innerHTML = DoxygenAwesomeDarkModeToggle.lightModeIcon + } + } +} + +customElements.define("doxygen-awesome-dark-mode-toggle", DoxygenAwesomeDarkModeToggle); diff --git a/docs/theme/doxygen-awesome-fragment-copy-button.js b/docs/theme/doxygen-awesome-fragment-copy-button.js new file mode 100644 index 0000000..86c16fd --- /dev/null +++ b/docs/theme/doxygen-awesome-fragment-copy-button.js @@ -0,0 +1,85 @@ +/** + +Doxygen Awesome +https://github.com/jothepro/doxygen-awesome-css + +MIT License + +Copyright (c) 2022 - 2023 jothepro + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +class DoxygenAwesomeFragmentCopyButton extends HTMLElement { + constructor() { + super(); + this.onclick=this.copyContent + } + static title = "Copy to clipboard" + static copyIcon = `` + static successIcon = `` + static successDuration = 980 + static init() { + $(function() { + $(document).ready(function() { + if(navigator.clipboard) { + const fragments = document.getElementsByClassName("fragment") + for(const fragment of fragments) { + const fragmentWrapper = document.createElement("div") + fragmentWrapper.className = "doxygen-awesome-fragment-wrapper" + const fragmentCopyButton = document.createElement("doxygen-awesome-fragment-copy-button") + fragmentCopyButton.innerHTML = DoxygenAwesomeFragmentCopyButton.copyIcon + fragmentCopyButton.title = DoxygenAwesomeFragmentCopyButton.title + + fragment.parentNode.replaceChild(fragmentWrapper, fragment) + fragmentWrapper.appendChild(fragment) + fragmentWrapper.appendChild(fragmentCopyButton) + + } + } + }) + }) + } + + + copyContent() { + const content = this.previousSibling.cloneNode(true) + // filter out line number from file listings + content.querySelectorAll(".lineno, .ttc").forEach((node) => { + node.remove() + }) + let textContent = content.textContent + // remove trailing newlines that appear in file listings + let numberOfTrailingNewlines = 0 + while(textContent.charAt(textContent.length - (numberOfTrailingNewlines + 1)) == '\n') { + numberOfTrailingNewlines++; + } + textContent = textContent.substring(0, textContent.length - numberOfTrailingNewlines) + navigator.clipboard.writeText(textContent); + this.classList.add("success") + this.innerHTML = DoxygenAwesomeFragmentCopyButton.successIcon + window.setTimeout(() => { + this.classList.remove("success") + this.innerHTML = DoxygenAwesomeFragmentCopyButton.copyIcon + }, DoxygenAwesomeFragmentCopyButton.successDuration); + } +} + +customElements.define("doxygen-awesome-fragment-copy-button", DoxygenAwesomeFragmentCopyButton) diff --git a/docs/theme/doxygen-awesome-interactive-toc.js b/docs/theme/doxygen-awesome-interactive-toc.js new file mode 100644 index 0000000..20a9669 --- /dev/null +++ b/docs/theme/doxygen-awesome-interactive-toc.js @@ -0,0 +1,81 @@ +/** + +Doxygen Awesome +https://github.com/jothepro/doxygen-awesome-css + +MIT License + +Copyright (c) 2022 - 2023 jothepro + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +class DoxygenAwesomeInteractiveToc { + static topOffset = 38 + static hideMobileMenu = true + static headers = [] + + static init() { + window.addEventListener("load", () => { + let toc = document.querySelector(".contents > .toc") + if(toc) { + toc.classList.add("interactive") + if(!DoxygenAwesomeInteractiveToc.hideMobileMenu) { + toc.classList.add("open") + } + document.querySelector(".contents > .toc > h3")?.addEventListener("click", () => { + if(toc.classList.contains("open")) { + toc.classList.remove("open") + } else { + toc.classList.add("open") + } + }) + + document.querySelectorAll(".contents > .toc > ul a").forEach((node) => { + let id = node.getAttribute("href").substring(1) + DoxygenAwesomeInteractiveToc.headers.push({ + node: node, + headerNode: document.getElementById(id) + }) + + document.getElementById("doc-content")?.addEventListener("scroll", () => { + DoxygenAwesomeInteractiveToc.update() + }) + }) + DoxygenAwesomeInteractiveToc.update() + } + }) + } + + static update() { + let active = DoxygenAwesomeInteractiveToc.headers[0]?.node + DoxygenAwesomeInteractiveToc.headers.forEach((header) => { + let position = header.headerNode.getBoundingClientRect().top + header.node.classList.remove("active") + header.node.classList.remove("aboveActive") + if(position < DoxygenAwesomeInteractiveToc.topOffset) { + active = header.node + active?.classList.add("aboveActive") + } + }) + active?.classList.add("active") + active?.classList.remove("aboveActive") + } +} \ No newline at end of file diff --git a/docs/theme/doxygen-awesome-paragraph-link.js b/docs/theme/doxygen-awesome-paragraph-link.js new file mode 100644 index 0000000..e53d132 --- /dev/null +++ b/docs/theme/doxygen-awesome-paragraph-link.js @@ -0,0 +1,51 @@ +/** + +Doxygen Awesome +https://github.com/jothepro/doxygen-awesome-css + +MIT License + +Copyright (c) 2022 - 2023 jothepro + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +class DoxygenAwesomeParagraphLink { + // Icon from https://fonts.google.com/icons + // Licensed under the Apache 2.0 license: + // https://www.apache.org/licenses/LICENSE-2.0.html + static icon = `` + static title = "Permanent Link" + static init() { + $(function() { + $(document).ready(function() { + document.querySelectorAll(".contents a.anchor[id], .contents .groupheader > a[id]").forEach((node) => { + let anchorlink = document.createElement("a") + anchorlink.setAttribute("href", `#${node.getAttribute("id")}`) + anchorlink.setAttribute("title", DoxygenAwesomeParagraphLink.title) + anchorlink.classList.add("anchorlink") + node.classList.add("anchor") + anchorlink.innerHTML = DoxygenAwesomeParagraphLink.icon + node.parentElement.appendChild(anchorlink) + }) + }) + }) + } +} diff --git a/docs/theme/doxygen-awesome-sidebar-only-darkmode-toggle.css b/docs/theme/doxygen-awesome-sidebar-only-darkmode-toggle.css new file mode 100644 index 0000000..d207446 --- /dev/null +++ b/docs/theme/doxygen-awesome-sidebar-only-darkmode-toggle.css @@ -0,0 +1,40 @@ + +/** + +Doxygen Awesome +https://github.com/jothepro/doxygen-awesome-css + +MIT License + +Copyright (c) 2021 - 2023 jothepro + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +@media screen and (min-width: 768px) { + + #MSearchBox { + width: calc(var(--side-nav-fixed-width) - calc(2 * var(--spacing-medium)) - var(--searchbar-height) - 1px); + } + + #MSearchField { + width: calc(var(--side-nav-fixed-width) - calc(2 * var(--spacing-medium)) - 66px - var(--searchbar-height)); + } +} diff --git a/docs/theme/doxygen-awesome-sidebar-only.css b/docs/theme/doxygen-awesome-sidebar-only.css new file mode 100644 index 0000000..853f6d6 --- /dev/null +++ b/docs/theme/doxygen-awesome-sidebar-only.css @@ -0,0 +1,116 @@ +/** + +Doxygen Awesome +https://github.com/jothepro/doxygen-awesome-css + +MIT License + +Copyright (c) 2021 - 2023 jothepro + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + */ + +html { + /* side nav width. MUST be = `TREEVIEW_WIDTH`. + * Make sure it is wide enough to contain the page title (logo + title + version) + */ + --side-nav-fixed-width: 335px; + --menu-display: none; + + --top-height: 120px; + --toc-sticky-top: -25px; + --toc-max-height: calc(100vh - 2 * var(--spacing-medium) - 25px); +} + +#projectname { + white-space: nowrap; +} + + +@media screen and (min-width: 768px) { + html { + --searchbar-background: var(--page-background-color); + } + + #side-nav { + min-width: var(--side-nav-fixed-width); + max-width: var(--side-nav-fixed-width); + top: var(--top-height); + overflow: visible; + } + + #nav-tree, #side-nav { + height: calc(100vh - var(--top-height)) !important; + } + + #nav-tree { + padding: 0; + } + + #top { + display: block; + border-bottom: none; + height: var(--top-height); + margin-bottom: calc(0px - var(--top-height)); + max-width: var(--side-nav-fixed-width); + overflow: hidden; + background: var(--side-nav-background); + } + #main-nav { + float: left; + padding-right: 0; + } + + .ui-resizable-handle { + cursor: default; + width: 1px !important; + background: var(--separator-color); + box-shadow: 0 calc(-2 * var(--top-height)) 0 0 var(--separator-color); + } + + #nav-path { + position: fixed; + right: 0; + left: var(--side-nav-fixed-width); + bottom: 0; + width: auto; + } + + #doc-content { + height: calc(100vh - 31px) !important; + padding-bottom: calc(3 * var(--spacing-large)); + padding-top: calc(var(--top-height) - 80px); + box-sizing: border-box; + margin-left: var(--side-nav-fixed-width) !important; + } + + #MSearchBox { + width: calc(var(--side-nav-fixed-width) - calc(2 * var(--spacing-medium))); + } + + #MSearchField { + width: calc(var(--side-nav-fixed-width) - calc(2 * var(--spacing-medium)) - 65px); + } + + #MSearchResultsWindow { + left: var(--spacing-medium) !important; + right: auto; + } +} diff --git a/docs/theme/doxygen-awesome-tabs.js b/docs/theme/doxygen-awesome-tabs.js new file mode 100644 index 0000000..8a7c3f1 --- /dev/null +++ b/docs/theme/doxygen-awesome-tabs.js @@ -0,0 +1,90 @@ +/** + +Doxygen Awesome +https://github.com/jothepro/doxygen-awesome-css + +MIT License + +Copyright (c) 2023 jothepro + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +class DoxygenAwesomeTabs { + + static init() { + window.addEventListener("load", () => { + document.querySelectorAll(".tabbed:not(:empty)").forEach((tabbed, tabbedIndex) => { + let tabLinkList = [] + tabbed.querySelectorAll("li").forEach((tab, tabIndex) => { + tab.id = "tab_" + tabbedIndex + "_" + tabIndex + let header = tab.querySelector(".tab-title") + let tabLink = document.createElement("button") + tabLink.classList.add("tab-button") + tabLink.appendChild(header) + header.title = header.textContent + tabLink.addEventListener("click", () => { + tabbed.querySelectorAll("li").forEach((tab) => { + tab.classList.remove("selected") + }) + tabLinkList.forEach((tabLink) => { + tabLink.classList.remove("active") + }) + tab.classList.add("selected") + tabLink.classList.add("active") + }) + tabLinkList.push(tabLink) + if(tabIndex == 0) { + tab.classList.add("selected") + tabLink.classList.add("active") + } + }) + let tabsOverview = document.createElement("div") + tabsOverview.classList.add("tabs-overview") + let tabsOverviewContainer = document.createElement("div") + tabsOverviewContainer.classList.add("tabs-overview-container") + tabLinkList.forEach((tabLink) => { + tabsOverview.appendChild(tabLink) + }) + tabsOverviewContainer.appendChild(tabsOverview) + tabbed.before(tabsOverviewContainer) + + function resize() { + let maxTabHeight = 0 + tabbed.querySelectorAll("li").forEach((tab, tabIndex) => { + let visibility = tab.style.display + tab.style.display = "block" + maxTabHeight = Math.max(tab.offsetHeight, maxTabHeight) + tab.style.display = visibility + }) + tabbed.style.height = `${maxTabHeight + 10}px` + } + + resize() + new ResizeObserver(resize).observe(tabbed) + }) + }) + + } + + static resize(tabbed) { + + } +} \ No newline at end of file diff --git a/docs/theme/doxygen-awesome.css b/docs/theme/doxygen-awesome.css new file mode 100644 index 0000000..ac7f060 --- /dev/null +++ b/docs/theme/doxygen-awesome.css @@ -0,0 +1,2669 @@ +/** + +Doxygen Awesome +https://github.com/jothepro/doxygen-awesome-css + +MIT License + +Copyright (c) 2021 - 2023 jothepro + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +html { + /* primary theme color. This will affect the entire websites color scheme: links, arrows, labels, ... */ + --primary-color: #1779c4; + --primary-dark-color: #335c80; + --primary-light-color: #70b1e9; + + /* page base colors */ + --page-background-color: #ffffff; + --page-foreground-color: #2f4153; + --page-secondary-foreground-color: #6f7e8e; + + /* color for all separators on the website: hr, borders, ... */ + --separator-color: #dedede; + + /* border radius for all rounded components. Will affect many components, like dropdowns, memitems, codeblocks, ... */ + --border-radius-large: 8px; + --border-radius-small: 4px; + --border-radius-medium: 6px; + + /* default spacings. Most components reference these values for spacing, to provide uniform spacing on the page. */ + --spacing-small: 5px; + --spacing-medium: 10px; + --spacing-large: 16px; + + /* default box shadow used for raising an element above the normal content. Used in dropdowns, search result, ... */ + --box-shadow: 0 2px 8px 0 rgba(0,0,0,.075); + + --odd-color: rgba(0,0,0,.028); + + /* font-families. will affect all text on the website + * font-family: the normal font for text, headlines, menus + * font-family-monospace: used for preformatted text in memtitle, code, fragments + */ + --font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif; + --font-family-monospace: ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,Liberation Mono,monospace; + + /* font sizes */ + --page-font-size: 15.6px; + --navigation-font-size: 14.4px; + --toc-font-size: 13.4px; + --code-font-size: 14px; /* affects code, fragment */ + --title-font-size: 22px; + + /* content text properties. These only affect the page content, not the navigation or any other ui elements */ + --content-line-height: 27px; + /* The content is centered and constraint in it's width. To make the content fill the whole page, set the variable to auto.*/ + --content-maxwidth: 1050px; + --table-line-height: 24px; + --toc-sticky-top: var(--spacing-medium); + --toc-width: 200px; + --toc-max-height: calc(100vh - 2 * var(--spacing-medium) - 85px); + + /* colors for various content boxes: @warning, @note, @deprecated @bug */ + --warning-color: #faf3d8; + --warning-color-dark: #f3a600; + --warning-color-darker: #5f4204; + --note-color: #e4f3ff; + --note-color-dark: #1879C4; + --note-color-darker: #274a5c; + --todo-color: #e4dafd; + --todo-color-dark: #5b2bdd; + --todo-color-darker: #2a0d72; + --deprecated-color: #ecf0f3; + --deprecated-color-dark: #5b6269; + --deprecated-color-darker: #43454a; + --bug-color: #f8d1cc; + --bug-color-dark: #b61825; + --bug-color-darker: #75070f; + --invariant-color: #d8f1e3; + --invariant-color-dark: #44b86f; + --invariant-color-darker: #265532; + + /* blockquote colors */ + --blockquote-background: #f8f9fa; + --blockquote-foreground: #636568; + + /* table colors */ + --tablehead-background: #f1f1f1; + --tablehead-foreground: var(--page-foreground-color); + + /* menu-display: block | none + * Visibility of the top navigation on screens >= 768px. On smaller screen the menu is always visible. + * `GENERATE_TREEVIEW` MUST be enabled! + */ + --menu-display: block; + + --menu-focus-foreground: var(--page-background-color); + --menu-focus-background: var(--primary-color); + --menu-selected-background: rgba(0,0,0,.05); + + + --header-background: var(--page-background-color); + --header-foreground: var(--page-foreground-color); + + /* searchbar colors */ + --searchbar-background: var(--side-nav-background); + --searchbar-foreground: var(--page-foreground-color); + + /* searchbar size + * (`searchbar-width` is only applied on screens >= 768px. + * on smaller screens the searchbar will always fill the entire screen width) */ + --searchbar-height: 33px; + --searchbar-width: 210px; + --searchbar-border-radius: var(--searchbar-height); + + /* code block colors */ + --code-background: #f5f5f5; + --code-foreground: var(--page-foreground-color); + + /* fragment colors */ + --fragment-background: #F8F9FA; + --fragment-foreground: #37474F; + --fragment-keyword: #bb6bb2; + --fragment-keywordtype: #8258b3; + --fragment-keywordflow: #d67c3b; + --fragment-token: #438a59; + --fragment-comment: #969696; + --fragment-link: #5383d6; + --fragment-preprocessor: #46aaa5; + --fragment-linenumber-color: #797979; + --fragment-linenumber-background: #f4f4f5; + --fragment-linenumber-border: #e3e5e7; + --fragment-lineheight: 20px; + + /* sidebar navigation (treeview) colors */ + --side-nav-background: #fbfbfb; + --side-nav-foreground: var(--page-foreground-color); + --side-nav-arrow-opacity: 0; + --side-nav-arrow-hover-opacity: 0.9; + + --toc-background: var(--side-nav-background); + --toc-foreground: var(--side-nav-foreground); + + /* height of an item in any tree / collapsible table */ + --tree-item-height: 30px; + + --memname-font-size: var(--code-font-size); + --memtitle-font-size: 18px; + + --webkit-scrollbar-size: 7px; + --webkit-scrollbar-padding: 4px; + --webkit-scrollbar-color: var(--separator-color); + + --animation-duration: .12s +} + +@media screen and (max-width: 767px) { + html { + --page-font-size: 16px; + --navigation-font-size: 16px; + --toc-font-size: 15px; + --code-font-size: 15px; /* affects code, fragment */ + --title-font-size: 22px; + } +} + +@media (prefers-color-scheme: dark) { + html:not(.light-mode) { + color-scheme: dark; + + --primary-color: #1982d2; + --primary-dark-color: #86a9c4; + --primary-light-color: #4779ac; + + --box-shadow: 0 2px 8px 0 rgba(0,0,0,.35); + + --odd-color: rgba(100,100,100,.06); + + --menu-selected-background: rgba(0,0,0,.4); + + --page-background-color: #1C1D1F; + --page-foreground-color: #d2dbde; + --page-secondary-foreground-color: #859399; + --separator-color: #38393b; + --side-nav-background: #252628; + + --code-background: #2a2c2f; + + --tablehead-background: #2a2c2f; + + --blockquote-background: #222325; + --blockquote-foreground: #7e8c92; + + --warning-color: #3b2e04; + --warning-color-dark: #f1b602; + --warning-color-darker: #ceb670; + --note-color: #163750; + --note-color-dark: #1982D2; + --note-color-darker: #dcf0fa; + --todo-color: #2a2536; + --todo-color-dark: #7661b3; + --todo-color-darker: #ae9ed6; + --deprecated-color: #2e323b; + --deprecated-color-dark: #738396; + --deprecated-color-darker: #abb0bd; + --bug-color: #2e1917; + --bug-color-dark: #ad2617; + --bug-color-darker: #f5b1aa; + --invariant-color: #303a35; + --invariant-color-dark: #76ce96; + --invariant-color-darker: #cceed5; + + --fragment-background: #282c34; + --fragment-foreground: #dbe4eb; + --fragment-keyword: #cc99cd; + --fragment-keywordtype: #ab99cd; + --fragment-keywordflow: #e08000; + --fragment-token: #7ec699; + --fragment-comment: #999999; + --fragment-link: #98c0e3; + --fragment-preprocessor: #65cabe; + --fragment-linenumber-color: #cccccc; + --fragment-linenumber-background: #35393c; + --fragment-linenumber-border: #1f1f1f; + } +} + +/* dark mode variables are defined twice, to support both the dark-mode without and with doxygen-awesome-darkmode-toggle.js */ +html.dark-mode { + color-scheme: dark; + + --primary-color: #1982d2; + --primary-dark-color: #86a9c4; + --primary-light-color: #4779ac; + + --box-shadow: 0 2px 8px 0 rgba(0,0,0,.30); + + --odd-color: rgba(100,100,100,.06); + + --menu-selected-background: rgba(0,0,0,.4); + + --page-background-color: #1C1D1F; + --page-foreground-color: #d2dbde; + --page-secondary-foreground-color: #859399; + --separator-color: #38393b; + --side-nav-background: #252628; + + --code-background: #2a2c2f; + + --tablehead-background: #2a2c2f; + + --blockquote-background: #222325; + --blockquote-foreground: #7e8c92; + + --warning-color: #3b2e04; + --warning-color-dark: #f1b602; + --warning-color-darker: #ceb670; + --note-color: #163750; + --note-color-dark: #1982D2; + --note-color-darker: #dcf0fa; + --todo-color: #2a2536; + --todo-color-dark: #7661b3; + --todo-color-darker: #ae9ed6; + --deprecated-color: #2e323b; + --deprecated-color-dark: #738396; + --deprecated-color-darker: #abb0bd; + --bug-color: #2e1917; + --bug-color-dark: #ad2617; + --bug-color-darker: #f5b1aa; + --invariant-color: #303a35; + --invariant-color-dark: #76ce96; + --invariant-color-darker: #cceed5; + + --fragment-background: #282c34; + --fragment-foreground: #dbe4eb; + --fragment-keyword: #cc99cd; + --fragment-keywordtype: #ab99cd; + --fragment-keywordflow: #e08000; + --fragment-token: #7ec699; + --fragment-comment: #999999; + --fragment-link: #98c0e3; + --fragment-preprocessor: #65cabe; + --fragment-linenumber-color: #cccccc; + --fragment-linenumber-background: #35393c; + --fragment-linenumber-border: #1f1f1f; +} + +body { + color: var(--page-foreground-color); + background-color: var(--page-background-color); + font-size: var(--page-font-size); +} + +body, table, div, p, dl, #nav-tree .label, .title, +.sm-dox a, .sm-dox a:hover, .sm-dox a:focus, #projectname, +.SelectItem, #MSearchField, .navpath li.navelem a, +.navpath li.navelem a:hover, p.reference, p.definition { + font-family: var(--font-family); +} + +h1, h2, h3, h4, h5 { + margin-top: 1em; + font-weight: 600; + line-height: initial; +} + +p, div, table, dl, p.reference, p.definition { + font-size: var(--page-font-size); +} + +p.reference, p.definition { + color: var(--page-secondary-foreground-color); +} + +a:link, a:visited, a:hover, a:focus, a:active { + color: var(--primary-color) !important; + font-weight: 500; +} + +a.anchor { + scroll-margin-top: var(--spacing-large); + display: block; +} + +/* + Title and top navigation + */ + +#top { + background: var(--header-background); + border-bottom: 1px solid var(--separator-color); +} + +@media screen and (min-width: 768px) { + #top { + display: flex; + flex-wrap: wrap; + justify-content: space-between; + align-items: center; + } +} + +#main-nav { + flex-grow: 5; + padding: var(--spacing-small) var(--spacing-medium); +} + +#titlearea { + width: auto; + padding: var(--spacing-medium) var(--spacing-large); + background: none; + color: var(--header-foreground); + border-bottom: none; +} + +@media screen and (max-width: 767px) { + #titlearea { + padding-bottom: var(--spacing-small); + } +} + +#titlearea table tbody tr { + height: auto !important; +} + +#projectname { + font-size: var(--title-font-size); + font-weight: 600; +} + +#projectnumber { + font-family: inherit; + font-size: 60%; +} + +#projectbrief { + font-family: inherit; + font-size: 80%; +} + +#projectlogo { + vertical-align: middle; +} + +#projectlogo img { + max-height: calc(var(--title-font-size) * 2); + margin-right: var(--spacing-small); +} + +.sm-dox, .tabs, .tabs2, .tabs3 { + background: none; + padding: 0; +} + +.tabs, .tabs2, .tabs3 { + border-bottom: 1px solid var(--separator-color); + margin-bottom: -1px; +} + +.main-menu-btn-icon, .main-menu-btn-icon:before, .main-menu-btn-icon:after { + background: var(--page-secondary-foreground-color); +} + +@media screen and (max-width: 767px) { + .sm-dox a span.sub-arrow { + background: var(--code-background); + } + + #main-menu a.has-submenu span.sub-arrow { + color: var(--page-secondary-foreground-color); + border-radius: var(--border-radius-medium); + } + + #main-menu a.has-submenu:hover span.sub-arrow { + color: var(--page-foreground-color); + } +} + +@media screen and (min-width: 768px) { + .sm-dox li, .tablist li { + display: var(--menu-display); + } + + .sm-dox a span.sub-arrow { + border-color: var(--header-foreground) transparent transparent transparent; + } + + .sm-dox a:hover span.sub-arrow { + border-color: var(--menu-focus-foreground) transparent transparent transparent; + } + + .sm-dox ul a span.sub-arrow { + border-color: transparent transparent transparent var(--page-foreground-color); + } + + .sm-dox ul a:hover span.sub-arrow { + border-color: transparent transparent transparent var(--menu-focus-foreground); + } +} + +.sm-dox ul { + background: var(--page-background-color); + box-shadow: var(--box-shadow); + border: 1px solid var(--separator-color); + border-radius: var(--border-radius-medium) !important; + padding: var(--spacing-small); + animation: ease-out 150ms slideInMenu; +} + +@keyframes slideInMenu { + from { + opacity: 0; + transform: translate(0px, -2px); + } + + to { + opacity: 1; + transform: translate(0px, 0px); + } +} + +.sm-dox ul a { + color: var(--page-foreground-color) !important; + background: var(--page-background-color); + font-size: var(--navigation-font-size); +} + +.sm-dox>li>ul:after { + border-bottom-color: var(--page-background-color) !important; +} + +.sm-dox>li>ul:before { + border-bottom-color: var(--separator-color) !important; +} + +.sm-dox ul a:hover, .sm-dox ul a:active, .sm-dox ul a:focus { + font-size: var(--navigation-font-size) !important; + color: var(--menu-focus-foreground) !important; + text-shadow: none; + background-color: var(--menu-focus-background); + border-radius: var(--border-radius-small) !important; +} + +.sm-dox a, .sm-dox a:focus, .tablist li, .tablist li a, .tablist li.current a { + text-shadow: none; + background: transparent; + background-image: none !important; + color: var(--header-foreground) !important; + font-weight: normal; + font-size: var(--navigation-font-size); + border-radius: var(--border-radius-small) !important; +} + +.sm-dox a:focus { + outline: auto; +} + +.sm-dox a:hover, .sm-dox a:active, .tablist li a:hover { + text-shadow: none; + font-weight: normal; + background: var(--menu-focus-background); + color: var(--menu-focus-foreground) !important; + border-radius: var(--border-radius-small) !important; + font-size: var(--navigation-font-size); +} + +.tablist li.current { + border-radius: var(--border-radius-small); + background: var(--menu-selected-background); +} + +.tablist li { + margin: var(--spacing-small) 0 var(--spacing-small) var(--spacing-small); +} + +.tablist a { + padding: 0 var(--spacing-large); +} + + +/* + Search box + */ + +#MSearchBox { + height: var(--searchbar-height); + background: var(--searchbar-background); + border-radius: var(--searchbar-border-radius); + border: 1px solid var(--separator-color); + overflow: hidden; + width: var(--searchbar-width); + position: relative; + box-shadow: none; + display: block; + margin-top: 0; +} + +/* until Doxygen 1.9.4 */ +.left img#MSearchSelect { + left: 0; + user-select: none; + padding-left: 8px; +} + +/* Doxygen 1.9.5 */ +.left span#MSearchSelect { + left: 0; + user-select: none; + margin-left: 8px; + padding: 0; +} + +.left #MSearchSelect[src$=".png"] { + padding-left: 0 +} + +.SelectionMark { + user-select: none; +} + +.tabs .left #MSearchSelect { + padding-left: 0; +} + +.tabs #MSearchBox { + position: absolute; + right: var(--spacing-medium); +} + +@media screen and (max-width: 767px) { + .tabs #MSearchBox { + position: relative; + right: 0; + margin-left: var(--spacing-medium); + margin-top: 0; + } +} + +#MSearchSelectWindow, #MSearchResultsWindow { + z-index: 9999; +} + +#MSearchBox.MSearchBoxActive { + border-color: var(--primary-color); + box-shadow: inset 0 0 0 1px var(--primary-color); +} + +#main-menu > li:last-child { + margin-right: 0; +} + +@media screen and (max-width: 767px) { + #main-menu > li:last-child { + height: 50px; + } +} + +#MSearchField { + font-size: var(--navigation-font-size); + height: calc(var(--searchbar-height) - 2px); + background: transparent; + width: calc(var(--searchbar-width) - 64px); +} + +.MSearchBoxActive #MSearchField { + color: var(--searchbar-foreground); +} + +#MSearchSelect { + top: calc(calc(var(--searchbar-height) / 2) - 11px); +} + +#MSearchBox span.left, #MSearchBox span.right { + background: none; + background-image: none; +} + +#MSearchBox span.right { + padding-top: calc(calc(var(--searchbar-height) / 2) - 12px); + position: absolute; + right: var(--spacing-small); +} + +.tabs #MSearchBox span.right { + top: calc(calc(var(--searchbar-height) / 2) - 12px); +} + +@keyframes slideInSearchResults { + from { + opacity: 0; + transform: translate(0, 15px); + } + + to { + opacity: 1; + transform: translate(0, 20px); + } +} + +#MSearchResultsWindow { + left: auto !important; + right: var(--spacing-medium); + border-radius: var(--border-radius-large); + border: 1px solid var(--separator-color); + transform: translate(0, 20px); + box-shadow: var(--box-shadow); + animation: ease-out 280ms slideInSearchResults; + background: var(--page-background-color); +} + +iframe#MSearchResults { + margin: 4px; +} + +iframe { + color-scheme: normal; +} + +@media (prefers-color-scheme: dark) { + html:not(.light-mode) iframe#MSearchResults { + filter: invert() hue-rotate(180deg); + } +} + +html.dark-mode iframe#MSearchResults { + filter: invert() hue-rotate(180deg); +} + +#MSearchResults .SRPage { + background-color: transparent; +} + +#MSearchResults .SRPage .SREntry { + font-size: 10pt; + padding: var(--spacing-small) var(--spacing-medium); +} + +#MSearchSelectWindow { + border: 1px solid var(--separator-color); + border-radius: var(--border-radius-medium); + box-shadow: var(--box-shadow); + background: var(--page-background-color); + padding-top: var(--spacing-small); + padding-bottom: var(--spacing-small); +} + +#MSearchSelectWindow a.SelectItem { + font-size: var(--navigation-font-size); + line-height: var(--content-line-height); + margin: 0 var(--spacing-small); + border-radius: var(--border-radius-small); + color: var(--page-foreground-color) !important; + font-weight: normal; +} + +#MSearchSelectWindow a.SelectItem:hover { + background: var(--menu-focus-background); + color: var(--menu-focus-foreground) !important; +} + +@media screen and (max-width: 767px) { + #MSearchBox { + margin-top: var(--spacing-medium); + margin-bottom: var(--spacing-medium); + width: calc(100vw - 30px); + } + + #main-menu > li:last-child { + float: none !important; + } + + #MSearchField { + width: calc(100vw - 110px); + } + + @keyframes slideInSearchResultsMobile { + from { + opacity: 0; + transform: translate(0, 15px); + } + + to { + opacity: 1; + transform: translate(0, 20px); + } + } + + #MSearchResultsWindow { + left: var(--spacing-medium) !important; + right: var(--spacing-medium); + overflow: auto; + transform: translate(0, 20px); + animation: ease-out 280ms slideInSearchResultsMobile; + width: auto !important; + } + + /* + * Overwrites for fixing the searchbox on mobile in doxygen 1.9.2 + */ + label.main-menu-btn ~ #searchBoxPos1 { + top: 3px !important; + right: 6px !important; + left: 45px; + display: flex; + } + + label.main-menu-btn ~ #searchBoxPos1 > #MSearchBox { + margin-top: 0; + margin-bottom: 0; + flex-grow: 2; + float: left; + } +} + +/* + Tree view + */ + +#side-nav { + padding: 0 !important; + background: var(--side-nav-background); + min-width: 8px; + max-width: 50vw; +} + +@media screen and (max-width: 767px) { + #side-nav { + display: none; + } + + #doc-content { + margin-left: 0 !important; + } +} + +#nav-tree { + background: transparent; + margin-right: 1px; +} + +#nav-tree .label { + font-size: var(--navigation-font-size); +} + +#nav-tree .item { + height: var(--tree-item-height); + line-height: var(--tree-item-height); +} + +#nav-sync { + bottom: 12px; + right: 12px; + top: auto !important; + user-select: none; +} + +#nav-tree .selected { + text-shadow: none; + background-image: none; + background-color: transparent; + position: relative; +} + +#nav-tree .selected::after { + content: ""; + position: absolute; + top: 1px; + bottom: 1px; + left: 0; + width: 4px; + border-radius: 0 var(--border-radius-small) var(--border-radius-small) 0; + background: var(--primary-color); +} + + +#nav-tree a { + color: var(--side-nav-foreground) !important; + font-weight: normal; +} + +#nav-tree a:focus { + outline-style: auto; +} + +#nav-tree .arrow { + opacity: var(--side-nav-arrow-opacity); +} + +.arrow { + color: inherit; + cursor: pointer; + font-size: 45%; + vertical-align: middle; + margin-right: 2px; + font-family: serif; + height: auto; + text-align: right; +} + +#nav-tree div.item:hover .arrow, #nav-tree a:focus .arrow { + opacity: var(--side-nav-arrow-hover-opacity); +} + +#nav-tree .selected a { + color: var(--primary-color) !important; + font-weight: bolder; + font-weight: 600; +} + +.ui-resizable-e { + width: 4px; + background: transparent; + box-shadow: inset -1px 0 0 0 var(--separator-color); +} + +/* + Contents + */ + +div.header { + border-bottom: 1px solid var(--separator-color); + background-color: var(--page-background-color); + background-image: none; +} + +@media screen and (min-width: 1000px) { + #doc-content > div > div.contents, + .PageDoc > div.contents { + display: flex; + flex-direction: row-reverse; + flex-wrap: nowrap; + align-items: flex-start; + } + + div.contents .textblock { + min-width: 200px; + flex-grow: 1; + } +} + +div.contents, div.header .title, div.header .summary { + max-width: var(--content-maxwidth); +} + +div.contents, div.header .title { + line-height: initial; + margin: calc(var(--spacing-medium) + .2em) auto var(--spacing-medium) auto; +} + +div.header .summary { + margin: var(--spacing-medium) auto 0 auto; +} + +div.headertitle { + padding: 0; +} + +div.header .title { + font-weight: 600; + font-size: 225%; + padding: var(--spacing-medium) var(--spacing-large); + word-break: break-word; +} + +div.header .summary { + width: auto; + display: block; + float: none; + padding: 0 var(--spacing-large); +} + +td.memSeparator { + border-color: var(--separator-color); +} + +span.mlabel { + background: var(--primary-color); + border: none; + padding: 4px 9px; + border-radius: 12px; + margin-right: var(--spacing-medium); +} + +span.mlabel:last-of-type { + margin-right: 2px; +} + +div.contents { + padding: 0 var(--spacing-large); +} + +div.contents p, div.contents li { + line-height: var(--content-line-height); +} + +div.contents div.dyncontent { + margin: var(--spacing-medium) 0; +} + +@media (prefers-color-scheme: dark) { + html:not(.light-mode) div.contents div.dyncontent img, + html:not(.light-mode) div.contents center img, + html:not(.light-mode) div.contents > table img, + html:not(.light-mode) div.contents div.dyncontent iframe, + html:not(.light-mode) div.contents center iframe, + html:not(.light-mode) div.contents table iframe, + html:not(.light-mode) div.contents .dotgraph iframe { + filter: brightness(89%) hue-rotate(180deg) invert(); + } +} + +html.dark-mode div.contents div.dyncontent img, +html.dark-mode div.contents center img, +html.dark-mode div.contents > table img, +html.dark-mode div.contents div.dyncontent iframe, +html.dark-mode div.contents center iframe, +html.dark-mode div.contents table iframe, +html.dark-mode div.contents .dotgraph iframe + { + filter: brightness(89%) hue-rotate(180deg) invert(); +} + +h2.groupheader { + border-bottom: 0px; + color: var(--page-foreground-color); + box-shadow: + 100px 0 var(--page-background-color), + -100px 0 var(--page-background-color), + 100px 0.75px var(--separator-color), + -100px 0.75px var(--separator-color), + 500px 0 var(--page-background-color), + -500px 0 var(--page-background-color), + 500px 0.75px var(--separator-color), + -500px 0.75px var(--separator-color), + 900px 0 var(--page-background-color), + -900px 0 var(--page-background-color), + 900px 0.75px var(--separator-color), + -900px 0.75px var(--separator-color), + 1400px 0 var(--page-background-color), + -1400px 0 var(--page-background-color), + 1400px 0.75px var(--separator-color), + -1400px 0.75px var(--separator-color), + 1900px 0 var(--page-background-color), + -1900px 0 var(--page-background-color), + 1900px 0.75px var(--separator-color), + -1900px 0.75px var(--separator-color); +} + +blockquote { + margin: 0 var(--spacing-medium) 0 var(--spacing-medium); + padding: var(--spacing-small) var(--spacing-large); + background: var(--blockquote-background); + color: var(--blockquote-foreground); + border-left: 0; + overflow: visible; + border-radius: var(--border-radius-medium); + overflow: visible; + position: relative; +} + +blockquote::before, blockquote::after { + font-weight: bold; + font-family: serif; + font-size: 360%; + opacity: .15; + position: absolute; +} + +blockquote::before { + content: "“"; + left: -10px; + top: 4px; +} + +blockquote::after { + content: "”"; + right: -8px; + bottom: -25px; +} + +blockquote p { + margin: var(--spacing-small) 0 var(--spacing-medium) 0; +} +.paramname { + font-weight: 600; + color: var(--primary-dark-color); +} + +.paramname > code { + border: 0; +} + +table.params .paramname { + font-weight: 600; + font-family: var(--font-family-monospace); + font-size: var(--code-font-size); + padding-right: var(--spacing-small); + line-height: var(--table-line-height); +} + +h1.glow, h2.glow, h3.glow, h4.glow, h5.glow, h6.glow { + text-shadow: 0 0 15px var(--primary-light-color); +} + +.alphachar a { + color: var(--page-foreground-color); +} + +.dotgraph { + max-width: 100%; + overflow-x: scroll; +} + +.dotgraph .caption { + position: sticky; + left: 0; +} + +/* Wrap Graphviz graphs with the `interactive_dotgraph` class if `INTERACTIVE_SVG = YES` */ +.interactive_dotgraph .dotgraph iframe { + max-width: 100%; +} + +/* + Table of Contents + */ + +div.contents .toc { + max-height: var(--toc-max-height); + min-width: var(--toc-width); + border: 0; + border-left: 1px solid var(--separator-color); + border-radius: 0; + background-color: transparent; + box-shadow: none; + position: sticky; + top: var(--toc-sticky-top); + padding: 0 var(--spacing-large); + margin: var(--spacing-small) 0 var(--spacing-large) var(--spacing-large); +} + +div.toc h3 { + color: var(--toc-foreground); + font-size: var(--navigation-font-size); + margin: var(--spacing-large) 0 var(--spacing-medium) 0; +} + +div.toc li { + padding: 0; + background: none; + line-height: var(--toc-font-size); + margin: var(--toc-font-size) 0 0 0; +} + +div.toc li::before { + display: none; +} + +div.toc ul { + margin-top: 0 +} + +div.toc li a { + font-size: var(--toc-font-size); + color: var(--page-foreground-color) !important; + text-decoration: none; +} + +div.toc li a:hover, div.toc li a.active { + color: var(--primary-color) !important; +} + +div.toc li a.aboveActive { + color: var(--page-secondary-foreground-color) !important; +} + + +@media screen and (max-width: 999px) { + div.contents .toc { + max-height: 45vh; + float: none; + width: auto; + margin: 0 0 var(--spacing-medium) 0; + position: relative; + top: 0; + position: relative; + border: 1px solid var(--separator-color); + border-radius: var(--border-radius-medium); + background-color: var(--toc-background); + box-shadow: var(--box-shadow); + } + + div.contents .toc.interactive { + max-height: calc(var(--navigation-font-size) + 2 * var(--spacing-large)); + overflow: hidden; + } + + div.contents .toc > h3 { + -webkit-tap-highlight-color: transparent; + cursor: pointer; + position: sticky; + top: 0; + background-color: var(--toc-background); + margin: 0; + padding: var(--spacing-large) 0; + display: block; + } + + div.contents .toc.interactive > h3::before { + content: ""; + width: 0; + height: 0; + border-left: 4px solid transparent; + border-right: 4px solid transparent; + border-top: 5px solid var(--primary-color); + display: inline-block; + margin-right: var(--spacing-small); + margin-bottom: calc(var(--navigation-font-size) / 4); + transform: rotate(-90deg); + transition: transform var(--animation-duration) ease-out; + } + + div.contents .toc.interactive.open > h3::before { + transform: rotate(0deg); + } + + div.contents .toc.interactive.open { + max-height: 45vh; + overflow: auto; + transition: max-height 0.2s ease-in-out; + } + + div.contents .toc a, div.contents .toc a.active { + color: var(--primary-color) !important; + } + + div.contents .toc a:hover { + text-decoration: underline; + } +} + +/* + Code & Fragments + */ + +code, div.fragment, pre.fragment { + border-radius: var(--border-radius-small); + border: 1px solid var(--separator-color); + overflow: hidden; +} + +code { + display: inline; + background: var(--code-background); + color: var(--code-foreground); + padding: 2px 6px; +} + +div.fragment, pre.fragment { + margin: var(--spacing-medium) 0; + padding: calc(var(--spacing-large) - (var(--spacing-large) / 6)) var(--spacing-large); + background: var(--fragment-background); + color: var(--fragment-foreground); + overflow-x: auto; +} + +@media screen and (max-width: 767px) { + div.fragment, pre.fragment { + border-top-right-radius: 0; + border-bottom-right-radius: 0; + border-right: 0; + } + + .contents > div.fragment, + .textblock > div.fragment, + .textblock > pre.fragment, + .textblock > .tabbed > ul > li > div.fragment, + .textblock > .tabbed > ul > li > pre.fragment, + .contents > .doxygen-awesome-fragment-wrapper > div.fragment, + .textblock > .doxygen-awesome-fragment-wrapper > div.fragment, + .textblock > .doxygen-awesome-fragment-wrapper > pre.fragment, + .textblock > .tabbed > ul > li > .doxygen-awesome-fragment-wrapper > div.fragment, + .textblock > .tabbed > ul > li > .doxygen-awesome-fragment-wrapper > pre.fragment { + margin: var(--spacing-medium) calc(0px - var(--spacing-large)); + border-radius: 0; + border-left: 0; + } + + .textblock li > .fragment, + .textblock li > .doxygen-awesome-fragment-wrapper > .fragment { + margin: var(--spacing-medium) calc(0px - var(--spacing-large)); + } + + .memdoc li > .fragment, + .memdoc li > .doxygen-awesome-fragment-wrapper > .fragment { + margin: var(--spacing-medium) calc(0px - var(--spacing-medium)); + } + + .textblock ul, .memdoc ul { + overflow: initial; + } + + .memdoc > div.fragment, + .memdoc > pre.fragment, + dl dd > div.fragment, + dl dd pre.fragment, + .memdoc > .doxygen-awesome-fragment-wrapper > div.fragment, + .memdoc > .doxygen-awesome-fragment-wrapper > pre.fragment, + dl dd > .doxygen-awesome-fragment-wrapper > div.fragment, + dl dd .doxygen-awesome-fragment-wrapper > pre.fragment { + margin: var(--spacing-medium) calc(0px - var(--spacing-medium)); + border-radius: 0; + border-left: 0; + } +} + +code, code a, pre.fragment, div.fragment, div.fragment .line, div.fragment span, div.fragment .line a, div.fragment .line span { + font-family: var(--font-family-monospace); + font-size: var(--code-font-size) !important; +} + +div.line:after { + margin-right: var(--spacing-medium); +} + +div.fragment .line, pre.fragment { + white-space: pre; + word-wrap: initial; + line-height: var(--fragment-lineheight); +} + +div.fragment span.keyword { + color: var(--fragment-keyword); +} + +div.fragment span.keywordtype { + color: var(--fragment-keywordtype); +} + +div.fragment span.keywordflow { + color: var(--fragment-keywordflow); +} + +div.fragment span.stringliteral { + color: var(--fragment-token) +} + +div.fragment span.comment { + color: var(--fragment-comment); +} + +div.fragment a.code { + color: var(--fragment-link) !important; +} + +div.fragment span.preprocessor { + color: var(--fragment-preprocessor); +} + +div.fragment span.lineno { + display: inline-block; + width: 27px; + border-right: none; + background: var(--fragment-linenumber-background); + color: var(--fragment-linenumber-color); +} + +div.fragment span.lineno a { + background: none; + color: var(--fragment-link) !important; +} + +div.fragment > .line:first-child .lineno { + box-shadow: -999999px 0px 0 999999px var(--fragment-linenumber-background), -999998px 0px 0 999999px var(--fragment-linenumber-border); + background-color: var(--fragment-linenumber-background) !important; +} + +div.line { + border-radius: var(--border-radius-small); +} + +div.line.glow { + background-color: var(--primary-light-color); + box-shadow: none; +} + +/* + dl warning, attention, note, deprecated, bug, ... + */ + +dl.bug dt a, dl.deprecated dt a, dl.todo dt a { + font-weight: bold !important; +} + +dl.warning, dl.attention, dl.note, dl.deprecated, dl.bug, dl.invariant, dl.pre, dl.post, dl.todo, dl.remark { + padding: var(--spacing-medium); + margin: var(--spacing-medium) 0; + color: var(--page-background-color); + overflow: hidden; + margin-left: 0; + border-radius: var(--border-radius-small); +} + +dl.section dd { + margin-bottom: 2px; +} + +dl.warning, dl.attention { + background: var(--warning-color); + border-left: 8px solid var(--warning-color-dark); + color: var(--warning-color-darker); +} + +dl.warning dt, dl.attention dt { + color: var(--warning-color-dark); +} + +dl.note, dl.remark { + background: var(--note-color); + border-left: 8px solid var(--note-color-dark); + color: var(--note-color-darker); +} + +dl.note dt, dl.remark dt { + color: var(--note-color-dark); +} + +dl.todo { + background: var(--todo-color); + border-left: 8px solid var(--todo-color-dark); + color: var(--todo-color-darker); +} + +dl.todo dt a { + color: var(--todo-color-dark) !important; +} + +dl.bug dt a { + color: var(--todo-color-dark) !important; +} + +dl.bug { + background: var(--bug-color); + border-left: 8px solid var(--bug-color-dark); + color: var(--bug-color-darker); +} + +dl.bug dt a { + color: var(--bug-color-dark) !important; +} + +dl.deprecated { + background: var(--deprecated-color); + border-left: 8px solid var(--deprecated-color-dark); + color: var(--deprecated-color-darker); +} + +dl.deprecated dt a { + color: var(--deprecated-color-dark) !important; +} + +dl.section dd, dl.bug dd, dl.deprecated dd, dl.todo dd { + margin-inline-start: 0px; +} + +dl.invariant, dl.pre, dl.post { + background: var(--invariant-color); + border-left: 8px solid var(--invariant-color-dark); + color: var(--invariant-color-darker); +} + +dl.invariant dt, dl.pre dt, dl.post dt { + color: var(--invariant-color-dark); +} + +/* + memitem + */ + +div.memdoc, div.memproto, h2.memtitle { + box-shadow: none; + background-image: none; + border: none; +} + +div.memdoc { + padding: 0 var(--spacing-medium); + background: var(--page-background-color); +} + +h2.memtitle, div.memitem { + border: 1px solid var(--separator-color); + box-shadow: var(--box-shadow); +} + +h2.memtitle { + box-shadow: 0px var(--spacing-medium) 0 -1px var(--fragment-background), var(--box-shadow); +} + +div.memitem { + transition: none; +} + +div.memproto, h2.memtitle { + background: var(--fragment-background); +} + +h2.memtitle { + font-weight: 500; + font-size: var(--memtitle-font-size); + font-family: var(--font-family-monospace); + border-bottom: none; + border-top-left-radius: var(--border-radius-medium); + border-top-right-radius: var(--border-radius-medium); + word-break: break-all; + position: relative; +} + +h2.memtitle:after { + content: ""; + display: block; + background: var(--fragment-background); + height: var(--spacing-medium); + bottom: calc(0px - var(--spacing-medium)); + left: 0; + right: -14px; + position: absolute; + border-top-right-radius: var(--border-radius-medium); +} + +h2.memtitle > span.permalink { + font-size: inherit; +} + +h2.memtitle > span.permalink > a { + text-decoration: none; + padding-left: 3px; + margin-right: -4px; + user-select: none; + display: inline-block; + margin-top: -6px; +} + +h2.memtitle > span.permalink > a:hover { + color: var(--primary-dark-color) !important; +} + +a:target + h2.memtitle, a:target + h2.memtitle + div.memitem { + border-color: var(--primary-light-color); +} + +div.memitem { + border-top-right-radius: var(--border-radius-medium); + border-bottom-right-radius: var(--border-radius-medium); + border-bottom-left-radius: var(--border-radius-medium); + overflow: hidden; + display: block !important; +} + +div.memdoc { + border-radius: 0; +} + +div.memproto { + border-radius: 0 var(--border-radius-small) 0 0; + overflow: auto; + border-bottom: 1px solid var(--separator-color); + padding: var(--spacing-medium); + margin-bottom: -1px; +} + +div.memtitle { + border-top-right-radius: var(--border-radius-medium); + border-top-left-radius: var(--border-radius-medium); +} + +div.memproto table.memname { + font-family: var(--font-family-monospace); + color: var(--page-foreground-color); + font-size: var(--memname-font-size); + text-shadow: none; +} + +div.memproto div.memtemplate { + font-family: var(--font-family-monospace); + color: var(--primary-dark-color); + font-size: var(--memname-font-size); + margin-left: 2px; + text-shadow: none; +} + +table.mlabels, table.mlabels > tbody { + display: block; +} + +td.mlabels-left { + width: auto; +} + +td.mlabels-right { + margin-top: 3px; + position: sticky; + left: 0; +} + +table.mlabels > tbody > tr:first-child { + display: flex; + justify-content: space-between; + flex-wrap: wrap; +} + +.memname, .memitem span.mlabels { + margin: 0 +} + +/* + reflist + */ + +dl.reflist { + box-shadow: var(--box-shadow); + border-radius: var(--border-radius-medium); + border: 1px solid var(--separator-color); + overflow: hidden; + padding: 0; +} + + +dl.reflist dt, dl.reflist dd { + box-shadow: none; + text-shadow: none; + background-image: none; + border: none; + padding: 12px; +} + + +dl.reflist dt { + font-weight: 500; + border-radius: 0; + background: var(--code-background); + border-bottom: 1px solid var(--separator-color); + color: var(--page-foreground-color) +} + + +dl.reflist dd { + background: none; +} + +/* + Table + */ + +.contents table:not(.memberdecls):not(.mlabels):not(.fieldtable):not(.memname), +.contents table:not(.memberdecls):not(.mlabels):not(.fieldtable):not(.memname) tbody { + display: inline-block; + max-width: 100%; +} + +.contents > table:not(.memberdecls):not(.mlabels):not(.fieldtable):not(.memname):not(.classindex) { + margin-left: calc(0px - var(--spacing-large)); + margin-right: calc(0px - var(--spacing-large)); + max-width: calc(100% + 2 * var(--spacing-large)); +} + +table.fieldtable, +table.markdownTable tbody, +table.doxtable tbody { + border: none; + margin: var(--spacing-medium) 0; + box-shadow: 0 0 0 1px var(--separator-color); + border-radius: var(--border-radius-small); +} + +table.markdownTable, table.doxtable, table.fieldtable { + padding: 1px; +} + +table.doxtable caption { + display: block; +} + +table.fieldtable { + border-collapse: collapse; + width: 100%; +} + +th.markdownTableHeadLeft, +th.markdownTableHeadRight, +th.markdownTableHeadCenter, +th.markdownTableHeadNone, +table.doxtable th { + background: var(--tablehead-background); + color: var(--tablehead-foreground); + font-weight: 600; + font-size: var(--page-font-size); +} + +th.markdownTableHeadLeft:first-child, +th.markdownTableHeadRight:first-child, +th.markdownTableHeadCenter:first-child, +th.markdownTableHeadNone:first-child, +table.doxtable tr th:first-child { + border-top-left-radius: var(--border-radius-small); +} + +th.markdownTableHeadLeft:last-child, +th.markdownTableHeadRight:last-child, +th.markdownTableHeadCenter:last-child, +th.markdownTableHeadNone:last-child, +table.doxtable tr th:last-child { + border-top-right-radius: var(--border-radius-small); +} + +table.markdownTable td, +table.markdownTable th, +table.fieldtable td, +table.fieldtable th, +table.doxtable td, +table.doxtable th { + border: 1px solid var(--separator-color); + padding: var(--spacing-small) var(--spacing-medium); +} + +table.markdownTable td:last-child, +table.markdownTable th:last-child, +table.fieldtable td:last-child, +table.fieldtable th:last-child, +table.doxtable td:last-child, +table.doxtable th:last-child { + border-right: none; +} + +table.markdownTable td:first-child, +table.markdownTable th:first-child, +table.fieldtable td:first-child, +table.fieldtable th:first-child, +table.doxtable td:first-child, +table.doxtable th:first-child { + border-left: none; +} + +table.markdownTable tr:first-child td, +table.markdownTable tr:first-child th, +table.fieldtable tr:first-child td, +table.fieldtable tr:first-child th, +table.doxtable tr:first-child td, +table.doxtable tr:first-child th { + border-top: none; +} + +table.markdownTable tr:last-child td, +table.markdownTable tr:last-child th, +table.fieldtable tr:last-child td, +table.fieldtable tr:last-child th, +table.doxtable tr:last-child td, +table.doxtable tr:last-child th { + border-bottom: none; +} + +table.markdownTable tr, table.doxtable tr { + border-bottom: 1px solid var(--separator-color); +} + +table.markdownTable tr:last-child, table.doxtable tr:last-child { + border-bottom: none; +} + +.full_width_table table:not(.memberdecls):not(.mlabels):not(.fieldtable):not(.memname) { + display: block; +} + +.full_width_table table:not(.memberdecls):not(.mlabels):not(.fieldtable):not(.memname) tbody { + display: table; + width: 100%; +} + +table.fieldtable th { + font-size: var(--page-font-size); + font-weight: 600; + background-image: none; + background-color: var(--tablehead-background); + color: var(--tablehead-foreground); +} + +table.fieldtable td.fieldtype, .fieldtable td.fieldname, .fieldtable td.fielddoc, .fieldtable th { + border-bottom: 1px solid var(--separator-color); + border-right: 1px solid var(--separator-color); +} + +table.fieldtable tr:last-child td:first-child { + border-bottom-left-radius: var(--border-radius-small); +} + +table.fieldtable tr:last-child td:last-child { + border-bottom-right-radius: var(--border-radius-small); +} + +.memberdecls td.glow, .fieldtable tr.glow { + background-color: var(--primary-light-color); + box-shadow: none; +} + +table.memberdecls { + display: block; + -webkit-tap-highlight-color: transparent; +} + +table.memberdecls tr[class^='memitem'] { + font-family: var(--font-family-monospace); + font-size: var(--code-font-size); +} + +table.memberdecls tr[class^='memitem'] .memTemplParams { + font-family: var(--font-family-monospace); + font-size: var(--code-font-size); + color: var(--primary-dark-color); + white-space: normal; +} + +table.memberdecls .memItemLeft, +table.memberdecls .memItemRight, +table.memberdecls .memTemplItemLeft, +table.memberdecls .memTemplItemRight, +table.memberdecls .memTemplParams { + transition: none; + padding-top: var(--spacing-small); + padding-bottom: var(--spacing-small); + border-top: 1px solid var(--separator-color); + border-bottom: 1px solid var(--separator-color); + background-color: var(--fragment-background); +} + +table.memberdecls .memTemplItemLeft, +table.memberdecls .memTemplItemRight { + padding-top: 2px; +} + +table.memberdecls .memTemplParams { + border-bottom: 0; + border-left: 1px solid var(--separator-color); + border-right: 1px solid var(--separator-color); + border-radius: var(--border-radius-small) var(--border-radius-small) 0 0; + padding-bottom: var(--spacing-small); +} + +table.memberdecls .memTemplItemLeft { + border-radius: 0 0 0 var(--border-radius-small); + border-left: 1px solid var(--separator-color); + border-top: 0; +} + +table.memberdecls .memTemplItemRight { + border-radius: 0 0 var(--border-radius-small) 0; + border-right: 1px solid var(--separator-color); + padding-left: 0; + border-top: 0; +} + +table.memberdecls .memItemLeft { + border-radius: var(--border-radius-small) 0 0 var(--border-radius-small); + border-left: 1px solid var(--separator-color); + padding-left: var(--spacing-medium); + padding-right: 0; +} + +table.memberdecls .memItemRight { + border-radius: 0 var(--border-radius-small) var(--border-radius-small) 0; + border-right: 1px solid var(--separator-color); + padding-right: var(--spacing-medium); + padding-left: 0; + +} + +table.memberdecls .mdescLeft, table.memberdecls .mdescRight { + background: none; + color: var(--page-foreground-color); + padding: var(--spacing-small) 0; +} + +table.memberdecls .memItemLeft, +table.memberdecls .memTemplItemLeft { + padding-right: var(--spacing-medium); +} + +table.memberdecls .memSeparator { + background: var(--page-background-color); + height: var(--spacing-large); + border: 0; + transition: none; +} + +table.memberdecls .groupheader { + margin-bottom: var(--spacing-large); +} + +table.memberdecls .inherit_header td { + padding: 0 0 var(--spacing-medium) 0; + text-indent: -12px; + color: var(--page-secondary-foreground-color); +} + +table.memberdecls img[src="closed.png"], +table.memberdecls img[src="open.png"], +div.dynheader img[src="open.png"], +div.dynheader img[src="closed.png"] { + width: 0; + height: 0; + border-left: 4px solid transparent; + border-right: 4px solid transparent; + border-top: 5px solid var(--primary-color); + margin-top: 8px; + display: block; + float: left; + margin-left: -10px; + transition: transform var(--animation-duration) ease-out; +} + +table.memberdecls img { + margin-right: 10px; +} + +table.memberdecls img[src="closed.png"], +div.dynheader img[src="closed.png"] { + transform: rotate(-90deg); + +} + +.compoundTemplParams { + font-family: var(--font-family-monospace); + color: var(--primary-dark-color); + font-size: var(--code-font-size); +} + +@media screen and (max-width: 767px) { + + table.memberdecls .memItemLeft, + table.memberdecls .memItemRight, + table.memberdecls .mdescLeft, + table.memberdecls .mdescRight, + table.memberdecls .memTemplItemLeft, + table.memberdecls .memTemplItemRight, + table.memberdecls .memTemplParams { + display: block; + text-align: left; + padding-left: var(--spacing-large); + margin: 0 calc(0px - var(--spacing-large)) 0 calc(0px - var(--spacing-large)); + border-right: none; + border-left: none; + border-radius: 0; + white-space: normal; + } + + table.memberdecls .memItemLeft, + table.memberdecls .mdescLeft, + table.memberdecls .memTemplItemLeft { + border-bottom: 0; + padding-bottom: 0; + } + + table.memberdecls .memTemplItemLeft { + padding-top: 0; + } + + table.memberdecls .mdescLeft { + margin-bottom: calc(0px - var(--page-font-size)); + } + + table.memberdecls .memItemRight, + table.memberdecls .mdescRight, + table.memberdecls .memTemplItemRight { + border-top: 0; + padding-top: 0; + padding-right: var(--spacing-large); + overflow-x: auto; + } + + table.memberdecls tr[class^='memitem']:not(.inherit) { + display: block; + width: calc(100vw - 2 * var(--spacing-large)); + } + + table.memberdecls .mdescRight { + color: var(--page-foreground-color); + } + + table.memberdecls tr.inherit { + visibility: hidden; + } + + table.memberdecls tr[style="display: table-row;"] { + display: block !important; + visibility: visible; + width: calc(100vw - 2 * var(--spacing-large)); + animation: fade .5s; + } + + @keyframes fade { + 0% { + opacity: 0; + max-height: 0; + } + + 100% { + opacity: 1; + max-height: 200px; + } + } +} + + +/* + Horizontal Rule + */ + +hr { + margin-top: var(--spacing-large); + margin-bottom: var(--spacing-large); + height: 1px; + background-color: var(--separator-color); + border: 0; +} + +.contents hr { + box-shadow: 100px 0 0 var(--separator-color), + -100px 0 0 var(--separator-color), + 500px 0 0 var(--separator-color), + -500px 0 0 var(--separator-color), + 1500px 0 0 var(--separator-color), + -1500px 0 0 var(--separator-color), + 2000px 0 0 var(--separator-color), + -2000px 0 0 var(--separator-color); +} + +.contents img, .contents .center, .contents center, .contents div.image object { + max-width: 100%; + overflow: auto; +} + +@media screen and (max-width: 767px) { + .contents .dyncontent > .center, .contents > center { + margin-left: calc(0px - var(--spacing-large)); + margin-right: calc(0px - var(--spacing-large)); + max-width: calc(100% + 2 * var(--spacing-large)); + } +} + +/* + Directories + */ +div.directory { + border-top: 1px solid var(--separator-color); + border-bottom: 1px solid var(--separator-color); + width: auto; +} + +table.directory { + font-family: var(--font-family); + font-size: var(--page-font-size); + font-weight: normal; + width: 100%; +} + +table.directory td.entry, table.directory td.desc { + padding: calc(var(--spacing-small) / 2) var(--spacing-small); + line-height: var(--table-line-height); +} + +table.directory tr.even td:last-child { + border-radius: 0 var(--border-radius-small) var(--border-radius-small) 0; +} + +table.directory tr.even td:first-child { + border-radius: var(--border-radius-small) 0 0 var(--border-radius-small); +} + +table.directory tr.even:last-child td:last-child { + border-radius: 0 var(--border-radius-small) 0 0; +} + +table.directory tr.even:last-child td:first-child { + border-radius: var(--border-radius-small) 0 0 0; +} + +table.directory td.desc { + min-width: 250px; +} + +table.directory tr.even { + background-color: var(--odd-color); +} + +table.directory tr.odd { + background-color: transparent; +} + +.icona { + width: auto; + height: auto; + margin: 0 var(--spacing-small); +} + +.icon { + background: var(--primary-color); + border-radius: var(--border-radius-small); + font-size: var(--page-font-size); + padding: calc(var(--page-font-size) / 5); + line-height: var(--page-font-size); + transform: scale(0.8); + height: auto; + width: var(--page-font-size); + user-select: none; +} + +.iconfopen, .icondoc, .iconfclosed { + background-position: center; + margin-bottom: 0; + height: var(--table-line-height); +} + +.icondoc { + filter: saturate(0.2); +} + +@media screen and (max-width: 767px) { + div.directory { + margin-left: calc(0px - var(--spacing-large)); + margin-right: calc(0px - var(--spacing-large)); + } +} + +@media (prefers-color-scheme: dark) { + html:not(.light-mode) .iconfopen, html:not(.light-mode) .iconfclosed { + filter: hue-rotate(180deg) invert(); + } +} + +html.dark-mode .iconfopen, html.dark-mode .iconfclosed { + filter: hue-rotate(180deg) invert(); +} + +/* + Class list + */ + +.classindex dl.odd { + background: var(--odd-color); + border-radius: var(--border-radius-small); +} + +.classindex dl.even { + background-color: transparent; +} + +/* + Class Index Doxygen 1.8 +*/ + +table.classindex { + margin-left: 0; + margin-right: 0; + width: 100%; +} + +table.classindex table div.ah { + background-image: none; + background-color: initial; + border-color: var(--separator-color); + color: var(--page-foreground-color); + box-shadow: var(--box-shadow); + border-radius: var(--border-radius-large); + padding: var(--spacing-small); +} + +div.qindex { + background-color: var(--odd-color); + border-radius: var(--border-radius-small); + border: 1px solid var(--separator-color); + padding: var(--spacing-small) 0; +} + +/* + Footer and nav-path + */ + +#nav-path { + width: 100%; +} + +#nav-path ul { + background-image: none; + background: var(--page-background-color); + border: none; + border-top: 1px solid var(--separator-color); + border-bottom: 1px solid var(--separator-color); + border-bottom: 0; + box-shadow: 0 0.75px 0 var(--separator-color); + font-size: var(--navigation-font-size); +} + +img.footer { + width: 60px; +} + +.navpath li.footer { + color: var(--page-secondary-foreground-color); +} + +address.footer { + color: var(--page-secondary-foreground-color); + margin-bottom: var(--spacing-large); +} + +#nav-path li.navelem { + background-image: none; + display: flex; + align-items: center; +} + +.navpath li.navelem a { + text-shadow: none; + display: inline-block; + color: var(--primary-color) !important; +} + +.navpath li.navelem b { + color: var(--primary-dark-color); + font-weight: 500; +} + +li.navelem { + padding: 0; + margin-left: -8px; +} + +li.navelem:first-child { + margin-left: var(--spacing-large); +} + +li.navelem:first-child:before { + display: none; +} + +#nav-path li.navelem:after { + content: ''; + border: 5px solid var(--page-background-color); + border-bottom-color: transparent; + border-right-color: transparent; + border-top-color: transparent; + transform: translateY(-1px) scaleY(4.2); + z-index: 10; + margin-left: 6px; +} + +#nav-path li.navelem:before { + content: ''; + border: 5px solid var(--separator-color); + border-bottom-color: transparent; + border-right-color: transparent; + border-top-color: transparent; + transform: translateY(-1px) scaleY(3.2); + margin-right: var(--spacing-small); +} + +.navpath li.navelem a:hover { + color: var(--primary-color); +} + +/* + Scrollbars for Webkit +*/ + +#nav-tree::-webkit-scrollbar, +div.fragment::-webkit-scrollbar, +pre.fragment::-webkit-scrollbar, +div.memproto::-webkit-scrollbar, +.contents center::-webkit-scrollbar, +.contents .center::-webkit-scrollbar, +.contents table:not(.memberdecls):not(.mlabels):not(.fieldtable):not(.memname) tbody::-webkit-scrollbar, +div.contents .toc::-webkit-scrollbar, +.contents .dotgraph::-webkit-scrollbar, +.contents .tabs-overview-container::-webkit-scrollbar { + background: transparent; + width: calc(var(--webkit-scrollbar-size) + var(--webkit-scrollbar-padding) + var(--webkit-scrollbar-padding)); + height: calc(var(--webkit-scrollbar-size) + var(--webkit-scrollbar-padding) + var(--webkit-scrollbar-padding)); +} + +#nav-tree::-webkit-scrollbar-thumb, +div.fragment::-webkit-scrollbar-thumb, +pre.fragment::-webkit-scrollbar-thumb, +div.memproto::-webkit-scrollbar-thumb, +.contents center::-webkit-scrollbar-thumb, +.contents .center::-webkit-scrollbar-thumb, +.contents table:not(.memberdecls):not(.mlabels):not(.fieldtable):not(.memname) tbody::-webkit-scrollbar-thumb, +div.contents .toc::-webkit-scrollbar-thumb, +.contents .dotgraph::-webkit-scrollbar-thumb, +.contents .tabs-overview-container::-webkit-scrollbar-thumb { + background-color: transparent; + border: var(--webkit-scrollbar-padding) solid transparent; + border-radius: calc(var(--webkit-scrollbar-padding) + var(--webkit-scrollbar-padding)); + background-clip: padding-box; +} + +#nav-tree:hover::-webkit-scrollbar-thumb, +div.fragment:hover::-webkit-scrollbar-thumb, +pre.fragment:hover::-webkit-scrollbar-thumb, +div.memproto:hover::-webkit-scrollbar-thumb, +.contents center:hover::-webkit-scrollbar-thumb, +.contents .center:hover::-webkit-scrollbar-thumb, +.contents table:not(.memberdecls):not(.mlabels):not(.fieldtable):not(.memname) tbody:hover::-webkit-scrollbar-thumb, +div.contents .toc:hover::-webkit-scrollbar-thumb, +.contents .dotgraph:hover::-webkit-scrollbar-thumb, +.contents .tabs-overview-container:hover::-webkit-scrollbar-thumb { + background-color: var(--webkit-scrollbar-color); +} + +#nav-tree::-webkit-scrollbar-track, +div.fragment::-webkit-scrollbar-track, +pre.fragment::-webkit-scrollbar-track, +div.memproto::-webkit-scrollbar-track, +.contents center::-webkit-scrollbar-track, +.contents .center::-webkit-scrollbar-track, +.contents table:not(.memberdecls):not(.mlabels):not(.fieldtable):not(.memname) tbody::-webkit-scrollbar-track, +div.contents .toc::-webkit-scrollbar-track, +.contents .dotgraph::-webkit-scrollbar-track, +.contents .tabs-overview-container::-webkit-scrollbar-track { + background: transparent; +} + +#nav-tree::-webkit-scrollbar-corner { + background-color: var(--side-nav-background); +} + +#nav-tree, +div.fragment, +pre.fragment, +div.memproto, +.contents center, +.contents .center, +.contents table:not(.memberdecls):not(.mlabels):not(.fieldtable):not(.memname) tbody, +div.contents .toc { + overflow-x: auto; + overflow-x: overlay; +} + +#nav-tree { + overflow-x: auto; + overflow-y: auto; + overflow-y: overlay; +} + +/* + Scrollbars for Firefox +*/ + +#nav-tree, +div.fragment, +pre.fragment, +div.memproto, +.contents center, +.contents .center, +.contents table:not(.memberdecls):not(.mlabels):not(.fieldtable):not(.memname) tbody, +div.contents .toc, +.contents .dotgraph, +.contents .tabs-overview-container { + scrollbar-width: thin; +} + +/* + Optional Dark mode toggle button +*/ + +doxygen-awesome-dark-mode-toggle { + display: inline-block; + margin: 0 0 0 var(--spacing-small); + padding: 0; + width: var(--searchbar-height); + height: var(--searchbar-height); + background: none; + border: none; + border-radius: var(--searchbar-height); + vertical-align: middle; + text-align: center; + line-height: var(--searchbar-height); + font-size: 22px; + display: flex; + align-items: center; + justify-content: center; + user-select: none; + cursor: pointer; +} + +doxygen-awesome-dark-mode-toggle > svg { + transition: transform var(--animation-duration) ease-in-out; +} + +doxygen-awesome-dark-mode-toggle:active > svg { + transform: scale(.5); +} + +doxygen-awesome-dark-mode-toggle:hover { + background-color: rgba(0,0,0,.03); +} + +html.dark-mode doxygen-awesome-dark-mode-toggle:hover { + background-color: rgba(0,0,0,.18); +} + +/* + Optional fragment copy button +*/ +.doxygen-awesome-fragment-wrapper { + position: relative; +} + +doxygen-awesome-fragment-copy-button { + opacity: 0; + background: var(--fragment-background); + width: 28px; + height: 28px; + position: absolute; + right: calc(var(--spacing-large) - (var(--spacing-large) / 2.5)); + top: calc(var(--spacing-large) - (var(--spacing-large) / 2.5)); + border: 1px solid var(--fragment-foreground); + cursor: pointer; + border-radius: var(--border-radius-small); + display: flex; + justify-content: center; + align-items: center; +} + +.doxygen-awesome-fragment-wrapper:hover doxygen-awesome-fragment-copy-button, doxygen-awesome-fragment-copy-button.success { + opacity: .28; +} + +doxygen-awesome-fragment-copy-button:hover, doxygen-awesome-fragment-copy-button.success { + opacity: 1 !important; +} + +doxygen-awesome-fragment-copy-button:active:not([class~=success]) svg { + transform: scale(.91); +} + +doxygen-awesome-fragment-copy-button svg { + fill: var(--fragment-foreground); + width: 18px; + height: 18px; +} + +doxygen-awesome-fragment-copy-button.success svg { + fill: rgb(14, 168, 14); +} + +doxygen-awesome-fragment-copy-button.success { + border-color: rgb(14, 168, 14); +} + +@media screen and (max-width: 767px) { + .textblock > .doxygen-awesome-fragment-wrapper > doxygen-awesome-fragment-copy-button, + .textblock li > .doxygen-awesome-fragment-wrapper > doxygen-awesome-fragment-copy-button, + .memdoc li > .doxygen-awesome-fragment-wrapper > doxygen-awesome-fragment-copy-button, + .memdoc > .doxygen-awesome-fragment-wrapper > doxygen-awesome-fragment-copy-button, + dl dd > .doxygen-awesome-fragment-wrapper > doxygen-awesome-fragment-copy-button { + right: 0; + } +} + +/* + Optional paragraph link button +*/ + +a.anchorlink { + font-size: 90%; + margin-left: var(--spacing-small); + color: var(--page-foreground-color) !important; + text-decoration: none; + opacity: .15; + display: none; + transition: opacity var(--animation-duration) ease-in-out, color var(--animation-duration) ease-in-out; +} + +a.anchorlink svg { + fill: var(--page-foreground-color); +} + +h3 a.anchorlink svg, h4 a.anchorlink svg { + margin-bottom: -3px; + margin-top: -4px; +} + +a.anchorlink:hover { + opacity: .45; +} + +h2:hover a.anchorlink, h1:hover a.anchorlink, h3:hover a.anchorlink, h4:hover a.anchorlink { + display: inline-block; +} + +/* + Optional tab feature +*/ + +.tabbed ul { + padding-inline-start: 0px; + margin: 0; + padding: var(--spacing-small) 0; +} + +.tabbed li { + display: none; +} + +.tabbed li.selected { + display: block; +} + +.tabs-overview-container { + overflow-x: auto; + display: block; + overflow-y: visible; +} + +.tabs-overview { + border-bottom: 1px solid var(--separator-color); + display: flex; + flex-direction: row; +} + +@media screen and (max-width: 767px) { + .tabs-overview-container { + margin: 0 calc(0px - var(--spacing-large)); + } + .tabs-overview { + padding: 0 var(--spacing-large) + } +} + +.tabs-overview button.tab-button { + color: var(--page-foreground-color); + margin: 0; + border: none; + background: transparent; + padding: calc(var(--spacing-large) / 2) 0; + display: inline-block; + font-size: var(--page-font-size); + cursor: pointer; + box-shadow: 0 1px 0 0 var(--separator-color); + position: relative; + + -webkit-tap-highlight-color: transparent; +} + +.tabs-overview button.tab-button .tab-title::before { + display: block; + content: attr(title); + font-weight: 600; + height: 0; + overflow: hidden; + visibility: hidden; +} + +.tabs-overview button.tab-button .tab-title { + float: left; + white-space: nowrap; + font-weight: normal; + padding: calc(var(--spacing-large) / 2) var(--spacing-large); + border-radius: var(--border-radius-medium); + transition: background-color var(--animation-duration) ease-in-out, font-weight var(--animation-duration) ease-in-out; +} + +.tabs-overview button.tab-button:not(:last-child) .tab-title { + box-shadow: 8px 0 0 -7px var(--separator-color); +} + +.tabs-overview button.tab-button:hover .tab-title { + background: var(--separator-color); + box-shadow: none; +} + +.tabs-overview button.tab-button.active .tab-title { + font-weight: 600; +} + +.tabs-overview button.tab-button::after { + content: ''; + display: block; + position: absolute; + left: 0; + bottom: 0; + right: 0; + height: 0; + width: 0%; + margin: 0 auto; + border-radius: var(--border-radius-small) var(--border-radius-small) 0 0; + background-color: var(--primary-color); + transition: width var(--animation-duration) ease-in-out, height var(--animation-duration) ease-in-out; +} + +.tabs-overview button.tab-button.active::after { + width: 100%; + box-sizing: border-box; + height: 3px; +} + + +/* + Navigation Buttons +*/ + +.section_buttons:not(:empty) { + margin-top: calc(var(--spacing-large) * 3); +} + +.section_buttons table.markdownTable { + display: block; + width: 100%; +} + +.section_buttons table.markdownTable tbody { + display: table !important; + width: 100%; + box-shadow: none; + border-spacing: 10px; +} + +.section_buttons table.markdownTable td { + padding: 0; +} + +.section_buttons table.markdownTable th { + display: none; +} + +.section_buttons table.markdownTable tr.markdownTableHead { + border: none; +} + +.section_buttons tr th, .section_buttons tr td { + background: none; + border: none; + padding: var(--spacing-large) 0 var(--spacing-small); +} + +.section_buttons a { + display: inline-block; + border: 1px solid var(--separator-color); + border-radius: var(--border-radius-medium); + color: var(--page-secondary-foreground-color) !important; + text-decoration: none; + transition: color var(--animation-duration) ease-in-out, background-color var(--animation-duration) ease-in-out; +} + +.section_buttons a:hover { + color: var(--page-foreground-color) !important; + background-color: var(--odd-color); +} + +.section_buttons tr td.markdownTableBodyLeft a { + padding: var(--spacing-medium) var(--spacing-large) var(--spacing-medium) calc(var(--spacing-large) / 2); +} + +.section_buttons tr td.markdownTableBodyRight a { + padding: var(--spacing-medium) calc(var(--spacing-large) / 2) var(--spacing-medium) var(--spacing-large); +} + +.section_buttons tr td.markdownTableBodyLeft a::before, +.section_buttons tr td.markdownTableBodyRight a::after { + color: var(--page-secondary-foreground-color) !important; + display: inline-block; + transition: color .08s ease-in-out, transform .09s ease-in-out; +} + +.section_buttons tr td.markdownTableBodyLeft a::before { + content: '〈'; + padding-right: var(--spacing-large); +} + + +.section_buttons tr td.markdownTableBodyRight a::after { + content: '〉'; + padding-left: var(--spacing-large); +} + + +.section_buttons tr td.markdownTableBodyLeft a:hover::before { + color: var(--page-foreground-color) !important; + transform: translateX(-3px); +} + +.section_buttons tr td.markdownTableBodyRight a:hover::after { + color: var(--page-foreground-color) !important; + transform: translateX(3px); +} + +@media screen and (max-width: 450px) { + .section_buttons a { + width: 100%; + box-sizing: border-box; + } + + .section_buttons tr td:nth-of-type(1).markdownTableBodyLeft a { + border-radius: var(--border-radius-medium) 0 0 var(--border-radius-medium); + border-right: none; + } + + .section_buttons tr td:nth-of-type(2).markdownTableBodyRight a { + border-radius: 0 var(--border-radius-medium) var(--border-radius-medium) 0; + } +} diff --git a/docs/theme/header.html b/docs/theme/header.html new file mode 100644 index 0000000..4bcbc67 --- /dev/null +++ b/docs/theme/header.html @@ -0,0 +1,104 @@ + + + + + + + + +$projectname: $title +$title + + + + + + + + + + + + + + +$treeview +$search +$mathjax +$darkmode + +$extrastylesheet + + + + + + + + + + + + + + + + + + +
+ + + +
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
$projectname $projectnumber +
+
$projectbrief
+
+
$projectbrief
+
$searchbox
$searchbox
+
+ + diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..47599fc --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 3.27) + +add_subdirectory(demo) + +if (DEMO_ENABLE_TEST) + add_subdirectory(tests) +endif () + +if (DEMO_ENABLE_EXAMPLE) + add_subdirectory(examples) +endif () diff --git a/src/demo/CMakeLists.txt b/src/demo/CMakeLists.txt new file mode 100644 index 0000000..f521969 --- /dev/null +++ b/src/demo/CMakeLists.txt @@ -0,0 +1,5 @@ +cmake_minimum_required(VERSION 3.27) + +add_subdirectory(cmd) +add_subdirectory(common) +add_subdirectory(liba) \ No newline at end of file diff --git a/src/demo/cmd/CMakeLists.txt b/src/demo/cmd/CMakeLists.txt new file mode 100644 index 0000000..697fdb0 --- /dev/null +++ b/src/demo/cmd/CMakeLists.txt @@ -0,0 +1,18 @@ +cmake_minimum_required(VERSION 3.27) + +demo_add_executable( + TARGET + demo_cmd + HEADERS + FILES + cmd.cpp +) + +if (DEMO_ENABLE_TEST) + demo_add_test( + TARGET + demo_cmd_unit + FILES + cmd_test.cpp + ) +endif () diff --git a/src/demo/cmd/cmd.cpp b/src/demo/cmd/cmd.cpp new file mode 100644 index 0000000..1f7bf4b --- /dev/null +++ b/src/demo/cmd/cmd.cpp @@ -0,0 +1,8 @@ +// +// Copyright (c) Zhanwei Wang (me@zhanwei.wang) +// Licenced under Apache License Version 2.0. See LICENSE for details. +// + +int main() { + return 0; +} \ No newline at end of file diff --git a/src/demo/cmd/cmd_test.cpp b/src/demo/cmd/cmd_test.cpp new file mode 100644 index 0000000..cb3464a --- /dev/null +++ b/src/demo/cmd/cmd_test.cpp @@ -0,0 +1,4 @@ +// +// Copyright (c) Zhanwei Wang (me@zhanwei.wang) +// Licenced under Apache License Version 2.0. See LICENSE for details. +// diff --git a/src/demo/common/CMakeLists.txt b/src/demo/common/CMakeLists.txt new file mode 100644 index 0000000..54e8d23 --- /dev/null +++ b/src/demo/common/CMakeLists.txt @@ -0,0 +1,23 @@ +cmake_minimum_required(VERSION 3.27) + +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/version.h.in ${CMAKE_CURRENT_SOURCE_DIR}/version.h) + +demo_add_library( + TARGET + demo_common + HEADERS + version.h + common_utils.h + FILES + common_utils.cpp +) + +if (DEMO_ENABLE_TEST) + demo_add_test( + TARGET + demo_common_unit + FILES + common_utils_test.cpp + ) + target_link_libraries(demo_common_unit PRIVATE demo_common) +endif () diff --git a/src/demo/common/common_utils.cpp b/src/demo/common/common_utils.cpp new file mode 100644 index 0000000..70a479d --- /dev/null +++ b/src/demo/common/common_utils.cpp @@ -0,0 +1,11 @@ +// +// Copyright (c) Zhanwei Wang (me@zhanwei.wang) +// Licenced under Apache License Version 2.0. See LICENSE for details. +// +#include "demo/common/common_utils.h" + +namespace demo { + +void common_utils::demo() {} + +} // namespace demo diff --git a/src/demo/common/common_utils.h b/src/demo/common/common_utils.h new file mode 100644 index 0000000..e8fc1ab --- /dev/null +++ b/src/demo/common/common_utils.h @@ -0,0 +1,16 @@ +// +// Copyright (c) Zhanwei Wang (me@zhanwei.wang) +// Licenced under Apache License Version 2.0. See LICENSE for details. +// +#ifndef DEMO_SRC_DEMO_COMMON_COMMON_UTILS_H +#define DEMO_SRC_DEMO_COMMON_COMMON_UTILS_H + +namespace demo { + +class common_utils { + public: + void demo(); +}; + +} // namespace demo +#endif // DEMO_SRC_DEMO_COMMON_COMMON_UTILS_H diff --git a/src/demo/common/common_utils_test.cpp b/src/demo/common/common_utils_test.cpp new file mode 100644 index 0000000..4316880 --- /dev/null +++ b/src/demo/common/common_utils_test.cpp @@ -0,0 +1,18 @@ +// +// Copyright (c) Zhanwei Wang (me@zhanwei.wang) +// Licenced under Apache License Version 2.0. See LICENSE for details. +// +#include +#include + +namespace { +struct mock_class { + MOCK_METHOD(void, test, ()); +}; + +TEST(demo, test) { + mock_class a; + EXPECT_CALL(a, test).Times(1); + a.test(); +} +} // namespace diff --git a/src/demo/common/version.h.in b/src/demo/common/version.h.in new file mode 100644 index 0000000..1b460ce --- /dev/null +++ b/src/demo/common/version.h.in @@ -0,0 +1,13 @@ +#ifndef DEMO_VERSION_H +#define DEMO_VERSION_H + +#define DEMO_VERSION_STRING "@DEMO_VERSION_STRING@" + +#define DEMO_VERSION_MAJOR @DEMO_VERSION_MAJOR@ +#define DEMO_VERSION_MINOR @DEMO_VERSION_MINOR@ +#define DEMO_VERSION_PATCH @DEMO_VERSION_PATCH@ + +#define DEMO_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c)) +#define DEMO_VERSION_NUMBER DEMO_VERSION(@DEMO_VERSION_MAJOR@, @DEMO_VERSION_MINOR@, @DEMO_VERSION_PATCH@) + +#endif // DEMO_VERSION_H diff --git a/src/demo/liba/CMakeLists.txt b/src/demo/liba/CMakeLists.txt new file mode 100644 index 0000000..a2d8a9c --- /dev/null +++ b/src/demo/liba/CMakeLists.txt @@ -0,0 +1,21 @@ +cmake_minimum_required(VERSION 3.27) + +demo_add_library( + TARGET + demo_a + HEADERS + demo.h + FILES + demo.cpp +) +target_link_libraries(demo_a PUBLIC demo_common) + +if (DEMO_ENABLE_TEST) + demo_add_test( + TARGET + demo_a_unit + FILES + demo_test.cpp + ) + target_link_libraries(demo_a_unit PRIVATE demo_a) +endif () diff --git a/src/demo/liba/demo.cpp b/src/demo/liba/demo.cpp new file mode 100644 index 0000000..4b69488 --- /dev/null +++ b/src/demo/liba/demo.cpp @@ -0,0 +1,11 @@ +// +// Copyright (c) Zhanwei Wang (me@zhanwei.wang) +// Licenced under Apache License Version 2.0. See LICENSE for details. +// +#include "demo/liba/demo.h" + +namespace demo { + +void demo::test() {} + +} // namespace demo diff --git a/src/demo/liba/demo.h b/src/demo/liba/demo.h new file mode 100644 index 0000000..4a4c434 --- /dev/null +++ b/src/demo/liba/demo.h @@ -0,0 +1,15 @@ +// +// Copyright (c) Zhanwei Wang (me@zhanwei.wang) +// Licenced under Apache License Version 2.0. See LICENSE for details. +// +#ifndef DEMO_SRC_DEMO_LIBA_DEMO_H +#define DEMO_SRC_DEMO_LIBA_DEMO_H + +namespace demo { +class demo { + public: + void test(); +}; +} // namespace demo + +#endif // DEMO_SRC_DEMO_LIBA_DEMO_H diff --git a/src/demo/liba/demo_test.cpp b/src/demo/liba/demo_test.cpp new file mode 100644 index 0000000..cb3464a --- /dev/null +++ b/src/demo/liba/demo_test.cpp @@ -0,0 +1,4 @@ +// +// Copyright (c) Zhanwei Wang (me@zhanwei.wang) +// Licenced under Apache License Version 2.0. See LICENSE for details. +// diff --git a/src/examples/CMakeLists.txt b/src/examples/CMakeLists.txt new file mode 100644 index 0000000..94aedb2 --- /dev/null +++ b/src/examples/CMakeLists.txt @@ -0,0 +1,5 @@ +cmake_minimum_required(VERSION 3.27) + +if (DEMO_WITH_GRPC) + add_subdirectory(grpc) +endif () diff --git a/src/examples/grpc/CMakeLists.txt b/src/examples/grpc/CMakeLists.txt new file mode 100644 index 0000000..2e6dc1c --- /dev/null +++ b/src/examples/grpc/CMakeLists.txt @@ -0,0 +1,31 @@ +cmake_minimum_required(VERSION 3.27) + +find_package(protobuf CONFIG REQUIRED) +find_package(gRPC CONFIG REQUIRED) + +add_library(grpc_proto OBJECT proto/helloworld.proto) + +protobuf_generate(TARGET grpc_proto + LANGUAGE cpp + OUT_VAR PROTO_GENERATED_FILES + IMPORT_DIRS ${CMAKE_CURRENT_SOURCE_DIR} + PROTOC_OUT_DIR ${CMAKE_CURRENT_BINARY_DIR} +) + +protobuf_generate(TARGET grpc_proto + LANGUAGE grpc + OUT_VAR PROTO_GRPC_GENERATED_FILES + GENERATE_EXTENSIONS .grpc.pb.h .grpc.pb.cc + PLUGIN "protoc-gen-grpc=\$" + IMPORT_DIRS ${CMAKE_CURRENT_SOURCE_DIR} + PROTOC_OUT_DIR ${CMAKE_CURRENT_BINARY_DIR} +) + +target_include_directories(grpc_proto PUBLIC ${CMAKE_CURRENT_BINARY_DIR}) +target_link_libraries(grpc_proto PUBLIC gRPC::grpc gRPC::grpc++) + +demo_add_example(TARGET grpc_server FILES grpc_server.cpp greeter_service.h greeter_service.cpp) +target_link_libraries(grpc_server grpc_proto) + +demo_add_example(TARGET grpc_client FILES grpc_client.cpp proto/helloworld.proto) +target_link_libraries(grpc_client grpc_proto) diff --git a/src/examples/grpc/greeter_service.cpp b/src/examples/grpc/greeter_service.cpp new file mode 100644 index 0000000..421eaaf --- /dev/null +++ b/src/examples/grpc/greeter_service.cpp @@ -0,0 +1,5 @@ +// +// Copyright (c) Zhanwei Wang (me@zhanwei.wang) +// Licenced under Apache License Version 2.0. See LICENSE for details. +// +#include "greeter_service.h" diff --git a/src/examples/grpc/greeter_service.h b/src/examples/grpc/greeter_service.h new file mode 100644 index 0000000..c6348cc --- /dev/null +++ b/src/examples/grpc/greeter_service.h @@ -0,0 +1,16 @@ +// +// Copyright (c) Zhanwei Wang (me@zhanwei.wang) +// Licenced under Apache License Version 2.0. See LICENSE for details. +// +#ifndef DEMO_SRC_EXAMPLES_GRPC_GREETER_SERVICE_H +#define DEMO_SRC_EXAMPLES_GRPC_GREETER_SERVICE_H + +#include "proto/helloworld.grpc.pb.h" + +namespace helloworld { +class greeter_service : public Greeter::AsyncService { + public: +}; +} // namespace helloworld + +#endif // DEMO_SRC_EXAMPLES_GRPC_GREETER_SERVICE_H diff --git a/src/examples/grpc/grpc_client.cpp b/src/examples/grpc/grpc_client.cpp new file mode 100644 index 0000000..df2b13f --- /dev/null +++ b/src/examples/grpc/grpc_client.cpp @@ -0,0 +1,10 @@ +// +// Copyright (c) Zhanwei Wang (me@zhanwei.wang) +// Licenced under Apache License Version 2.0. See LICENSE for details. +// + +#include "proto/helloworld.pb.h" + +int main() { + return 0; +} diff --git a/src/examples/grpc/grpc_server.cpp b/src/examples/grpc/grpc_server.cpp new file mode 100644 index 0000000..12304c9 --- /dev/null +++ b/src/examples/grpc/grpc_server.cpp @@ -0,0 +1,13 @@ +// +// Copyright (c) Zhanwei Wang (me@zhanwei.wang) +// Licenced under Apache License Version 2.0. See LICENSE for details. +// +#include + +#include "greeter_service.h" + +using namespace helloworld; + +int main() { + return 0; +} diff --git a/src/examples/grpc/proto/helloworld.proto b/src/examples/grpc/proto/helloworld.proto new file mode 100644 index 0000000..9a0b59c --- /dev/null +++ b/src/examples/grpc/proto/helloworld.proto @@ -0,0 +1,42 @@ +// Copyright 2015 gRPC authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +option java_multiple_files = true; +option java_package = "io.grpc.examples.helloworld"; +option java_outer_classname = "HelloWorldProto"; +option objc_class_prefix = "HLW"; + +package helloworld; + +// The greeting service definition. +service Greeter { + // Sends a greeting + rpc SayHello (HelloRequest) returns (HelloReply) {} + + rpc SayHelloStreamReply (HelloRequest) returns (stream HelloReply) {} + + rpc SayHelloBidiStream (stream HelloRequest) returns (stream HelloReply) {} +} + +// The request message containing the user's name. +message HelloRequest { + string name = 1; +} + +// The response message containing the greetings +message HelloReply { + string message = 1; +} diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt new file mode 100644 index 0000000..b1f12ca --- /dev/null +++ b/src/tests/CMakeLists.txt @@ -0,0 +1,7 @@ +cmake_minimum_required(VERSION 3.27) + +if (DEMO_ENABLE_BENCHMARK) + add_subdirectory(benchmark) +endif () + +add_subdirectory(function) diff --git a/src/tests/benchmark/CMakeLists.txt b/src/tests/benchmark/CMakeLists.txt new file mode 100644 index 0000000..7222a41 --- /dev/null +++ b/src/tests/benchmark/CMakeLists.txt @@ -0,0 +1,10 @@ +cmake_minimum_required(VERSION 3.27) + +demo_add_benchmark( + TARGET + demo_benchmark + FILES + demo_benchmark.cpp +) + +target_link_libraries(demo_benchmark PRIVATE demo_common) diff --git a/src/tests/benchmark/demo_benchmark.cpp b/src/tests/benchmark/demo_benchmark.cpp new file mode 100644 index 0000000..90a2bbe --- /dev/null +++ b/src/tests/benchmark/demo_benchmark.cpp @@ -0,0 +1,18 @@ +// +// Copyright (c) Zhanwei Wang (me@zhanwei.wang) +// Licenced under Apache License Version 2.0. See LICENSE for details. +// +#include + +namespace { + +static void(demo_throughput)(benchmark::State& state) { + for (auto _ : state) { + } + + state.counters["Rate"] = benchmark::Counter( + static_cast(state.range(0)), benchmark::Counter::kIsRate); +} + +BENCHMARK(demo_throughput); +} // namespace diff --git a/src/tests/function/CMakeLists.txt b/src/tests/function/CMakeLists.txt new file mode 100644 index 0000000..98fbec7 --- /dev/null +++ b/src/tests/function/CMakeLists.txt @@ -0,0 +1,8 @@ +cmake_minimum_required(VERSION 3.27) + +demo_add_test( + TARGET + function_test + FILES + function_test.cpp +) diff --git a/src/tests/function/function_test.cpp b/src/tests/function/function_test.cpp new file mode 100644 index 0000000..cb3464a --- /dev/null +++ b/src/tests/function/function_test.cpp @@ -0,0 +1,4 @@ +// +// Copyright (c) Zhanwei Wang (me@zhanwei.wang) +// Licenced under Apache License Version 2.0. See LICENSE for details. +// diff --git a/thirdparty/.gitignore b/thirdparty/.gitignore new file mode 100644 index 0000000..0920e14 --- /dev/null +++ b/thirdparty/.gitignore @@ -0,0 +1 @@ +/download/ diff --git a/thirdparty/CMakeLists.txt b/thirdparty/CMakeLists.txt new file mode 100644 index 0000000..30d260b --- /dev/null +++ b/thirdparty/CMakeLists.txt @@ -0,0 +1,80 @@ +cmake_minimum_required(VERSION 3.27) + +if (POLICY CMP0135) + cmake_policy(SET CMP0135 NEW) +endif () + +project(thirdparty) + +include(ExternalProject) + +ExternalProject_Add(googletest + PREFIX googletest + URL "https://github.com/google/googletest/archive/refs/tags/v1.14.0.tar.gz" + URL_HASH "MD5=c8340a482851ef6a3fe618a082304cfc" + DOWNLOAD_DIR "${CMAKE_SOURCE_DIR}/download/" + SOURCE_DIR "${CMAKE_BINARY_DIR}/googletest/src/googletest-1.14.0" + CMAKE_ARGS + -DCMAKE_TOOLCHAIN_FILE:PATH=${CMAKE_TOOLCHAIN_FILE} + CMAKE_CACHE_ARGS + -DCMAKE_BUILD_TYPE:STRING=Release + -DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_INSTALL_PREFIX} + -DCMAKE_PREFIX_PATH:LIST=${CMAKE_PREFIX_PATH} + -DCMAKE_FIND_ROOT_PATH:LIST=${CMAKE_FIND_ROOT_PATH} + -DCMAKE_POSITION_INDEPENDENT_CODE:BOOL=ON +) + +ExternalProject_Add(benchmark + PREFIX benchmark + GIT_REPOSITORY "https://github.com/google/benchmark.git" + GIT_TAG "v1.8.3" + SOURCE_DIR "${CMAKE_BINARY_DIR}/benchmark/src/benchmark" + CMAKE_ARGS + -DCMAKE_TOOLCHAIN_FILE:PATH=${CMAKE_TOOLCHAIN_FILE} + CMAKE_CACHE_ARGS + -DCMAKE_BUILD_TYPE:STRING=Release + -DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_INSTALL_PREFIX} + -DCMAKE_POSITION_INDEPENDENT_CODE:BOOL=TRUE + -DCMAKE_PREFIX_PATH:LIST=${CMAKE_PREFIX_PATH} + -DCMAKE_FIND_ROOT_PATH:LIST=${CMAKE_FIND_ROOT_PATH} + -DCMAKE_POSITION_INDEPENDENT_CODE:BOOL=ON + -DBENCHMARK_ENABLE_TESTING:BOOL=OFF + -DBENCHMARK_ENABLE_WERROR:BOOL=OFF + -DBENCHMARK_INSTALL_DOCS:BOOL=OFF + -DBENCHMARK_ENABLE_GTEST_TESTS:BOOL=OFF +) + +ExternalProject_Add(doxygen + PREFIX doxygen + URL "https://github.com/doxygen/doxygen/archive/refs/tags/Release_1_10_0.tar.gz" + URL_HASH "MD5=82de97da58776ee561cceab097c34ac0" + DOWNLOAD_DIR "${CMAKE_SOURCE_DIR}/download/" + SOURCE_DIR "${CMAKE_BINARY_DIR}/doxygen/src/doxygen-1.10.0" + CMAKE_ARGS + -DCMAKE_TOOLCHAIN_FILE:PATH=${CMAKE_TOOLCHAIN_FILE} + CMAKE_CACHE_ARGS + -DCMAKE_BUILD_TYPE:STRING=Release + -DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_INSTALL_PREFIX} + -DCMAKE_PREFIX_PATH:LIST=${CMAKE_PREFIX_PATH} + -DCMAKE_FIND_ROOT_PATH:LIST=${CMAKE_FIND_ROOT_PATH} + -DCMAKE_POSITION_INDEPENDENT_CODE:BOOL=ON +) + +ExternalProject_Add(grpc + PREFIX grpc + GIT_REPOSITORY "https://github.com/grpc/grpc.git" + GIT_TAG "v1.62.1" + DOWNLOAD_DIR "${CMAKE_SOURCE_DIR}/download/" + SOURCE_DIR "${CMAKE_BINARY_DIR}/grpc/src/v1.62.1" + CMAKE_ARGS + -DCMAKE_TOOLCHAIN_FILE:PATH=${CMAKE_TOOLCHAIN_FILE} + -DCMAKE_CROSSCOMPILING:BOOL=OFF + CMAKE_CACHE_ARGS + -DCMAKE_BUILD_TYPE:STRING=Release + -DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_INSTALL_PREFIX} + -DCMAKE_PREFIX_PATH:LIST=${CMAKE_PREFIX_PATH} + -DCMAKE_FIND_ROOT_PATH:LIST=${CMAKE_FIND_ROOT_PATH} + -DCMAKE_POSITION_INDEPENDENT_CODE:BOOL=ON + -DgRPC_INSTALL:BOOL=ON + -DgRPC_BUILD_TESTS:BOOL=OFF +)