diff --git a/.ci/docker/ubuntu.Dockerfile b/.ci/docker/ubuntu.Dockerfile deleted file mode 100644 index e71b915..0000000 --- a/.ci/docker/ubuntu.Dockerfile +++ /dev/null @@ -1,38 +0,0 @@ -# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -# Using a non-LTS Ubuntu, just until CMake 3.25 is available on Ubuntu 24.04 -FROM ubuntu:23.10 - -# Install dependencies, -RUN apt-get update -RUN apt-get install -y \ - clang \ - clang-tidy \ - g++ \ - ninja-build \ - cmake \ - git -RUN apt-get clean - -WORKDIR /workarea -COPY ./ ./ - -# Set build arguments. -ARG cc=gcc -ARG cxx=g++ -ARG cmake_args= - -# Workaround Ubuntu broken ASan -RUN sysctl vm.mmap_rnd_bits=28 - -# Build. -ENV CC="$cc" CXX="$cxx" CMAKE_GENERATOR="Ninja Multi-Config" CMAKE_EXPORT_COMPILE_COMMANDS=on -RUN ls -lR src -RUN cmake -B build -S . "$cmake_args" -RUN cmake --build build --config Release --verbose -RUN cmake --build build --config Release --target all_verify_interface_header_sets -RUN cmake --install build --config Release --prefix /opt/beman.exemplar -RUN cmake --build build --config Debug --verbose -RUN cmake --build build --config Debug --target all_verify_interface_header_sets -RUN cmake --install build --config Debug --prefix /opt/beman.exemplar -RUN find /opt/beman.exemplar -type f diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..05baf03 --- /dev/null +++ b/.clang-format @@ -0,0 +1,242 @@ +--- +Language: Cpp +AccessModifierOffset: -2 +AlignAfterOpenBracket: Align +AlignArrayOfStructures: None +AlignConsecutiveAssignments: + Enabled: true + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + PadOperators: true +AlignConsecutiveBitFields: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + PadOperators: false +AlignConsecutiveDeclarations: + Enabled: true + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + PadOperators: true +AlignConsecutiveMacros: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + PadOperators: false +AlignConsecutiveShortCaseStatements: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCaseColons: false +AlignEscapedNewlines: Left +AlignOperands: Align +AlignTrailingComments: + Kind: Always + OverEmptyLines: 0 +AllowAllArgumentsOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: Never +AllowShortCaseLabelsOnASingleLine: false +AllowShortEnumsOnASingleLine: true +AllowShortFunctionsOnASingleLine: All +AllowShortIfStatementsOnASingleLine: Never +AllowShortLambdasOnASingleLine: All +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: Yes +AttributeMacros: + - __capability +BinPackArguments: false +BinPackParameters: false +BitFieldColonSpacing: Both +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: Never + AfterEnum: false + AfterExternBlock: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + BeforeCatch: false + BeforeElse: false + BeforeLambdaBody: false + BeforeWhile: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakAfterAttributes: Never +BreakAfterJavaFieldAnnotations: false +BreakArrays: true +BreakBeforeBinaryOperators: None +BreakBeforeConceptDeclarations: Always +BreakBeforeBraces: Custom +BreakBeforeInlineASMColon: OnlyMultiline +BreakBeforeTernaryOperators: true +BreakConstructorInitializers: BeforeColon +BreakInheritanceList: BeforeColon +BreakStringLiterals: true +# Please update .markdownlint.yaml if this line is to be updated +ColumnLimit: 119 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DerivePointerAlignment: false +DisableFormat: false +EmptyLineAfterAccessModifier: Never +EmptyLineBeforeAccessModifier: LogicalBlock +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IfMacros: + - KJ_IF_MAYBE +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '^"(llvm|llvm-c|clang|clang-c)/' + Priority: 2 + SortPriority: 0 + CaseSensitive: false + - Regex: '^(<|"(gtest|isl|json)/)' + Priority: 3 + SortPriority: 0 + CaseSensitive: false + - Regex: '.*' + Priority: 1 + SortPriority: 0 + CaseSensitive: false +IncludeIsMainRegex: '$' +IncludeIsMainSourceRegex: '' +IndentAccessModifiers: false +IndentCaseBlocks: false +IndentCaseLabels: false +IndentExternBlock: AfterExternBlock +IndentGotoLabels: true +IndentPPDirectives: None +IndentRequiresClause: true +IndentWidth: 4 +IndentWrappedFunctionNames: false +InsertBraces: false +InsertNewlineAtEOF: false +InsertTrailingCommas: None +IntegerLiteralSeparator: + Binary: 0 + BinaryMinDigits: 0 + Decimal: 0 + DecimalMinDigits: 0 + Hex: 0 + HexMinDigits: 0 +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: true +KeepEmptyLinesAtEOF: false +LambdaBodyIndentation: Signature +LineEnding: DeriveLF +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBinPackProtocolList: Auto +ObjCBlockIndentWidth: 4 +ObjCBreakBeforeNestedBlockParam: true +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PackConstructorInitializers: NextLine +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakOpenParenthesis: 0 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyIndentedWhitespace: 0 +PenaltyReturnTypeOnItsOwnLine: 60 +PointerAlignment: Left +PPIndentWidth: -1 +QualifierAlignment: Custom +QualifierOrder: + - inline + - static + - constexpr + - const + - volatile + - type +ReferenceAlignment: Pointer +ReflowComments: true +RemoveBracesLLVM: false +RemoveParentheses: Leave +RemoveSemicolon: false +RequiresClausePosition: OwnLine +RequiresExpressionIndentation: OuterScope +SeparateDefinitionBlocks: Leave +ShortNamespaceLines: 1 +SortIncludes: Never +SortJavaStaticImport: Before +SortUsingDeclarations: LexicographicNumeric +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: true +SpaceAroundPointerQualifiers: Default +SpaceBeforeAssignmentOperators: true +SpaceBeforeCaseColon: false +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeJsonColon: false +SpaceBeforeParens: ControlStatements +SpaceBeforeParensOptions: + AfterControlStatements: true + AfterForeachMacros: true + AfterFunctionDefinitionName: false + AfterFunctionDeclarationName: false + AfterIfMacros: true + AfterOverloadedOperator: false + AfterRequiresInClause: false + AfterRequiresInExpression: false + BeforeNonEmptyParentheses: false +SpaceBeforeRangeBasedForLoopColon: true +SpaceBeforeSquareBrackets: false +SpaceInEmptyBlock: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: Never +SpacesInContainerLiterals: true +SpacesInLineCommentPrefix: + Minimum: 1 + Maximum: -1 +SpacesInParens: Never +SpacesInParensOptions: + InCStyleCasts: false + InConditionalStatements: false + InEmptyParentheses: false + Other: false +SpacesInSquareBrackets: false +Standard: Auto +StatementAttributeLikeMacros: + - Q_EMIT +StatementMacros: + - Q_UNUSED + - QT_REQUIRE_VERSION +TabWidth: 8 +UseTab: Never +VerilogBreakBetweenInstancePorts: true +WhitespaceSensitiveMacros: + - STRINGIZE + - PP_STRINGIZE + - BOOST_PP_STRINGIZE + - NS_SWIFT_NAME + - CF_SWIFT_NAME +... diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index dd041dd..90d9ff2 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,4 +1,4 @@ # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception # Codeowners for reviews on PRs -* @bretbrownjr @camio @dietmarkuehl @neatudarius @steve-downey +* @bretbrownjr @camio @dietmarkuehl @neatudarius @steve-downey diff --git a/.github/workflows/ci_tests.yml b/.github/workflows/ci_tests.yml index 84602e7..e350afd 100644 --- a/.github/workflows/ci_tests.yml +++ b/.github/workflows/ci_tests.yml @@ -11,84 +11,128 @@ on: - cron: '30 15 * * *' jobs: - test: + preset-test: runs-on: ubuntu-latest strategy: matrix: - cfg: - - { id: ubuntu-gcc-werror, platform: ubuntu, cc: gcc, cpp: g++, cmake_args: "-DCMAKE_CXX_FLAGS='-Werror=all -Werror=extra'"} - - { id: ubuntu-gcc-aubsan, platform: ubuntu, cc: gcc, cpp: g++, cmake_args: "-DCMAKE_CXX_FLAGS=-fsanitize=address -fsanitize=undefined"} - - { id: ubuntu-gcc-tsan, platform: ubuntu, cc: gcc, cpp: g++, cmake_args: "-DCMAKE_CXX_FLAGS=-fsanitize=thread"} - - { id: ubuntu-gcc-static, platform: ubuntu, cc: gcc, cpp: g++, cmake_args: ""} - - { id: ubuntu-gcc-dynamic, platform: ubuntu, cc: gcc, cpp: g++, cmake_args: "-DBUILD_SHARED_LIBS=on"} - - { id: ubuntu-clang-static, platform: ubuntu, cc: clang, cpp: clang++, cmake_args: ""} - - { id: ubuntu-clang-dynamic, platform: ubuntu, cc: clang, cpp: clang++, cmake_args: "-DBUILD_SHARED_LIBS=on"} - - { id: ubuntu-gcc-static-cxx20, platform: ubuntu, cc: gcc, cpp: g++, cmake_args: "-DCMAKE_CXX_STANDARD=20 -DCMAKE_CXX_STANDARD_REQUIRED=on"} - - { id: ubuntu-gcc-static-cxx23, platform: ubuntu, cc: gcc, cpp: g++, cmake_args: "-DCMAKE_CXX_STANDARD=23 -DCMAKE_CXX_STANDARD_REQUIRED=on"} - - { id: ubuntu-gcc-static-cxx26, platform: ubuntu, cc: gcc, cpp: g++, cmake_args: "-DCMAKE_CXX_STANDARD=26 -DCMAKE_CXX_STANDARD_REQUIRED=on"} - - { id: ubuntu-clang-static-cxx20, platform: ubuntu, cc: clang, cpp: clang++, cmake_args: "-DCMAKE_CXX_STANDARD=20 -DCMAKE_CXX_STANDARD_REQUIRED=on"} - - { id: ubuntu-clang-static-cxx23, platform: ubuntu, cc: clang, cpp: clang++, cmake_args: "-DCMAKE_CXX_STANDARD=23 -DCMAKE_CXX_STANDARD_REQUIRED=on"} - - { id: ubuntu-clang-static-cxx26, platform: ubuntu, cc: clang, cpp: clang++, cmake_args: "-DCMAKE_CXX_STANDARD=26 -DCMAKE_CXX_STANDARD_REQUIRED=on"} + preset: ["gcc-debug", "gcc-release"] + steps: + - uses: actions/checkout@v4 + - name: Setup cmake + uses: jwlawson/actions-setup-cmake@v2 + with: + cmake-version: '3.25.x' + - uses: seanmiddleditch/gha-setup-ninja@v5 + - name: Run preset + run: cmake --workflow --preset ${{ matrix.preset }} + + test: + strategy: + fail-fast: false + matrix: + platform: [ubuntu-latest] + compiler: + - cpp: g++ + c: gcc + - cpp: clang++ + c: clang + cmake_args: + - description: "Static default" + args: "" + - description: "Dynamic default" + args: "-DBUILD_SHARED_LIBS=on" + - description: "static C++17" + args: "-DCMAKE_CXX_STANDARD=17" + - description: "static C++20" + args: "-DCMAKE_CXX_STANDARD=20" + - description: "static C++23" + args: "-DCMAKE_CXX_STANDARD=23" + - description: "static C++26" + args: "-DCMAKE_CXX_STANDARD=26" + include: + - platform: ubuntu-latest + compiler: + cpp: g++ + c: gcc + cmake_args: + description: "Werror" + cmake_args: "-DCMAKE_CXX_FLAGS='-Werror=all -Werror=extra'" + - platform: ubuntu-latest + compiler: + cpp: g++ + c: gcc + cmake_args: + description: "A-San" + cmake_args: "-DCMAKE_CXX_FLAGS=-fsanitize=address -fsanitize=undefined" + - platform: ubuntu-latest + compiler: + cpp: g++ + c: gcc + cmake_args: + description: "T-San" + cmake_args: "-DCMAKE_CXX_FLAGS=-fsanitize=thread" + name: "Bulid & Test: ${{ matrix.compiler.c }} ${{ matrix.cmake_args.description }}" + runs-on: ${{ matrix.platform }} steps: - uses: actions/checkout@v4 - # GitHub runners have updated the Ubuntu Linux Kernel to use strong ASLR, - # but LLVM is not configured for this change, and thus the address - # sanitizer breaks. - # - # The next image is supposed to fix this, so if the Ubuntu image has been - # updated, this work around is no longer required. - - name: get runner image version - id: runner-image-version + - name: Install Ninja + uses: lukka/get-cmake@latest + with: + cmakeVersion: "~3.25.0" + ninjaVersion: "^1.11.1" + - name: Print installed softwares run: | - echo "image-version=$(echo $ImageVersion)" >> "$GITHUB_OUTPUT" - working-directory: . - - name: modify number of bits to use for ASLR entropy - if: ${{ steps.runner-image-version.outputs.ImageVersion }} == '20240310.1.0' + clang++ --version + g++ --version + cmake --version + ninja --version + - name: Build Release run: | - sudo sysctl -a | grep vm.mmap.rnd - sudo sysctl -w vm.mmap_rnd_bits=28 - working-directory: . - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - name: Build and push - uses: docker/build-push-action@v6 - with: - context: . - push: false - load: true - tags: ${{ matrix.cfg.id }} - file: .ci/docker/${{ matrix.cfg.platform }}.Dockerfile - build-args: | - cc=${{ matrix.cfg.cc }} - cxx=${{ matrix.cfg.cpp }} - cmake_args=${{ matrix.cfg.cmake_args }} - cache-from: type=gha - cache-to: type=gha,mode=max - - name: Run tests + cmake -B build -S . "${{ matrix.cmake_args.args }}" + cmake --build build --config Release --verbose + cmake --build build --config Release --target all_verify_interface_header_sets + cmake --install build --config Release --prefix /opt/beman.exemplar + find /opt/beman.exemplar -type f + env: + CC: ${{ matrix.compiler.c }} + CXX: ${{ matrix.compiler.cpp }} + CMAKE_GENERATOR: "Ninja Multi-Config" + - name: Test Release + run: ctest --test-dir build --build-config Release + - name: Build Debug run: | - docker run ${{ matrix.cfg.id }} ctest --test-dir build -C Release - docker run ${{ matrix.cfg.id }} ctest --test-dir build -C Debug + cmake -B build -S . "${{ matrix.cmake_args.args }}" + cmake --build build --config Debug --verbose + cmake --build build --config Debug --target all_verify_interface_header_sets + cmake --install build --config Debug --prefix /opt/beman.exemplar + find /opt/beman.exemplar -type f + env: + CC: ${{ matrix.compiler.c }} + CXX: ${{ matrix.compiler.cpp }} + CMAKE_GENERATOR: "Ninja Multi-Config" + - name: Test Release + run: ctest --test-dir build --build-config Debug create-issue-when-fault: runs-on: ubuntu-latest - needs: [test] + needs: [preset-test, test] if: failure() && github.event_name == 'schedule' steps: - # See https://github.com/cli/cli/issues/5075 + # See https://github.com/cli/cli/issues/5075 - uses: actions/checkout@v4 - name: Create issue run: | issue_num=$(gh issue list -s open -S "[SCHEDULED-BUILD] Build & Test failure" -L 1 --json number | jq 'if length == 0 then -1 else .[0].number end') - + body="**Build-and-Test Failure Report** - **Time of Failure**: $(date -u '+%B %d, %Y, %H:%M %Z') - **Commit**: [${{ github.sha }}](${{ github.server_url }}/${{ github.repository }}/commit/${{ github.sha }}) - **Action Run**: [View logs](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) - + The scheduled build-and-test triggered by cron has failed. Please investigate the logs and recent changes associated with this commit or rerun the workflow if you believe this is an error." - + if [[ $issue_num -eq -1 ]]; then gh issue create --repo ${{ github.repository }} --title "[SCHEDULED-BUILD] Build & Test failure" --body "$body" else diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml new file mode 100644 index 0000000..cc12949 --- /dev/null +++ b/.github/workflows/pre-commit.yml @@ -0,0 +1,43 @@ +name: Lint Check (pre-commit) + +on: + pull_request: + push: + +jobs: + pre-commit: + runs-on: ubuntu-latest + name: pre-commit + permissions: + contents: read + checks: write + issues: write + pull-requests: write + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: 3.13 + + - name: Get Changed Files + id: changed-files + uses: tj-actions/changed-files@v45 + + # See: + # https://github.com/tj-actions/changed-files?tab=readme-ov-file#using-local-git-directory- + - uses: pre-commit/action@v3.0.1 + with: + extra_args: --files ${{ steps.changed-files.outputs.all_changed_files }} + continue-on-error: true + + - name: suggester / pre-commit + if: ${{ github.event_name == 'pull_request' }} + uses: reviewdog/action-suggester@v1 + with: + tool_name: pre-commit + level: warning + reviewdog_flags: "-fail-level=error" diff --git a/.markdownlint.yaml b/.markdownlint.yaml new file mode 100644 index 0000000..81f5fcd --- /dev/null +++ b/.markdownlint.yaml @@ -0,0 +1,9 @@ +# MD033/no-inline-html : Inline HTML : https://github.com/DavidAnson/markdownlint/blob/v0.35.0/doc/md033.md +# Disable inline html linter is needed for
+MD033: false + +# MD013/line-length : Line length : https://github.com/DavidAnson/markdownlint/blob/v0.35.0/doc/md013.md +# Conforms to .clang-format ColumnLimit +# Update the comment in .clang-format if we no-longer tie these two column limits. +MD013: + line_length: 119 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..a26bab0 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,33 @@ +# See https://pre-commit.com for more information +# See https://pre-commit.com/hooks.html for more hooks +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v5.0.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-yaml + - id: check-added-large-files + + # Clang-format for C++ + # This brings in a portable version of clang-format. + # See also: https://github.com/ssciwr/clang-format-wheel + - repo: https://github.com/pre-commit/mirrors-clang-format + rev: v18.1.8 + hooks: + - id: clang-format + types_or: [c++, c] + + # CMake linting and formatting + - repo: https://github.com/BlankSpruce/gersemi + rev: 0.15.1 + hooks: + - id: gersemi + name: CMake linting + + # Markdown linting + # Config file: .markdownlint.yaml + - repo: https://github.com/igorshubovych/markdownlint-cli + rev: v0.42.0 + hooks: + - id: markdownlint diff --git a/CMakeLists.txt b/CMakeLists.txt index 70e94f5..53b7b09 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,10 +3,11 @@ cmake_minimum_required(VERSION 3.25) project( - beman.exemplar # CMake Project Name, which is also the name of the top-level - # targets (e.g., library, executable, etc.). - DESCRIPTION "A Beman library exemplar" - LANGUAGES CXX) + beman.exemplar # CMake Project Name, which is also the name of the top-level + # targets (e.g., library, executable, etc.). + DESCRIPTION "A Beman library exemplar" + LANGUAGES CXX +) list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake") include(CTest) @@ -15,20 +16,21 @@ include(CompilerFeatureTest) beman_check_range_support(COMPILER_SUPPORTS_RANGES) if(BUILD_TESTING) - enable_testing() + enable_testing() - # Fetch GoogleTest - FetchContent_Declare( - googletest - GIT_REPOSITORY https://github.com/google/googletest.git - GIT_TAG f8d7d77c06936315286eb55f8de22cd23c188571 # release-1.14.0 - EXCLUDE_FROM_ALL - ) - block() - set(INSTALL_GTEST OFF) # Disable GoogleTest installation - set(BUILD_TESTING OFF) # Disable GoogleTest tests - FetchContent_MakeAvailable(googletest) - endblock() + # Fetch GoogleTest + FetchContent_Declare( + googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG + f8d7d77c06936315286eb55f8de22cd23c188571 # release-1.14.0 + EXCLUDE_FROM_ALL + ) + block() + set(INSTALL_GTEST OFF) # Disable GoogleTest installation + set(BUILD_TESTING OFF) # Disable GoogleTest tests + FetchContent_MakeAvailable(googletest) + endblock() endif() add_subdirectory(src/beman/exemplar) diff --git a/CMakePresets.json b/CMakePresets.json new file mode 100644 index 0000000..ede9472 --- /dev/null +++ b/CMakePresets.json @@ -0,0 +1,121 @@ +{ + "version": 6, + "configurePresets": [ + { + "name": "_root-config", + "hidden": true, + "generator": "Ninja", + "binaryDir": "${sourceDir}/build/${presetName}", + "cacheVariables": { + "CMAKE_CXX_STANDARD": "17" + } + }, + { + "name": "_debug-base", + "hidden": true, + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug", + "CMAKE_CXX_FLAGS": "-fsanitize=address -fsanitize=pointer-compare -fsanitize=pointer-subtract -fsanitize=leak -fsanitize=undefined" + } + }, + { + "name": "_release-base", + "hidden": true, + "cacheVariables": { + "CMAKE_BUILD_TYPE": "RelWithDebInfo", + "CMAKE_CXX_FLAGS": "-O3" + } + }, + { + "name": "gcc-debug", + "displayName": "GCC Debug Build", + "inherits": [ + "_root-config", + "_debug-base" + ], + "cacheVariables": { + "CMAKE_CXX_COMPILER": "g++" + } + }, + { + "name": "gcc-release", + "displayName": "GCC Release Build", + "inherits": [ + "_root-config", + "_release-base" + ], + "cacheVariables": { + "CMAKE_CXX_COMPILER": "g++" + } + } + ], + "buildPresets": [ + { + "name": "gcc-debug", + "configurePreset": "gcc-debug" + }, + { + "name": "gcc-release", + "configurePreset": "gcc-release" + } + ], + "testPresets": [ + { + "name": "_test_base", + "hidden": true, + "output": { + "outputOnFailure": true + }, + "execution": { + "noTestsAction": "error", + "stopOnFailure": true + } + }, + { + "name": "gcc-debug", + "inherits": "_test_base", + "configurePreset": "gcc-debug" + }, + { + "name": "gcc-release", + "inherits": "_test_base", + "configurePreset": "gcc-release" + } + ], + "workflowPresets": [ + { + "name": "gcc-debug", + "steps": [ + { + "type": "configure", + "name": "gcc-debug" + }, + { + "type": "build", + "name": "gcc-debug" + }, + { + "type": "test", + "name": "gcc-debug" + } + ] + }, + { + "name": "gcc-release", + "steps": [ + { + "type": "configure", + "name": "gcc-release" + }, + { + "type": "build", + "name": "gcc-release" + }, + { + "type": "test", + "name": "gcc-release" + } + ] + } + ] +} diff --git a/README.md b/README.md index 66fcbb1..ee8e927 100644 --- a/README.md +++ b/README.md @@ -6,22 +6,24 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception ![Continuous Integration Tests](https://github.com/beman-project/exemplar/actions/workflows/ci_tests.yml/badge.svg) -`beman.exemplar` is a minimal C++ library conforming to [The Beman Standard](https://github.com/beman-project/beman/blob/main/docs/beman-standard.md). This can be used as a template for those intending to write Beman libraries. It may also find use as a minimal and modern C++ project structure. +`beman.exemplar` is a minimal C++ library conforming to [The Beman Standard](https://github.com/beman-project/beman/blob/main/docs/beman-standard.md). +This can be used as a template for those intending to write Beman libraries. +It may also find use as a minimal and modern C++ project structure. Implements: `std::identity` proposed in [Standard Library Concepts (P0898R3)](https://wg21.link/P0898R3). - ## Usage -`std::identity` is a function object type whose `operator()` returns its argument unchanged. `std::identity` serves as the default projection in constrained algorithms. Its direct usage is usually not needed. +`std::identity` is a function object type whose `operator()` returns its argument unchanged. +`std::identity` serves as the default projection in constrained algorithms. +Its direct usage is usually not needed. ### Usage: default projection in constrained algorithms The following code snippet illustrates how we can achieve a default projection using `beman::exemplar::identity`: - ```cpp -#include +#include namespace exe = beman::exemplar; @@ -73,7 +75,7 @@ int main() ``` -Full runable examples can be found in `examples/` (e.g., [./examples/identity_as_default_projection.cpp.cpp](./examples/identity_as_default_projection.cpp.cpp)). +Full runnable examples can be found in `examples/` (e.g., [./examples/identity_as_default_projection.cpp](./examples/identity_as_default_projection.cpp)). ## Building beman.exemplar @@ -131,53 +133,135 @@ apt-get install \ ### How to build beman.exemplar -This project strives to be as normal and simple a CMake project as possible. This build workflow in particular will work, producing a static `libbeman.exemplar.a` library, ready to package with its headers: +This project strives to be as normal and simple a CMake project as possible. +This build workflow in particular will work, +producing a static `libbeman.exemplar.a` library, ready to package with its headers: ```shell -cmake -B build -S . -DCMAKE_CXX_STANDARD=20 -cmake --build build -ctest --test-dir build -cmake --install build --prefix /opt/beman.exemplar +cmake --workflow --preset gcc-debug +cmake --workflow --preset gcc-release +cmake --install build/gcc-release --prefix /opt/beman.exemplar ```
Build beman.exemplar (verbose logs) ```shell -# Configure beman.exemplar. -$ cmake -B build -S . -DCMAKE_CXX_STANDARD=20 --- The CXX compiler identification is GNU 13.2.0 +# Configure beman.exemplar via gcc-debug workflow for development. +$ cmake --workflow --preset gcc-debug +Executing workflow step 1 of 3: configure preset "gcc-debug" + +Preset CMake variables: + + CMAKE_BUILD_TYPE="Debug" + CMAKE_CXX_COMPILER="g++" + CMAKE_CXX_FLAGS="-fsanitize=address -fsanitize=pointer-compare -fsanitize=pointer-subtract -fsanitize=leak -fsanitize=undefined" + CMAKE_CXX_STANDARD="20" + +-- The CXX compiler identification is GNU 11.4.0 -- Detecting CXX compiler ABI info -- Detecting CXX compiler ABI info - done --- Check for working CXX compiler: /usr/bin/c++ - skipped +-- Check for working CXX compiler: /usr/bin/g++ - skipped -- Detecting CXX compile features -- Detecting CXX compile features - done --- Configuring done (0.1s) --- Generating done (0.0s) --- Build files have been written to: /path/to/repo/build - -# Build beman.exemplar. -$ cmake --build build -[ 10%] Building CXX object src/beman/exemplar/CMakeFiles/beman.exemplar.dir/identity.cpp.o -[ 20%] Linking CXX static library libbeman.exemplar.a -[ 20%] Built target beman.exemplar -[ 30%] Building CXX object _deps/googletest-build/googletest/CMakeFiles/gtest.dir/src/gtest-all.cc.o -[ 40%] Linking CXX static library ../../../lib/libgtest.a -[ 40%] Built target gtest -[ 50%] Building CXX object _deps/googletest-build/googletest/CMakeFiles/gtest_main.dir/src/gtest_main.cc.o -[ 60%] Linking CXX static library ../../../lib/libgtest_main.a -[ 60%] Built target gtest_main -[ 70%] Building CXX object src/beman/exemplar/tests/CMakeFiles/beman.exemplar.Test.dir/identity.t.cpp.o -[ 80%] Linking CXX executable beman.exemplar.Test -[ 80%] Built target beman.exemplar.Test -[ 90%] Building CXX object examples/CMakeFiles/identity_usage.dir/identity_usage.cpp.o -[100%] Linking CXX executable identity_usage -[100%] Built target identity_usage - -# Run beman.exemplar tests. -$ ctest --test-dir build -Internal ctest changing into directory: /path/to/your/repo/build -Test project /path/to/your/repo/build +-- The C compiler identification is GNU 11.4.0 +-- Detecting C compiler ABI info +-- Detecting C compiler ABI info - done +-- Check for working C compiler: /usr/bin/cc - skipped +-- Detecting C compile features +-- Detecting C compile features - done +-- Found Python3: /usr/bin/python3.10 (found version "3.10.12") found components: Interpreter +-- Performing Test CMAKE_HAVE_LIBC_PTHREAD +-- Performing Test CMAKE_HAVE_LIBC_PTHREAD - Success +-- Found Threads: TRUE +-- Configuring done +-- Generating done +-- Build files have been written to: /home/runner/work/exemplar/exemplar/build/gcc-debug + +Executing workflow step 2 of 3: build preset "gcc-debug" + +[1/14] Building CXX object src/beman/exemplar/CMakeFiles/beman.exemplar.dir/identity.cpp.o +[2/14] Linking CXX static library src/beman/exemplar/libbeman.exemplar.a +[3/14] Building CXX object examples/CMakeFiles/beman.exemplar.examples.identity_direct_usage.dir/identity_direct_usage.cpp.o +[4/14] Linking CXX executable examples/beman.exemplar.examples.identity_direct_usage +[5/14] Building CXX object _deps/googletest-build/googletest/CMakeFiles/gtest_main.dir/src/gtest_main.cc.o +[6/14] Building CXX object src/beman/exemplar/CMakeFiles/beman.exemplar.tests.dir/identity.t.cpp.o +[7/14] Building CXX object _deps/googletest-build/googlemock/CMakeFiles/gmock_main.dir/src/gmock_main.cc.o +[8/14] Building CXX object _deps/googletest-build/googlemock/CMakeFiles/gmock.dir/src/gmock-all.cc.o +[9/14] Building CXX object _deps/googletest-build/googletest/CMakeFiles/gtest.dir/src/gtest-all.cc.o +[10/14] Linking CXX static library lib/libgtest.a +[11/14] Linking CXX static library lib/libgtest_main.a +[12/14] Linking CXX static library lib/libgmock.a +[13/14] Linking CXX static library lib/libgmock_main.a +[14/14] Linking CXX executable src/beman/exemplar/beman.exemplar.tests + +Executing workflow step 3 of 3: test preset "gcc-debug" + +Test project /home/runner/work/exemplar/exemplar/build/gcc-debug + Start 1: IdentityTest.call_identity_with_int +1/4 Test #1: IdentityTest.call_identity_with_int ........... Passed 0.13 sec + Start 2: IdentityTest.call_identity_with_custom_type +2/4 Test #2: IdentityTest.call_identity_with_custom_type ... Passed 0.01 sec + Start 3: IdentityTest.compare_std_vs_beman +3/4 Test #3: IdentityTest.compare_std_vs_beman ............. Passed 0.01 sec + Start 4: IdentityTest.check_is_transparent +4/4 Test #4: IdentityTest.check_is_transparent ............. Passed 0.01 sec + +100% tests passed, 0 tests failed out of 4 + +Total Test time (real) = 0.18 sec + +# Configure beman.exemplar via gcc-release workflow for direct usage. +$ cmake --workflow --preset gcc-release +Executing workflow step 1 of 3: configure preset "gcc-release" + +Preset CMake variables: + + CMAKE_BUILD_TYPE="RelWithDebInfo" + CMAKE_CXX_COMPILER="g++" + CMAKE_CXX_FLAGS="-O3" + CMAKE_CXX_STANDARD="20" + +-- The CXX compiler identification is GNU 11.4.0 +-- Detecting CXX compiler ABI info +-- Detecting CXX compiler ABI info - done +-- Check for working CXX compiler: /usr/bin/g++ - skipped +-- Detecting CXX compile features +-- Detecting CXX compile features - done +-- The C compiler identification is GNU 11.4.0 +-- Detecting C compiler ABI info +-- Detecting C compiler ABI info - done +-- Check for working C compiler: /usr/bin/cc - skipped +-- Detecting C compile features +-- Detecting C compile features - done +-- Found Python3: /usr/bin/python3.10 (found version "3.10.12") found components: Interpreter +-- Performing Test CMAKE_HAVE_LIBC_PTHREAD +-- Performing Test CMAKE_HAVE_LIBC_PTHREAD - Success +-- Found Threads: TRUE +-- Configuring done +-- Generating done +-- Build files have been written to: /home/runner/work/exemplar/exemplar/build/gcc-release + +Executing workflow step 2 of 3: build preset "gcc-release" + +[1/14] Building CXX object src/beman/exemplar/CMakeFiles/beman.exemplar.dir/identity.cpp.o +[2/14] Linking CXX static library src/beman/exemplar/libbeman.exemplar.a +[3/14] Building CXX object examples/CMakeFiles/beman.exemplar.examples.identity_direct_usage.dir/identity_direct_usage.cpp.o +[4/14] Linking CXX executable examples/beman.exemplar.examples.identity_direct_usage +[5/14] Building CXX object _deps/googletest-build/googletest/CMakeFiles/gtest_main.dir/src/gtest_main.cc.o +[6/14] Building CXX object src/beman/exemplar/CMakeFiles/beman.exemplar.tests.dir/identity.t.cpp.o +[7/14] Building CXX object _deps/googletest-build/googlemock/CMakeFiles/gmock_main.dir/src/gmock_main.cc.o +[8/14] Building CXX object _deps/googletest-build/googlemock/CMakeFiles/gmock.dir/src/gmock-all.cc.o +[9/14] Building CXX object _deps/googletest-build/googletest/CMakeFiles/gtest.dir/src/gtest-all.cc.o +[10/14] Linking CXX static library lib/libgtest.a +[11/14] Linking CXX static library lib/libgtest_main.a +[12/14] Linking CXX static library lib/libgmock.a +[13/14] Linking CXX executable src/beman/exemplar/beman.exemplar.tests +[14/14] Linking CXX static library lib/libgmock_main.a + +Executing workflow step 3 of 3: test preset "gcc-release" + +Test project /home/runner/work/exemplar/exemplar/build/gcc-release Start 1: IdentityTest.call_identity_with_int 1/4 Test #1: IdentityTest.call_identity_with_int ........... Passed 0.00 sec Start 2: IdentityTest.call_identity_with_custom_type @@ -191,9 +275,8 @@ Test project /path/to/your/repo/build Total Test time (real) = 0.01 sec - # Run examples. -$ build/exemplar/beman.exemplar.examples.identity_direct_usage +$ build/gcc-release/examples/beman.exemplar.examples.identity_direct_usage 2024 ``` @@ -205,14 +288,12 @@ $ build/exemplar/beman.exemplar.examples.identity_direct_usage ```shell # Install build artifacts from `build` directory into `opt/beman.exemplar` path. -$ cmake --install build --prefix /opt/beman.exemplar --- Install configuration: "" +$ cmake --install build/gcc-release --prefix /opt/beman.exemplar +-- Install configuration: "RelWithDebInfo" -- Up-to-date: /opt/beman.exemplar/lib/libbeman.exemplar.a --- Up-to-date: /opt/beman.exemplar/include --- Up-to-date: /opt/beman.exemplar/include/beman --- Up-to-date: /opt/beman.exemplar/include/beman/exemplar -- Up-to-date: /opt/beman.exemplar/include/beman/exemplar/identity.hpp + # Check tree. $ tree /opt/beman.exemplar /opt/beman.exemplar @@ -223,7 +304,7 @@ $ tree /opt/beman.exemplar └── lib └── libbeman.exemplar.a -5 directories, 2 files +4 directories, 2 files ```
@@ -231,7 +312,8 @@ $ tree /opt/beman.exemplar
Disable tests build -To build this project with tests disabled (and their dependencies), simply use `BUILD_TESTING=OFF` as documented in upstream [CMake documentation](https://cmake.org/cmake/help/latest/module/CTest.html): +To build this project with tests disabled (and their dependencies), +simply use `BUILD_TESTING=OFF` as documented in upstream [CMake documentation](https://cmake.org/cmake/help/latest/module/CTest.html): ```shell cmake -B build -S . -DBUILD_TESTING=OFF @@ -245,7 +327,8 @@ cmake -B build -S . -DBUILD_TESTING=OFF Use beman.exemplar directly from C++ -If you want to use `beman.exemplar` from your project, you can include `beman/exemplar/*.hpp` files from your C++ source files +If you want to use `beman.exemplar` from your project, +you can include `beman/exemplar/*.hpp` files from your C++ source files ```cpp #include @@ -273,7 +356,8 @@ For CMake based projects, you will need to use the `beman.exemplar` CMake module find_package(beman.exemplar REQUIRED) ``` -You will also need to add `beman::exemplar` to the link libraries of any libraries or executables that include `beman/exemplar/*.hpp` in their source or header file. +You will also need to add `beman::exemplar` +to the link libraries of any libraries or executables that include `beman/exemplar/*.hpp` in their source or header file. ```cmake target_link_libraries(yourlib PUBLIC beman::exemplar) @@ -286,7 +370,8 @@ target_link_libraries(yourlib PUBLIC beman::exemplar) -Build systems that support `pkg-config` by providing a `beman.exemplar.pc` file. Build systems that support interoperation via `pkg-config` should be able to detect `beman.exemplar` for you automatically. +Build systems that support `pkg-config` by providing a `beman.exemplar.pc` file. +Build systems that support interoperation via `pkg-config` should be able to detect `beman.exemplar` for you automatically.
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 1339a95..6b4b7ab 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -8,7 +8,7 @@ else() endif() foreach(example ${ALL_EXAMPLES}) - add_executable(beman.exemplar.examples.${example}) - target_sources(beman.exemplar.examples.${example} PRIVATE ${example}.cpp) - target_link_libraries(beman.exemplar.examples.${example} beman::exemplar) + add_executable(beman.exemplar.examples.${example}) + target_sources(beman.exemplar.examples.${example} PRIVATE ${example}.cpp) + target_link_libraries(beman.exemplar.examples.${example} beman::exemplar) endforeach() diff --git a/examples/identity_as_default_projection.cpp b/examples/identity_as_default_projection.cpp index a2139b7..eecabac 100644 --- a/examples/identity_as_default_projection.cpp +++ b/examples/identity_as_default_projection.cpp @@ -16,15 +16,13 @@ namespace exe = beman::exemplar; // Class with a pair of values. -struct Pair -{ - int n; +struct Pair { + int n; std::string s; // Output the pair in the form {n, s}. // Used by the range-printer if no custom projection is provided (default: identity projection). - friend std::ostream &operator<<(std::ostream &os, const Pair &p) - { + friend std::ostream& operator<<(std::ostream& os, const Pair& p) { return os << "Pair" << '{' << p.n << ", " << p.s << '}'; } }; @@ -33,37 +31,28 @@ struct Pair // All the elements of the range are printed in the form {element1, element2, ...}. // e.g., pairs with identity: Pair{1, one}, Pair{2, two}, Pair{3, three} // e.g., pairs with custom projection: {1:one, 2:two, 3:three} -template -void print_helper(const std::string_view rem, R &&range, Projection projection) -{ +template +void print_helper(const std::string_view rem, R&& range, Projection projection) { std::cout << rem << '{'; - std::ranges::for_each( - range, - [O = 0](const auto &o) mutable - { std::cout << (O++ ? ", " : "") << o; }, - projection); + std::ranges::for_each(range, [O = 0](const auto& o) mutable { std::cout << (O++ ? ", " : "") << o; }, projection); std::cout << "}\n"; }; // Print wrapper with exe::identity. template // <- Notice the default projection. -void print_beman(const std::string_view rem, R &&range, Projection projection = {}) -{ +void print_beman(const std::string_view rem, R&& range, Projection projection = {}) { print_helper(rem, range, projection); } // Print wrapper with std::identity. template // <- Notice the default projection. -void print_std(const std::string_view rem, R &&range, Projection projection = {}) -{ +void print_std(const std::string_view rem, R&& range, Projection projection = {}) { print_helper(rem, range, projection); } -int main() -{ +int main() { // A vector of pairs to print. const std::vector pairs = { {1, "one"}, @@ -78,12 +67,8 @@ int main() // Print the pairs using a custom projection. std::cout << "Custom projection:\n"; - print_beman("\tpairs with beman: ", pairs, - [](const auto &p) - { return std::to_string(p.n) + ':' + p.s; }); - print_std("\tpairs with std: ", pairs, - [](const auto &p) - { return std::to_string(p.n) + ':' + p.s; }); + print_beman("\tpairs with beman: ", pairs, [](const auto& p) { return std::to_string(p.n) + ':' + p.s; }); + print_std("\tpairs with std: ", pairs, [](const auto& p) { return std::to_string(p.n) + ':' + p.s; }); return 0; } diff --git a/include/beman/exemplar/identity.hpp b/include/beman/exemplar/identity.hpp index 0a121eb..9463125 100644 --- a/include/beman/exemplar/identity.hpp +++ b/include/beman/exemplar/identity.hpp @@ -27,16 +27,10 @@ namespace beman::exemplar { struct __is_transparent; // not defined // A function object that returns its argument unchanged. -struct identity -{ +struct identity { // Returns `t`. template -#if defined(__cpp_constexpr) - constexpr -#endif - T && - operator()(T &&t) const noexcept - { + constexpr T&& operator()(T&& t) const noexcept { return std::forward(t); } diff --git a/src/beman/exemplar/CMakeLists.txt b/src/beman/exemplar/CMakeLists.txt index da3d195..c887c7f 100644 --- a/src/beman/exemplar/CMakeLists.txt +++ b/src/beman/exemplar/CMakeLists.txt @@ -5,32 +5,34 @@ add_library(beman::exemplar ALIAS beman.exemplar) target_sources(beman.exemplar PRIVATE identity.cpp) -set(INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../../include/) - target_sources( - beman.exemplar PUBLIC - FILE_SET HEADERS - BASE_DIRS ${INCLUDE_DIR} - FILES ${INCLUDE_DIR}/beman/exemplar/identity.hpp) + beman.exemplar + PUBLIC + FILE_SET HEADERS + BASE_DIRS ${PROJECT_SOURCE_DIR}/include + FILES ${PROJECT_SOURCE_DIR}/include/beman/exemplar/identity.hpp +) set_target_properties(beman.exemplar PROPERTIES VERIFY_INTERFACE_HEADER_SETS ON) install( - TARGETS beman.exemplar - EXPORT beman.exemplar - DESTINATION $<$:debug/>${CMAKE_INSTALL_LIBDIR} - RUNTIME DESTINATION $<$:debug/>${CMAKE_INSTALL_BINDIR} - FILE_SET HEADERS - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + TARGETS beman.exemplar + EXPORT beman.exemplar + DESTINATION + $<$:debug/>${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION $<$:debug/>${CMAKE_INSTALL_BINDIR} + FILE_SET HEADERS DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} ) if(BUILD_TESTING) - include(GoogleTest) + include(GoogleTest) - add_executable(beman.exemplar.tests) - target_sources(beman.exemplar.tests PRIVATE identity.t.cpp) - target_link_libraries(beman.exemplar.tests - PRIVATE beman::exemplar GTest::gtest GTest::gtest_main) + add_executable(beman.exemplar.tests) + target_sources(beman.exemplar.tests PRIVATE identity.t.cpp) + target_link_libraries( + beman.exemplar.tests + PRIVATE beman::exemplar GTest::gtest GTest::gtest_main + ) - gtest_add_tests(beman.exemplar.tests "" AUTO) + gtest_add_tests(beman.exemplar.tests "" AUTO) endif() diff --git a/src/beman/exemplar/identity.t.cpp b/src/beman/exemplar/identity.t.cpp index 64a687c..7f68fdf 100644 --- a/src/beman/exemplar/identity.t.cpp +++ b/src/beman/exemplar/identity.t.cpp @@ -9,51 +9,43 @@ namespace exe = beman::exemplar; -TEST(IdentityTest, call_identity_with_int) -{ - for (int i = -100; i < 100; ++i) - { +TEST(IdentityTest, call_identity_with_int) { + for (int i = -100; i < 100; ++i) { EXPECT_EQ(i, exe::identity()(i)); } } -TEST(IdentityTest, call_identity_with_custom_type) -{ - struct S - { +TEST(IdentityTest, call_identity_with_custom_type) { + struct S { int i; }; - for (int i = -100; i < 100; ++i) - { + for (int i = -100; i < 100; ++i) { const S s{i}; const S s_id = exe::identity()(s); EXPECT_EQ(s.i, s_id.i); } } -TEST(IdentityTest, compare_std_vs_beman) -{ +TEST(IdentityTest, compare_std_vs_beman) { // Requires: std::identity support. #if defined(__cpp_lib_identity) std::identity std_id; exe::identity beman_id; - for (int i = -100; i < 100; ++i) - { + for (int i = -100; i < 100; ++i) { EXPECT_EQ(std_id(i), beman_id(i)); } #endif } -TEST(IdentityTest, check_is_transparent) -{ +TEST(IdentityTest, check_is_transparent) { // Requires: transparent operators support. #if defined(__cpp_lib_transparent_operators) exe::identity id; const auto container = {1, 2, 3, 4, 5}; - auto it = std::find(std::begin(container), std::end(container), 3); + auto it = std::find(std::begin(container), std::end(container), 3); EXPECT_EQ(3, *it); auto it_with_id = std::find(std::begin(container), std::end(container), id(3)); EXPECT_EQ(3, *it_with_id);