From bc0b8a9ea8a713e93adc3fa0cb45896574208e19 Mon Sep 17 00:00:00 2001 From: Edward Nolan Date: Mon, 27 Apr 2026 21:14:37 +0000 Subject: [PATCH] Refresh bounds_test to use latest beman.exemplar template - Adopt exemplar's infra/ vendored infrastructure package - Replace manual install logic with beman_install_library - Replace vcpkg/FetchContent with lockfile.json dependency management - Migrate CI from inline matrix to exemplar's JSON-configuration-based reusable workflows (infra-workflows@1.5.3) - Remove C++17 from CI matrix (minimum is C++20) - Remove AppleClang from CI matrix (not tested in original) - Add exemplar-standard CONTRIBUTING.md, CMakePresets.json, and pre-commit configuration - Update README with exemplar boilerplate while preserving bounds_test-specific documentation Co-Authored-By: Claude Opus 4.6 --- .clang-format | 224 +++++++----- .devcontainer/devcontainer.json | 16 - .exemplar_version | 1 + .gitattributes | 5 + .github/CODEOWNERS | 3 +- .github/actions/cmake-build-test/action.yml | 78 ----- .github/actions/setup-environment/action.yml | 17 - .github/pull_request_template.md | 72 +--- .github/workflows/ci_tests.yml | 322 +++++++---------- .github/workflows/pre-commit-check.yml | 19 ++ .github/workflows/pre-commit-update.yml | 15 + .gitignore | 17 +- .markdownlint.yaml | 1 + .pre-commit-config.yaml | 40 ++- CMakeLists.txt | 87 ++--- CMakePresets.json | 18 +- CONTRIBUTING.md | 111 ++++++ README.md | 157 ++++++--- cmake/bootstrap_vcpkg.cmake | 47 --- examples/CMakeLists.txt | 19 +- include/beman/bounds_test/CMakeLists.txt | 1 + include/beman/bounds_test/bounds_test.hpp | 106 +++--- infra/.beman_submodule | 3 + infra/.github/CODEOWNERS | 1 + .../.github}/workflows/pre-commit.yml | 2 + ...reusable-beman-create-issue-when-fault.yml | 28 ++ infra/.gitignore | 59 ++++ infra/.pre-commit-config.yaml | 21 ++ infra/LICENSE | 219 ++++++++++++ infra/README.md | 88 +++++ infra/cmake/BuildTelemetry.cmake | 4 + infra/cmake/BuildTelemetryConfig.cmake | 58 ++++ infra/cmake/Config.cmake.in | 12 + .../cmake}/appleclang-toolchain.cmake | 5 + infra/cmake/beman-install-library.cmake | 323 ++++++++++++++++++ {cmake => infra/cmake}/gnu-toolchain.cmake | 3 + infra/cmake/llvm-libc++-toolchain.cmake | 20 ++ {cmake => infra/cmake}/llvm-toolchain.cmake | 3 + {cmake => infra/cmake}/msvc-toolchain.cmake | 8 +- infra/cmake/telemetry.sh | 118 +++++++ infra/cmake/use-fetch-content.cmake | 188 ++++++++++ lockfile.json | 10 + tests/beman/bounds_test/CMakeLists.txt | 11 +- vcpkg.json | 20 -- 44 files changed, 1840 insertions(+), 740 deletions(-) delete mode 100644 .devcontainer/devcontainer.json create mode 100644 .exemplar_version create mode 100644 .gitattributes delete mode 100644 .github/actions/cmake-build-test/action.yml delete mode 100644 .github/actions/setup-environment/action.yml create mode 100644 .github/workflows/pre-commit-check.yml create mode 100644 .github/workflows/pre-commit-update.yml create mode 100644 CONTRIBUTING.md delete mode 100644 cmake/bootstrap_vcpkg.cmake create mode 100644 include/beman/bounds_test/CMakeLists.txt create mode 100644 infra/.beman_submodule create mode 100644 infra/.github/CODEOWNERS rename {.github => infra/.github}/workflows/pre-commit.yml (98%) create mode 100644 infra/.github/workflows/reusable-beman-create-issue-when-fault.yml create mode 100644 infra/.gitignore create mode 100644 infra/.pre-commit-config.yaml create mode 100644 infra/LICENSE create mode 100644 infra/README.md create mode 100755 infra/cmake/BuildTelemetry.cmake create mode 100755 infra/cmake/BuildTelemetryConfig.cmake create mode 100644 infra/cmake/Config.cmake.in rename {cmake => infra/cmake}/appleclang-toolchain.cmake (85%) create mode 100644 infra/cmake/beman-install-library.cmake rename {cmake => infra/cmake}/gnu-toolchain.cmake (90%) create mode 100644 infra/cmake/llvm-libc++-toolchain.cmake rename {cmake => infra/cmake}/llvm-toolchain.cmake (90%) rename {cmake => infra/cmake}/msvc-toolchain.cmake (87%) create mode 100755 infra/cmake/telemetry.sh create mode 100644 infra/cmake/use-fetch-content.cmake create mode 100644 lockfile.json delete mode 100644 vcpkg.json diff --git a/.clang-format b/.clang-format index 2d264bf..74f95dc 100644 --- a/.clang-format +++ b/.clang-format @@ -1,85 +1,102 @@ --- -Language: Cpp -BasedOnStyle: LLVM +Language: Cpp AccessModifierOffset: -2 AlignAfterOpenBracket: Align AlignArrayOfStructures: None +AlignConsecutiveAssignments: + Enabled: true + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + PadOperators: true AlignConsecutiveBitFields: - Enabled: true + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + PadOperators: false +AlignConsecutiveDeclarations: + Enabled: true AcrossEmptyLines: false - AcrossComments: true - AlignCompound: true - PadOperators: true + AcrossComments: false + AlignCompound: false + PadOperators: true AlignConsecutiveMacros: - Enabled: true + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + PadOperators: false +AlignConsecutiveShortCaseStatements: + Enabled: false AcrossEmptyLines: false - AcrossComments: false - AlignCompound: true - PadOperators: true + AcrossComments: false + AlignCaseColons: false AlignEscapedNewlines: Left -AlignOperands: AlignAfterOperator -AlignTrailingComments: true -AllowAllArgumentsOnNextLine: false -AllowAllParametersOfDeclarationOnNextLine: false -AllowShortEnumsOnASingleLine: false -AllowShortBlocksOnASingleLine: Empty -AllowShortCaseLabelsOnASingleLine: true -AllowShortFunctionsOnASingleLine: Inline +AlignOperands: Align +AlignTrailingComments: + Kind: Always + OverEmptyLines: 0 +AllowAllArgumentsOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: Never +AllowShortCaseLabelsOnASingleLine: false +AllowShortEnumsOnASingleLine: true +AllowShortFunctionsOnASingleLine: All +AllowShortIfStatementsOnASingleLine: Never AllowShortLambdasOnASingleLine: All -AllowShortIfStatementsOnASingleLine: WithoutElse -AllowShortLoopsOnASingleLine: true +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None AlwaysBreakAfterReturnType: None -AlwaysBreakBeforeMultilineStrings: true +AlwaysBreakBeforeMultilineStrings: false AlwaysBreakTemplateDeclarations: Yes AttributeMacros: - __capability BinPackArguments: false BinPackParameters: false +BitFieldColonSpacing: Both BraceWrapping: - AfterCaseLabel: false - AfterClass: false + AfterCaseLabel: false + AfterClass: false AfterControlStatement: Never - AfterEnum: false - AfterFunction: false - AfterNamespace: false - AfterObjCDeclaration: false - AfterStruct: false - AfterUnion: false + AfterEnum: false AfterExternBlock: false - BeforeCatch: false - BeforeElse: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + BeforeCatch: false + BeforeElse: false BeforeLambdaBody: false - BeforeWhile: false - IndentBraces: false + BeforeWhile: false + IndentBraces: false SplitEmptyFunction: true SplitEmptyRecord: true SplitEmptyNamespace: true +BreakAfterAttributes: Never +BreakAfterJavaFieldAnnotations: false +BreakArrays: true BreakBeforeBinaryOperators: None BreakBeforeConceptDeclarations: Always -BreakBeforeBraces: Attach -BreakBeforeInheritanceComma: false -BreakInheritanceList: AfterColon +BreakBeforeBraces: Custom +BreakBeforeInlineASMColon: OnlyMultiline BreakBeforeTernaryOperators: true -BreakConstructorInitializersBeforeComma: false -BreakConstructorInitializers: AfterColon -BreakAfterJavaFieldAnnotations: false +BreakConstructorInitializers: BeforeColon +BreakInheritanceList: BeforeColon BreakStringLiterals: true -ColumnLimit: 119 -CommentPragmas: "^ IWYU pragma:" -QualifierAlignment: Leave +# Please update .markdownlint.yaml if this line is to be updated +ColumnLimit: 119 +CommentPragmas: '^ IWYU pragma:' CompactNamespaces: false ConstructorInitializerIndentWidth: 4 ContinuationIndentWidth: 4 Cpp11BracedListStyle: true -DeriveLineEnding: true DerivePointerAlignment: false -DisableFormat: false +DisableFormat: false EmptyLineAfterAccessModifier: Never EmptyLineBeforeAccessModifier: LogicalBlock ExperimentalAutoDetectBinPacking: false -PackConstructorInitializers: BinPack -ConstructorInitializerAllOnOneLineOrOnePerLine: false -AllowAllConstructorInitializersOnNextLine: true FixNamespaceComments: true ForEachMacros: - foreach @@ -87,46 +104,57 @@ ForEachMacros: - BOOST_FOREACH IfMacros: - KJ_IF_MAYBE -IncludeBlocks: Preserve +IncludeBlocks: Preserve IncludeCategories: - - Regex: '^"(llvm|llvm-c|clang|clang-c)/' - Priority: 2 - SortPriority: 0 - CaseSensitive: false - - Regex: '^(<|"(gtest|gmock|isl|json)/)' - Priority: 3 - SortPriority: 0 - CaseSensitive: false - - Regex: ".*" - Priority: 1 - SortPriority: 0 - CaseSensitive: false -IncludeIsMainRegex: "(Test)?$" -IncludeIsMainSourceRegex: "" + - 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 -IndentCaseLabels: false IndentCaseBlocks: false -IndentGotoLabels: true -IndentPPDirectives: None +IndentCaseLabels: false IndentExternBlock: AfterExternBlock +IndentGotoLabels: true +IndentPPDirectives: BeforeHash IndentRequiresClause: true -IndentWidth: 2 +IndentWidth: 4 IndentWrappedFunctionNames: false -InsertBraces: 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 -MacroBlockBegin: "" -MacroBlockEnd: "" +LineEnding: DeriveLF +MacroBlockBegin: '' +MacroBlockEnd: '' MaxEmptyLinesToKeep: 1 NamespaceIndentation: None ObjCBinPackProtocolList: Auto -ObjCBlockIndentWidth: 2 +ObjCBlockIndentWidth: 4 ObjCBreakBeforeNestedBlockParam: true ObjCSpaceAfterProperty: false ObjCSpaceBeforeProtocolList: true +PackConstructorInitializers: NextLine PenaltyBreakAssignment: 2 PenaltyBreakBeforeFirstCallParameter: 19 PenaltyBreakComment: 300 @@ -135,66 +163,80 @@ PenaltyBreakOpenParenthesis: 0 PenaltyBreakString: 1000 PenaltyBreakTemplateDeclaration: 10 PenaltyExcessCharacter: 1000000 -PenaltyReturnTypeOnItsOwnLine: 60 PenaltyIndentedWhitespace: 0 +PenaltyReturnTypeOnItsOwnLine: 60 PointerAlignment: Left -PPIndentWidth: -1 -ReferenceAlignment: Left -ReflowComments: true +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: CaseSensitive +SortIncludes: Never SortJavaStaticImport: Before -SortUsingDeclarations: true +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 + AfterIfMacros: true AfterOverloadedOperator: false AfterRequiresInClause: false AfterRequiresInExpression: false BeforeNonEmptyParentheses: false -SpaceAroundPointerQualifiers: Default SpaceBeforeRangeBasedForLoopColon: true +SpaceBeforeSquareBrackets: false SpaceInEmptyBlock: false -SpaceInEmptyParentheses: false SpacesBeforeTrailingComments: 1 -SpacesInAngles: Never -SpacesInConditionalStatement: false +SpacesInAngles: Never SpacesInContainerLiterals: true -SpacesInCStyleCastParentheses: false SpacesInLineCommentPrefix: - Minimum: 1 - Maximum: -1 -SpacesInParentheses: false + Minimum: 1 + Maximum: -1 +SpacesInParens: Never +SpacesInParensOptions: + InCStyleCasts: false + InConditionalStatements: false + InEmptyParentheses: false + Other: false SpacesInSquareBrackets: false -SpaceBeforeSquareBrackets: false -BitFieldColonSpacing: Both -Standard: Latest +Standard: Auto StatementAttributeLikeMacros: - Q_EMIT StatementMacros: - Q_UNUSED - QT_REQUIRE_VERSION -TabWidth: 8 -UseCRLF: false -UseTab: Never +TabWidth: 8 +UseTab: Never +VerilogBreakBetweenInstancePorts: true WhitespaceSensitiveMacros: - STRINGIZE - PP_STRINGIZE - BOOST_PP_STRINGIZE - NS_SWIFT_NAME - CF_SWIFT_NAME +... diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json deleted file mode 100644 index e1662e7..0000000 --- a/.devcontainer/devcontainer.json +++ /dev/null @@ -1,16 +0,0 @@ -// For format details, see https://aka.ms/devcontainer.json. For config options, see the -// README at: https://github.com/devcontainers/templates/tree/main/src/cpp - -{ - "name": "Beman Project Generic Devcontainer", - "image": "ghcr.io/bemanproject/devcontainers-gcc:14", - "postCreateCommand": "pre-commit", - "customizations": { - "vscode": { - "extensions": [ - "ms-vscode.cpptools", - "ms-vscode.cmake-tools" - ] - } - } -} diff --git a/.exemplar_version b/.exemplar_version new file mode 100644 index 0000000..85f7dd3 --- /dev/null +++ b/.exemplar_version @@ -0,0 +1 @@ +9883c77ef9f6fddbee09565347ab611c4dde63d0 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..793dce7 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,5 @@ +infra/** linguist-vendored +cookiecutter/** linguist-vendored +*.bib -linguist-detectable +*.tex -linguist-detectable +papers/* linguist-documentation diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 30973e7..04e7635 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,4 +1,3 @@ # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -# Codeowners for reviews on PRs -* @rishyak @nickelpro +* @nickelpro diff --git a/.github/actions/cmake-build-test/action.yml b/.github/actions/cmake-build-test/action.yml deleted file mode 100644 index 60c4a0a..0000000 --- a/.github/actions/cmake-build-test/action.yml +++ /dev/null @@ -1,78 +0,0 @@ -# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -name: "CMake Build Test" -description: "" -inputs: - cpp_version: - description: "cpp version" - required: true - toolchain_file: - description: "toolchain file" - required: true - cmake_extra_args: - description: "extra cmake arguments" - default: "" - disable_test: - description: "disable test" - default: "false" -runs: - using: "composite" - steps: - - name: Print installed software - shell: bash - run: | - echo "Build system:" - cmake --version - ninja --version - - name: Get pkg-config - shell: bash - if: ${{ !contains(runner.os, 'Windows') }} - run: | - sudo apt-get install -y pkg-config zip - - name: Restore vcpkg cache - id: vcpkg-cache - uses: TAServers/vcpkg-cache@v3 - with: - token: ${{ github.token }} - - name: Configure CMake - shell: bash - env: - CMAKE_GENERATOR: "Ninja Multi-Config" - VCPKG_BINARY_SOURCES: "clear;files,${{ steps.vcpkg-cache.outputs.path }},readwrite" - run: | - cmake \ - -B build \ - -S . \ - -DCMAKE_CXX_STANDARD=${{ inputs.cpp_version }} \ - -DVCPKG_CHAINLOAD_TOOLCHAIN_FILE="$(pwd)/${{ inputs.toolchain_file }}" \ - -DBEMAN_BOUNDS_TEST_BOOTSTRAP_VCPKG=ON \ - ${{ matrix.cmake_args.args }} - - name: Build Release - shell: bash - run: | - cmake --build build --config Release --parallel --verbose - cmake --build build --config Release --target all_verify_interface_header_sets - - name: Prepare Install Directory - if: ${{ !startsWith(matrix.platform.os, 'windows') }} - shell: bash - run: sudo chmod -R 777 /opt/ - - name: Build Install - shell: bash - run: | - cmake --install build --config Release --prefix /opt/beman.package - ls -R /opt/beman.package - - name: Test Release - if: ${{ !inputs.disable_test }} - shell: bash - run: ctest --test-dir build --build-config Release - - name: Build Debug - shell: bash - run: | - cmake --build build --config Debug --parallel --verbose - cmake --build build --config Debug --target all_verify_interface_header_sets - cmake --install build --config Debug --prefix /opt/beman.package - ls -R /opt/beman.package - - name: Test Debug - if: ${{ !inputs.disable_test }} - shell: bash - run: ctest --test-dir build --build-config Debug diff --git a/.github/actions/setup-environment/action.yml b/.github/actions/setup-environment/action.yml deleted file mode 100644 index ed3484a..0000000 --- a/.github/actions/setup-environment/action.yml +++ /dev/null @@ -1,17 +0,0 @@ -# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -name: "Setup Environment" -description: "Setup environment on windows/ macos" -runs: - using: "composite" - steps: - - name: Setup CMake - uses: lukka/get-cmake@latest - with: - cmakeVersion: latest - ninjaVersion: latest - - name: Setup MSVC - if: runner.os == 'windows' - uses: TheMrMilchmann/setup-msvc-dev@v3 - with: - arch: x64 diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 6085d62..071cb28 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,71 +1,5 @@ - - - - -## Description - -Please describe your contribution in a single sentence. - -## Related Issues - - - -## Motivation and Context - -Explain why this change is needed. - -## Testing - -Explain how is this tested. - -## Meta - - - -- [ ] If all approvals are obtained and the PR is green, any Beman member can merge the PR. - - diff --git a/.github/workflows/ci_tests.yml b/.github/workflows/ci_tests.yml index 6d5d89a..194ab3c 100644 --- a/.github/workflows/ci_tests.yml +++ b/.github/workflows/ci_tests.yml @@ -4,212 +4,136 @@ name: Continuous Integration Tests on: push: + branches: + - main pull_request: workflow_dispatch: schedule: - - cron: "30 15 * * *" + - cron: '30 15 * * *' jobs: - preset-test: - permissions: - actions: read - strategy: - fail-fast: false - matrix: - presets: - - preset: "gcc-debug" - compiler: "gcc:14" - - preset: "gcc-release" - compiler: "gcc:14" - - preset: "llvm-debug" - compiler: "clang:19" - - preset: "llvm-release" - compiler: "clang:19" - # - preset: "appleclang-debug" - # platform: "macos-latest" - # - preset: "appleclang-release" - # platform: "macos-latest" - - preset: "msvc-debug" - platform: "windows-latest" - - preset: "msvc-release" - platform: "windows-latest" - name: "Preset: ${{ matrix.presets.preset }} on ${{ matrix.presets.platform || matrix.presets.compiler }}" - runs-on: ${{ matrix.presets.platform || 'ubuntu-latest' }} - container: - image: ${{ matrix.presets.compiler && 'ghcr.io/bemanproject/testingcontainers-' }}${{ matrix.presets.compiler }} - steps: - - uses: actions/checkout@v4 - - name: Setup Environment - if: ${{ !matrix.presets.compiler }} - uses: ./.github/actions/setup-environment - - name: Get vcpkg deps - shell: bash - if: ${{ !contains(runner.os, 'Windows') }} - run: | - sudo apt-get install -y pkg-config zip - - name: Restore vcpkg cache - id: vcpkg-cache - uses: TAServers/vcpkg-cache@v3 - with: - token: ${{ secrets.GITHUB_TOKEN }} - - name: Run preset - env: - VCPKG_BINARY_SOURCES: "clear;files,${{ steps.vcpkg-cache.outputs.path }},readwrite" - run: cmake --workflow --preset ${{ matrix.presets.preset }} - - catch-test: - permissions: - actions: read - strategy: - fail-fast: false - matrix: - platform: - - description: "GNU 14" - compiler: "gcc:14" - toolchain: "cmake/gnu-toolchain.cmake" - - description: "LLVM 19" - compiler: "clang:19" - toolchain: "cmake/llvm-toolchain.cmake" - - description: "Windows MSVC" - os: windows-latest - toolchain: "cmake/msvc-toolchain.cmake" - #- description: "Macos Appleclang" - # os: macos-latest - # toolchain: "cmake/appleclang-toolchain.cmake" - cpp_version: [20, 23, 26] - cmake_args: - - description: "Default" - - description: "TSan" - args: "-DBEMAN_BUILDSYS_SANITIZER=TSan" - - description: "MaxSan" - args: "-DBEMAN_BUILDSYS_SANITIZER=MaxSan" - include: - - platform: - description: "GCC 14" - compiler: "gcc:14" - toolchain: "cmake/gnu-toolchain.cmake" - cpp_version: 20 - cmake_args: - description: "Werror" - args: "-DCMAKE_CXX_FLAGS='-Werror=all -Werror=extra'" - - platform: - description: "GCC 14" - compiler: "gcc:14" - toolchain: "cmake/gnu-toolchain.cmake" - cpp_version: 20 - cmake_args: - description: "Dynamic" - args: "-DBUILD_SHARED_LIBS=on" - exclude: - # MSVC does not support thread sanitizer - - platform: - description: "Windows MSVC" - cmake_args: - description: "TSan" + beman-submodule-check: + uses: bemanproject/infra-workflows/.github/workflows/reusable-beman-submodule-check.yml@1.5.3 - name: "Unit: - ${{ matrix.platform.description }} - ${{ matrix.cpp_version }} - ${{ matrix.cmake_args.description }}" - runs-on: ${{ matrix.platform.os || 'ubuntu-latest' }} - container: - image: ${{ matrix.platform.compiler && 'ghcr.io/bemanproject/testingcontainers-' }}${{ matrix.platform.compiler }} - steps: - - uses: actions/checkout@v4 - - name: Setup Environment - if: ${{ !matrix.platform.compiler }} - uses: ./.github/actions/setup-environment - - name: Build and Test - uses: ./.github/actions/cmake-build-test - with: - cpp_version: ${{ matrix.cpp_version }} - toolchain_file: ${{ matrix.platform.toolchain }} - cmake_extra_args: ${{ matrix.cmake_args.args }} - - configuration-test: - permissions: - actions: read - runs-on: ubuntu-latest - container: - image: ghcr.io/bemanproject/testingcontainers-gcc:14 - strategy: - fail-fast: false - matrix: - args: - - name: "Disable build testing" - arg: "-DBEMAN_BOUNDS_TEST_BUILD_TESTS=OFF" - - name: "Disable example building" - arg: "-DBEMAN_BOUNDS_TEST_BUILD_EXAMPLES=OFF" - - name: "Disable config-file package creation" - arg: "-DBEMAN_BOUNDS_TEST_INSTALL_CONFIG_FILE_PACKAGE=OFF" - name: "CMake: ${{ matrix.args.name }}" - steps: - - uses: actions/checkout@v4 - - name: Build and Test - uses: ./.github/actions/cmake-build-test - with: - cpp_version: 20 - toolchain_file: "cmake/gnu-toolchain.cmake" - cmake_extra_args: ${{ matrix.args.arg }} - disable_test: true + preset-test: + uses: bemanproject/infra-workflows/.github/workflows/reusable-beman-preset-test.yml@1.5.3 + with: + matrix_config: > + [ + {"preset": "gcc-debug", "image": "ghcr.io/bemanproject/infra-containers-gcc:latest"}, + {"preset": "gcc-release", "image": "ghcr.io/bemanproject/infra-containers-gcc:latest"}, + {"preset": "llvm-debug", "image": "ghcr.io/bemanproject/infra-containers-clang:latest"}, + {"preset": "llvm-release", "image": "ghcr.io/bemanproject/infra-containers-clang:latest"}, + {"preset": "msvc-debug", "runner": "windows-latest"}, + {"preset": "msvc-release", "runner": "windows-latest"} + ] - compiler-test: - permissions: - actions: read - strategy: - fail-fast: false - matrix: - compilers: - - class: gcc - version: 14 - toolchain: "cmake/gnu-toolchain.cmake" - - class: clang - version: 20 - toolchain: "cmake/llvm-toolchain.cmake" - - class: clang - version: 19 - toolchain: "cmake/llvm-toolchain.cmake" - - class: clang - version: 18 - toolchain: "cmake/llvm-toolchain.cmake" - - class: clang - version: 17 - toolchain: "cmake/llvm-toolchain.cmake" - name: "Compiler: ${{ matrix.compilers.class }} ${{ matrix.compilers.version }}" - runs-on: ubuntu-24.04 - container: - image: ghcr.io/bemanproject/testingcontainers-${{ matrix.compilers.class }}:${{ matrix.compilers.version }} - steps: - - uses: actions/checkout@v4 - - name: Build and Test - uses: ./.github/actions/cmake-build-test - with: - cpp_version: 20 - toolchain_file: ${{ matrix.compilers.toolchain }} + build-and-test: + uses: bemanproject/infra-workflows/.github/workflows/reusable-beman-build-and-test.yml@1.5.3 + with: + matrix_config: > + { + "gcc": [ + { "versions": ["15"], + "tests": [ + { "cxxversions": ["c++26"], + "tests": [ + { "stdlibs": ["libstdc++"], + "tests": [ + "Debug.Default", "Release.Default", "Release.TSan", + "Release.MaxSan", "Debug.Werror", + "Debug.Coverage", "Debug.Dynamic" + ] + } + ] + }, + { "cxxversions": ["c++23", "c++20"], + "tests": [{ "stdlibs": ["libstdc++"], "tests": ["Release.Default"]}] + } + ] + }, + { "versions": ["14", "13"], + "tests": [ + { "cxxversions": ["c++26", "c++23", "c++20"], + "tests": [{ "stdlibs": ["libstdc++"], "tests": ["Release.Default"]}] + } + ] + }, + { + "versions": ["12", "11"], + "tests": [ + { "cxxversions": ["c++23", "c++20"], + "tests": [{ "stdlibs": ["libstdc++"], "tests": ["Release.Default"]}] + } + ] + } + ], + "clang": [ + { "versions": ["22"], + "tests": [ + {"cxxversions": ["c++26"], + "tests": [ + { "stdlibs": ["libstdc++", "libc++"], + "tests": [ + "Debug.Default", "Release.Default", "Release.TSan", + "Release.MaxSan", "Debug.Werror" + ] + } + ] + }, + { "cxxversions": ["c++23", "c++20"], + "tests": [ + {"stdlibs": ["libstdc++", "libc++"], "tests": ["Release.Default"]} + ] + } + ] + }, + { "versions": ["21", "20", "19"], + "tests": [ + { "cxxversions": ["c++26", "c++23", "c++20"], + "tests": [ + {"stdlibs": ["libstdc++", "libc++"], "tests": ["Release.Default"]} + ] + } + ] + }, + { "versions": ["18"], + "tests": [ + { "cxxversions": ["c++26", "c++23", "c++20"], + "tests": [{"stdlibs": ["libc++"], "tests": ["Release.Default"]}] + }, + { "cxxversions": ["c++23", "c++20"], + "tests": [{"stdlibs": ["libstdc++"], "tests": ["Release.Default"]}] + } + ] + }, + { "versions": ["17"], + "tests": [ + { "cxxversions": ["c++26", "c++23", "c++20"], + "tests": [{"stdlibs": ["libc++"], "tests": ["Release.Default"]}] + }, + { "cxxversions": ["c++20"], + "tests": [{"stdlibs": ["libstdc++"], "tests": ["Release.Default"]}] + } + ] + } + ], + "msvc": [ + { "versions": ["latest"], + "tests": [ + { "cxxversions": ["c++23"], + "tests": [ + { "stdlibs": ["stl"], + "tests": ["Debug.Default", "Release.Default", "Release.MaxSan"] + } + ] + } + ] + } + ] + } create-issue-when-fault: - runs-on: ubuntu-latest - needs: [preset-test, catch-test, configuration-test, compiler-test] + needs: [preset-test, build-and-test] if: failure() && github.event_name == 'schedule' - steps: - # 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 - gh issue comment --repo ${{ github.repository }} $issue_num --body "$body" - fi - env: - GH_TOKEN: ${{ github.token }} + uses: bemanproject/infra-workflows/.github/workflows/reusable-beman-create-issue-when-fault.yml@1.5.3 diff --git a/.github/workflows/pre-commit-check.yml b/.github/workflows/pre-commit-check.yml new file mode 100644 index 0000000..980f6c5 --- /dev/null +++ b/.github/workflows/pre-commit-check.yml @@ -0,0 +1,19 @@ +name: Lint Check (pre-commit) + +on: + # We have to use pull_request_target here as pull_request does not grant + # enough permission for reviewdog + pull_request_target: + push: + branches: + - main + +permissions: + contents: read + checks: write + issues: write + pull-requests: write + +jobs: + pre-commit: + uses: bemanproject/infra-workflows/.github/workflows/reusable-beman-pre-commit.yml@1.5.3 diff --git a/.github/workflows/pre-commit-update.yml b/.github/workflows/pre-commit-update.yml new file mode 100644 index 0000000..9257379 --- /dev/null +++ b/.github/workflows/pre-commit-update.yml @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +name: Weekly pre-commit autoupdate + +on: + workflow_dispatch: + schedule: + - cron: "38 15 * * 4" + +jobs: + auto-update-pre-commit: + uses: bemanproject/infra-workflows/.github/workflows/reusable-beman-update-pre-commit.yml@1.5.3 + secrets: + APP_ID: ${{ secrets.AUTO_PR_BOT_APP_ID }} + PRIVATE_KEY: ${{ secrets.AUTO_PR_BOT_PRIVATE_KEY }} diff --git a/.gitignore b/.gitignore index 4957963..d293e3b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,15 +1,12 @@ -# IDE and development folders -.vscode/ -.idea/ -.settings/ -.cache/ +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +/.cache +/compile_commands.json +/build # ignore emacs temp files *~ \#*\# -# Miscellaneous build and testing folders -build/ - -# Loose files -.DS_Store +# ignore vscode settings +.vscode diff --git a/.markdownlint.yaml b/.markdownlint.yaml index 81f5fcd..21c2849 100644 --- a/.markdownlint.yaml +++ b/.markdownlint.yaml @@ -7,3 +7,4 @@ MD033: false # Update the comment in .clang-format if we no-longer tie these two column limits. MD013: line_length: 119 + code_blocks: false diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d19c5e6..eb01186 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,35 +2,47 @@ # See https://pre-commit.com/hooks.html for more hooks repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v5.0.0 + rev: v6.0.0 hooks: - - id: trailing-whitespace - - id: end-of-file-fixer - - id: check-yaml - - id: check-added-large-files + - 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 + rev: v22.1.4 hooks: - - id: clang-format - types_or: [c++, c] + - id: clang-format + types_or: [c++, c] + + # CMake linting and formatting + - repo: https://github.com/BlankSpruce/gersemi-pre-commit + rev: 0.27.2 + hooks: + - id: gersemi + name: CMake linting + exclude: ^.*/tests/.*/data/ # Exclude test data directories # Markdown linting # Config file: .markdownlint.yaml - - repo: https://github.com/igorshubovych/markdownlint-cli - rev: v0.42.0 - hooks: - - id: markdownlint + # Commented out to disable this by default. Uncomment to enable markdown linting. + # - repo: https://github.com/igorshubovych/markdownlint-cli + # rev: v0.42.0 + # hooks: + # - id: markdownlint - repo: https://github.com/codespell-project/codespell - rev: v2.3.0 + rev: v2.4.2 hooks: - id: codespell + # Beman Standard checking via beman-tidy - repo: https://github.com/bemanproject/beman-tidy rev: v0.3.1 hooks: - - id: beman-tidy + - id: beman-tidy + +exclude: 'cookiecutter/|infra/' diff --git a/CMakeLists.txt b/CMakeLists.txt index 5d0e4e8..7bd5d17 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,13 +1,12 @@ # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -cmake_minimum_required(VERSION 3.28) -include(cmake/bootstrap_vcpkg.cmake) +cmake_minimum_required(VERSION 3.30...4.3) project( beman.bounds_test DESCRIPTION "A library for checking integer operation boundary conditions" LANGUAGES CXX - VERSION 0.0.1 + VERSION 0.1.0 ) # [CMAKE.SKIP_TESTS] @@ -24,83 +23,55 @@ option( ${PROJECT_IS_TOP_LEVEL} ) -option( - BEMAN_BOUNDS_TEST_INSTALL_CONFIG_FILE_PACKAGE - "Enable creating and installing a CMake config-file package. Default: ${PROJECT_IS_TOP_LEVEL}. Values: { ON, OFF }." - ${PROJECT_IS_TOP_LEVEL} -) +# for find of beman_install_library and configure_build_telemetry +include(infra/cmake/beman-install-library.cmake) +include(infra/cmake/BuildTelemetryConfig.cmake) add_library(beman.bounds_test) add_library(beman::bounds_test ALIAS beman.bounds_test) -set_target_properties( - beman.bounds_test - PROPERTIES - VERIFY_INTERFACE_HEADER_SETS ON - EXPORT_NAME bounds_test -) - target_sources( beman.bounds_test - PUBLIC FILE_SET HEADERS - BASE_DIRS include - FILES - include/beman/bounds_test/bounds_test.hpp - include/beman/bounds_test/plat/common.hpp - + BASE_DIRS include + FILES + include/beman/bounds_test/bounds_test.hpp + include/beman/bounds_test/plat/common.hpp PUBLIC FILE_SET CXX_MODULES - BASE_DIRS include - FILES - include/beman/bounds_test/beman.bounds_test.cppm + BASE_DIRS include + FILES include/beman/bounds_test/beman.bounds_test.cppm +) + +set_target_properties( + beman.bounds_test + PROPERTIES VERIFY_INTERFACE_HEADER_SETS ${PROJECT_IS_TOP_LEVEL} ) -include(GNUInstallDirs) include(cmake/check_plat.cmake) add_subdirectory(include/beman/bounds_test/plat) -install( - TARGETS beman.bounds_test - EXPORT beman.bounds_test-targets - COMPONENT beman.bounds_test +beman_install_library(beman.bounds_test TARGETS beman.bounds_test) +configure_build_telemetry() - FILE_SET CXX_MODULES - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} +include(GNUInstallDirs) - FILE_SET HEADERS +install( + FILES cmake/check_plat.cmake cmake/beman.bounds_test-config.cmake + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/beman.bounds_test ) -if(BEMAN_BOUNDS_TEST_INSTALL_CONFIG_FILE_PACKAGE) - include(CMakePackageConfigHelpers) - - write_basic_package_version_file( - ${CMAKE_CURRENT_BINARY_DIR}/beman.bounds_test-config-version.cmake - COMPATIBILITY ExactVersion - ) - - install( - FILES - cmake/beman.bounds_test-config.cmake - cmake/check_plat.cmake - ${CMAKE_CURRENT_BINARY_DIR}/beman.bounds_test-config-version.cmake - DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/beman.bounds_test - COMPONENT beman.bounds_test - ) - - install( - EXPORT beman.bounds_test-targets - DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/beman.bounds_test - NAMESPACE beman:: - CXX_MODULES_DIRECTORY cxx-modules - COMPONENT beman.bounds_test - ) -endif() +install( + DIRECTORY + include/beman/bounds_test/plat/generic + include/beman/bounds_test/plat/gnu + include/beman/bounds_test/plat/msvc + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/beman/bounds_test/plat +) if(BEMAN_BOUNDS_TEST_BUILD_TESTS) - include(CTestUseLaunchers) enable_testing() add_subdirectory(tests/beman/bounds_test) endif() diff --git a/CMakePresets.json b/CMakePresets.json index c8d62c1..483e1a3 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -9,7 +9,7 @@ "cacheVariables": { "CMAKE_CXX_STANDARD": "20", "CMAKE_EXPORT_COMPILE_COMMANDS": "ON", - "BEMAN_BOUNDS_TEST_BOOTSTRAP_VCPKG": "ON" + "CMAKE_PROJECT_TOP_LEVEL_INCLUDES": "./infra/cmake/use-fetch-content.cmake" } }, { @@ -35,7 +35,7 @@ "_debug-base" ], "cacheVariables": { - "VCPKG_CHAINLOAD_TOOLCHAIN_FILE": "${sourceDir}/cmake/gnu-toolchain.cmake" + "CMAKE_TOOLCHAIN_FILE": "infra/cmake/gnu-toolchain.cmake" } }, { @@ -46,7 +46,7 @@ "_release-base" ], "cacheVariables": { - "VCPKG_CHAINLOAD_TOOLCHAIN_FILE": "${sourceDir}/cmake/gnu-toolchain.cmake" + "CMAKE_TOOLCHAIN_FILE": "infra/cmake/gnu-toolchain.cmake" } }, { @@ -57,7 +57,7 @@ "_debug-base" ], "cacheVariables": { - "VCPKG_CHAINLOAD_TOOLCHAIN_FILE": "${sourceDir}/cmake/llvm-toolchain.cmake" + "CMAKE_TOOLCHAIN_FILE": "infra/cmake/llvm-toolchain.cmake" } }, { @@ -68,7 +68,7 @@ "_release-base" ], "cacheVariables": { - "VCPKG_CHAINLOAD_TOOLCHAIN_FILE": "${sourceDir}/cmake/llvm-toolchain.cmake" + "CMAKE_TOOLCHAIN_FILE": "infra/cmake/llvm-toolchain.cmake" } }, { @@ -79,7 +79,7 @@ "_debug-base" ], "cacheVariables": { - "VCPKG_CHAINLOAD_TOOLCHAIN_FILE": "${sourceDir}/cmake/appleclang-toolchain.cmake" + "CMAKE_TOOLCHAIN_FILE": "infra/cmake/appleclang-toolchain.cmake" } }, { @@ -90,7 +90,7 @@ "_release-base" ], "cacheVariables": { - "VCPKG_CHAINLOAD_TOOLCHAIN_FILE": "${sourceDir}/cmake/appleclang-toolchain.cmake" + "CMAKE_TOOLCHAIN_FILE": "infra/cmake/appleclang-toolchain.cmake" } }, { @@ -101,7 +101,7 @@ "_debug-base" ], "cacheVariables": { - "VCPKG_CHAINLOAD_TOOLCHAIN_FILE": "${sourceDir}/cmake/msvc-toolchain.cmake" + "CMAKE_TOOLCHAIN_FILE": "infra/cmake/msvc-toolchain.cmake" } }, { @@ -112,7 +112,7 @@ "_release-base" ], "cacheVariables": { - "VCPKG_CHAINLOAD_TOOLCHAIN_FILE": "${sourceDir}/cmake/msvc-toolchain.cmake" + "CMAKE_TOOLCHAIN_FILE": "infra/cmake/msvc-toolchain.cmake" } } ], diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..8272dce --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,111 @@ +# Development + +## Configure and Build the Project Using CMake Presets + +The simplest way of configuring and building the project is to use [CMake +Presets](https://cmake.org/cmake/help/latest/manual/cmake-presets.7.html). Appropriate +presets for major compilers have been included by default. You can use `cmake +--list-presets=workflow` to see all available presets. + +Here is an example of invoking the `gcc-debug` preset: + +```shell +cmake --workflow --preset gcc-debug +``` + +Generally, there are two kinds of presets, `debug` and `release`. + +The `debug` presets are designed to aid development, so they have debuginfo and sanitizers +enabled. + +> [!NOTE] +> +> The sanitizers that are enabled vary from compiler to compiler. See the toolchain files +> under ([`infra/cmake`](infra/cmake/)) to determine the exact configuration used for each +> preset. + +The `release` presets are designed for production use, and +consequently have the highest optimization turned on (e.g. `O3`). + +## Configure and Build Manually + +If the presets are not suitable for your use case, a traditional CMake invocation will +provide more configurability. + +To configure, build and test the project manually, you can run this set of commands. Note +that this requires GoogleTest to be installed. + +```bash +cmake \ + -B build \ + -S . \ + -DCMAKE_CXX_STANDARD=20 \ + # Your extra arguments here. +cmake --build build +ctest --test-dir build +``` + +> [!IMPORTANT] +> +> Beman projects are [passive projects]( +> https://github.com/bemanproject/beman/blob/main/docs/beman_standard.md#cmakepassive_projects), +> so you need to specify the C++ version via `CMAKE_CXX_STANDARD` when manually +> configuring the project. + +## Dependency Management + +### FetchContent + +Instead of installing the project's dependencies via a package manager, you can optionally +configure beman.bounds_test to fetch them automatically via CMake FetchContent. + +To do so, specify +`-DCMAKE_PROJECT_TOP_LEVEL_INCLUDES=./infra/cmake/use-fetch-content.cmake`. This will +bring in GoogleTest automatically along with any other dependency the project may require. + +Example commands: + +```shell +cmake \ + -B build \ + -S . \ + -DCMAKE_CXX_STANDARD=20 \ + -DCMAKE_PROJECT_TOP_LEVEL_INCLUDES=./infra/cmake/use-fetch-content.cmake +cmake --build build +ctest --test-dir build +``` + +The file `./lockfile.json` configures the list of dependencies and versions that will be +acquired by FetchContent. + +## Project-specific configure arguments + +Project-specific options are prefixed with `BEMAN_BOUNDS_TEST`. +You can see the list of available options with: + +```bash +cmake -LH -S . -B build | grep "BEMAN_BOUNDS_TEST" -C 2 +``` + +
+ +Some project-specific configure arguments + +### `BEMAN_BOUNDS_TEST_BUILD_TESTS` + +Enable building tests and test infrastructure. Default: `ON`. +Values: `{ ON, OFF }`. + +### `BEMAN_BOUNDS_TEST_BUILD_EXAMPLES` + +Enable building examples. Default: `ON`. Values: `{ ON, OFF }`. + +### `BEMAN_BOUNDS_TEST_INSTALL_CONFIG_FILE_PACKAGE` + +Enable installing the CMake config file package. Default: `ON`. +Values: `{ ON, OFF }`. + +This is required so that users of `beman.bounds_test` can use +`find_package(beman.bounds_test)` to locate the library. + +
diff --git a/README.md b/README.md index 0aefc35..fe57e2a 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception --> -![Library Status](https://raw.githubusercontent.com/bemanproject/beman/refs/heads/main/images/badges/beman_badge-beman_library_under_development.svg) ![Continuous Integration Tests](https://github.com/bemanproject/bounds_test/actions/workflows/ci_tests.yml/badge.svg) ![Lint Check (pre-commit)](https://github.com/bemanproject/bounds_test/actions/workflows/pre-commit.yml/badge.svg) ![Standard Target](https://github.com/bemanproject/beman/blob/main/images/badges/cpp29.svg) +![Library Status](https://raw.githubusercontent.com/bemanproject/beman/refs/heads/main/images/badges/beman_badge-beman_library_under_development.svg) ![Continuous Integration Tests](https://github.com/bemanproject/bounds_test/actions/workflows/ci_tests.yml/badge.svg) ![Lint Check (pre-commit)](https://github.com/bemanproject/bounds_test/actions/workflows/pre-commit-check.yml/badge.svg) [![Coverage](https://coveralls.io/repos/github/bemanproject/bounds_test/badge.svg?branch=main)](https://coveralls.io/github/bemanproject/bounds_test?branch=main) ![Standard Target](https://github.com/bemanproject/beman/blob/main/images/badges/cpp29.svg) `beman.bounds_test` is a C++ library providing overflow and undefined behavior checking for integer operations. The library conforms to [The Beman Standard](https://github.com/bemanproject/beman/blob/main/docs/beman_standard.md). @@ -15,6 +15,10 @@ targeted at C++29. **Status**: [Under development and not yet ready for production use.](https://github.com/bemanproject/beman/blob/main/docs/beman_library_maturity_model.md#under-development-and-not-yet-ready-for-production-use) +## License + +`beman.bounds_test` is licensed under the Apache License v2.0 with LLVM Exceptions. + ## Overview The integer operations in C++ have boundary conditions that may readily be @@ -45,6 +49,53 @@ static_assert(beman::bounds_test::can_negate(small)); static_assert(beman::bounds_test::can_negate(big)); ``` +Full runnable examples can be found in [`examples/`](examples/). + +## Implementation Details + +All provided checks are fully `constexpr`, and so have zero runtime cost where +they can be evaluated at compile-time. For the runtime case, wherever possible +`beman.bounds_test` delegates to compiler builtins for bounds checking. + +This has trivial cost on most platforms, for example `can_add()` typically +resolves to a single [`setno`](https://www.felixcloutier.com/x86/setcc) +instruction on x86, or is optimized out entirely in favor of a conditional jump. +However, where compiler builtins are not available generic range-checking is +used instead. This optimizes less well than the builtins. + +The builtin checks used by `beman.bounds_test` can be found in +`cmake/check_plat.cmake`. + +## Dependencies + +### Build Environment + +This project requires at least the following to build: + +* A C++ compiler that conforms to the C++20 standard or greater +* CMake 3.30 or later +* (Test Only) Catch2 + +You can disable building tests by setting CMake option `BEMAN_BOUNDS_TEST_BUILD_TESTS` to +`OFF` when configuring the project. + +### Supported Platforms + +| Compiler | Version | C++ Standards | Standard Library | +|----------|---------|---------------|-------------------| +| GCC | 15-13 | C++26-C++20 | libstdc++ | +| GCC | 12-11 | C++23, C++20 | libstdc++ | +| Clang | 22-19 | C++26-C++20 | libstdc++, libc++ | +| Clang | 18 | C++26-C++20 | libc++ | +| Clang | 18 | C++23, C++20 | libstdc++ | +| Clang | 17 | C++26-C++20 | libc++ | +| Clang | 17 | C++20 | libstdc++ | +| MSVC | latest | C++23 | MSVC STL | + +## Development + +See the [Contributing Guidelines](CONTRIBUTING.md). + ## Integrate beman.bounds_test into your project `beman.bounds_test` is available as both a header and a module. It requires @@ -67,62 +118,90 @@ find_package(beman.bounds_test) target_link_libraries( PRIVATE beman::bounds_test) ``` -## Building beman.bounds_test +### Build + +You can build bounds_test using a CMake workflow preset: -`beman.bounds_test` has no dependencies when being built without tests, so is -easily built via typical CMake workflows. The CMakePresets provide some -examples, but a trivial build might be performed as follows: +```bash +cmake --workflow --preset gcc-release +``` -```plaintext -cmake -B build -G Ninja \ - -DCMAKE_CXX_STANDARD=20 \ - -DBEMAN_BOUNDS_TEST_BUILD_TESTS=OFF \ - -DBEMAN_BOUNDS_TEST_BUILD_EXAMPLES=OFF +To list available workflow presets, you can invoke: -cmake --build build -cmake --install build --prefix +```bash +cmake --list-presets=workflow ``` -When building tests `beman.bounds_test` relies on [Catch2](https://github.com/catchorg/Catch2) -to provide testing infrastructure. This can be provided as part of the build -environment, or can be provided by vcpkg as part of the build. In order to -bootstrap vcpkg, use the `-DBEMAN_BOUNDS_TEST_BOOTSTRAP_VCPKG=ON` option. +For details on building beman.bounds_test without using a CMake preset, refer to the +[Contributing Guidelines](CONTRIBUTING.md). -## Implementation Details +### Installation -All provided checks are fully `constexpr`, and so have zero runtime cost where -they can be evaluated at compile-time. For the runtime case, wherever possible -`beman.bounds_test` delegates to compiler builtins for bounds checking. +To install beman.bounds_test globally after building with the `gcc-release` preset, you can +run: -This has trivial cost on most platforms, for example `can_add()` typically -resolves to a single [`setno`](https://www.felixcloutier.com/x86/setcc) -instruction on x86, or is optimized out entirely in favor of a conditional jump. -However, where compiler builtins are not available generic range-checking is -used instead. This optimizes less well than the builtins. +```bash +sudo cmake --install build/gcc-release +``` -The builtin checks used by `beman.bounds_test` can be found in -`cmake/check_plat.cmake`. +Alternatively, to install to a prefix, for example `/opt/beman`, you can run: -## License +```bash +sudo cmake --install build/gcc-release --prefix /opt/beman +``` -beman.bounds_test is licensed under the Apache License v2.0 with LLVM +This will generate the following directory structure: + +```txt +/opt/beman +├── include +│ └── beman +│ └── bounds_test +│ ├── bounds_test.hpp +│ └── ... +└── lib + ├── cmake + │ └── beman.bounds_test + │ ├── beman.bounds_test-config-version.cmake + │ ├── beman.bounds_test-config.cmake + │ ├── beman.bounds_test-targets-debug.cmake + │ └── beman.bounds_test-targets.cmake + └── libbeman.bounds_test.a +``` -Source is licensed with the Apache 2.0 license with LLVM exceptions +### CMake Configuration -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +If you installed beman.bounds_test to a prefix, you can specify that prefix to your CMake +project using `CMAKE_PREFIX_PATH`; for example, `-DCMAKE_PREFIX_PATH=/opt/beman`. -Documentation and associated papers are licensed with the Creative Commons -Attribution 4.0 International license. +You need to bring in the `beman.bounds_test` package to define the `beman::bounds_test` CMake +target: -// SPDX-License-Identifier: CC-BY-4.0 +```cmake +find_package(beman.bounds_test REQUIRED) +``` -The intent is that the source and documentation are available for use by people -how they wish. +You will then need to add `beman::bounds_test` to the link libraries of any libraries or +executables that include `beman.bounds_test` headers. -The README itself is licensed with CC0 1.0 Universal. Copy the contents and -incorporate in your own work as you see fit. +```cmake +target_link_libraries(yourlib PUBLIC beman::bounds_test) +``` + +### Using beman.bounds_test + +To use `beman.bounds_test` in your C++ project, +include an appropriate `beman.bounds_test` header from your source code. + +```c++ +#include +``` -// SPDX-License-Identifier: CC0-1.0 +> [!NOTE] +> +> `beman.bounds_test` headers are to be included with the `beman/bounds_test/` prefix. +> Altering include search paths to spell the include target another way (e.g. +> `#include `) is unsupported. ## Contributing diff --git a/cmake/bootstrap_vcpkg.cmake b/cmake/bootstrap_vcpkg.cmake deleted file mode 100644 index fd61f30..0000000 --- a/cmake/bootstrap_vcpkg.cmake +++ /dev/null @@ -1,47 +0,0 @@ -option(BEMAN_BOUNDS_TEST_BOOTSTRAP_VCPKG "Bootstrap vcpkg if no other toolchain exists" OFF) - -if(BEMAN_BOUNDS_TEST_BOOTSTRAP_VCPKG AND NOT DEFINED CMAKE_TOOLCHAIN_FILE) - include(FetchContent) - - if(WIN32) - set(VCPKG vcpkg.exe) - elseif(LINUX) - if(EXISTS "/etc/alpine-release") - set(VCPKG vcpkg-musl) - else() - set(VCPKG vcpkg-glibc) - endif() - elseif(APPLE) - set(VCPKG vcpkg-macos) - else() - message(FATAL_ERROR "Cannot bootstrap vcpkg: Unsupported platform") - endif() - - FetchContent_Declare(vcpkg - URL https://github.com/microsoft/vcpkg-tool/releases/latest/download/${VCPKG} - DOWNLOAD_NO_EXTRACT TRUE - ) - - FetchContent_MakeAvailable(vcpkg) - set(VCPKG ${vcpkg_SOURCE_DIR}/${VCPKG}) - - file(CHMOD ${VCPKG} PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE) - set(ENV{VCPKG_ROOT} ${vcpkg_SOURCE_DIR}) - execute_process(COMMAND ${VCPKG} bootstrap-standalone) - - if(NOT WIN32) - file(RENAME ${VCPKG} ${vcpkg_SOURCE_DIR}/vcpkg) - endif() - - set(CMAKE_TOOLCHAIN_FILE - ${vcpkg_SOURCE_DIR}/scripts/buildsystems/vcpkg.cmake - CACHE FILEPATH "Vcpkg toolchain file" - ) - set(VCPKG_ROOT_DIR ${vcpkg_SOURCE_DIR} CACHE PATH "Vcpkg Root Directory") -endif() - -if(DEFINED VCPKG_ROOT_DIR) - add_custom_target(UpdateVcpkgBaseline - ${VCPKG_ROOT_DIR}/vcpkg x-update-baseline - ) -endif() diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index cbb01c5..99d8ba6 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -1,16 +1,13 @@ # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -function(add_bounds_test_example NAME) - add_executable(beman.bounds_test.examples.${NAME}) - target_sources(beman.bounds_test.examples.${NAME} PRIVATE ${NAME}.cpp) - target_compile_features(beman.bounds_test.examples.${NAME} - PRIVATE cxx_std_20 - ) +set(ALL_EXAMPLES placeholder cartesian_plane) +message("Examples to be built: ${ALL_EXAMPLES}") + +foreach(example ${ALL_EXAMPLES}) + add_executable(beman.bounds_test.examples.${example}) + target_sources(beman.bounds_test.examples.${example} PRIVATE ${example}.cpp) target_link_libraries( - beman.bounds_test.examples.${NAME} + beman.bounds_test.examples.${example} PRIVATE beman::bounds_test ) -endfunction() - -add_bounds_test_example(placeholder) -add_bounds_test_example(cartesian_plane) +endforeach() diff --git a/include/beman/bounds_test/CMakeLists.txt b/include/beman/bounds_test/CMakeLists.txt new file mode 100644 index 0000000..9e26200 --- /dev/null +++ b/include/beman/bounds_test/CMakeLists.txt @@ -0,0 +1 @@ +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception diff --git a/include/beman/bounds_test/bounds_test.hpp b/include/beman/bounds_test/bounds_test.hpp index ca99421..af48c51 100644 --- a/include/beman/bounds_test/bounds_test.hpp +++ b/include/beman/bounds_test/bounds_test.hpp @@ -8,9 +8,9 @@ #include #ifdef __INTELLISENSE__ -#include "plat/generic/beman/bounds_test/plat/plat.hpp" + #include "plat/generic/beman/bounds_test/plat/plat.hpp" #else -#include + #include #endif namespace beman::bounds_test { @@ -33,91 +33,93 @@ constexpr bool can_subtract_in_place_modular(A a, B b) noexcept; template constexpr bool can_convert(A a) noexcept { - return std::in_range(a); + return std::in_range(a); } template constexpr bool can_convert_modular(A /* a */) noexcept { - return true; + return true; } template constexpr bool can_increment(A a) noexcept { - return can_add_in_place(a, 1); + return can_add_in_place(a, 1); } template constexpr bool can_decrement(A a) noexcept { - return can_subtract_in_place(a, 1); + return can_subtract_in_place(a, 1); } template constexpr bool can_promote(A /* a */) noexcept { - return true; + return true; } template constexpr bool can_negate(A a) noexcept { - using result_t = decltype(-a); - if constexpr (std::unsigned_integral) return !a; - return a != std::numeric_limits::min(); + using result_t = decltype(-a); + if constexpr (std::unsigned_integral) + return !a; + return a != std::numeric_limits::min(); } template constexpr bool can_bitwise_not(A /* a */) noexcept { - return true; + return true; } template constexpr bool can_increment_modular(A a) noexcept { - return can_add_in_place_modular(a, 1); + return can_add_in_place_modular(a, 1); } template constexpr bool can_decrement_modular(A a) noexcept { - return can_subtract_in_place_modular(a, 1); + return can_subtract_in_place_modular(a, 1); } template constexpr bool can_promote_modular(A /* a */) noexcept { - return true; + return true; } template constexpr bool can_negate_modular(A a) noexcept { - using result_t = decltype(-a); - if constexpr (std::unsigned_integral) return true; - return a != std::numeric_limits::min(); + using result_t = decltype(-a); + if constexpr (std::unsigned_integral) + return true; + return a != std::numeric_limits::min(); } template constexpr bool can_bitwise_not_modular(A /* a */) noexcept { - return true; + return true; } template constexpr bool can_add(A a, B b) noexcept { - return ::beman::bounds_test::detail::can_add(a, b, decltype(a + b){}); + return ::beman::bounds_test::detail::can_add(a, b, decltype(a + b){}); } template constexpr bool can_subtract(A a, B b) noexcept { - return ::beman::bounds_test::detail::can_sub(a, b, decltype(a - b){}); + return ::beman::bounds_test::detail::can_sub(a, b, decltype(a - b){}); } template constexpr bool can_multiply(A a, B b) noexcept { - return ::beman::bounds_test::detail::can_mul(a, b, decltype(a * b){}); + return ::beman::bounds_test::detail::can_mul(a, b, decltype(a * b){}); } template constexpr bool can_divide(A a, B b) noexcept { - return ::beman::bounds_test::detail::can_div(a, b, decltype(a / b){}); + return ::beman::bounds_test::detail::can_div(a, b, decltype(a / b){}); } template constexpr bool can_take_remainder(A a, B b) noexcept { - return can_divide(a, b); + return can_divide(a, b); } template @@ -128,17 +130,17 @@ constexpr bool can_shift_right(A a, B b) noexcept; template constexpr bool can_bitwise_and(A /* a */, B /* b */) noexcept { - return true; + return true; } template constexpr bool can_bitwise_xor(A /* a */, B /* b */) noexcept { - return true; + return true; } template constexpr bool can_bitwise_or(A /* a */, B /* b */) noexcept { - return true; + return true; } template @@ -146,14 +148,16 @@ constexpr bool can_compare(A a, B b) noexcept; template constexpr bool can_add_modular(A a, B b) noexcept { - if constexpr (std::unsigned_integral) return true; - return can_add(a, b); + if constexpr (std::unsigned_integral) + return true; + return can_add(a, b); } template constexpr bool can_subtract_modular(A a, B b) noexcept { - if constexpr (std::unsigned_integral) return true; - return can_subtract(a, b); + if constexpr (std::unsigned_integral) + return true; + return can_subtract(a, b); } template @@ -167,42 +171,42 @@ constexpr bool can_shift_right_modular(A, B) noexcept; template constexpr bool can_bitwise_and_modular(A /* a */, B /* b */) noexcept { - return true; + return true; } template constexpr bool can_bitwise_xor_modular(A /* a */, B /* b */) noexcept { - return true; + return true; } template constexpr bool can_bitwise_or_modular(A /* a */, B /* b */) noexcept { - return true; + return true; } template constexpr bool can_add_in_place(A a, B b) noexcept { - return ::beman::bounds_test::detail::can_add(a, b, a); + return ::beman::bounds_test::detail::can_add(a, b, a); } template constexpr bool can_subtract_in_place(A a, B b) noexcept { - return ::beman::bounds_test::detail::can_sub(a, b, a); + return ::beman::bounds_test::detail::can_sub(a, b, a); } template constexpr bool can_multiply_in_place(A a, B b) noexcept { - return ::beman::bounds_test::detail::can_mul(a, b, a); + return ::beman::bounds_test::detail::can_mul(a, b, a); } template constexpr bool can_divide_in_place(A a, B b) noexcept { - return ::beman::bounds_test::detail::can_div(a, b, a); + return ::beman::bounds_test::detail::can_div(a, b, a); } template constexpr bool can_take_remainder_in_place(A a, B b) noexcept { - return can_divide_in_place(a, b); + return can_divide_in_place(a, b); } template @@ -213,29 +217,31 @@ constexpr bool can_shift_right_in_place(A a, B b) noexcept; template constexpr bool can_bitwise_and_in_place(A a, B b) noexcept { - return std::in_range(a & b); + return std::in_range(a & b); } template constexpr bool can_bitwise_xor_in_place(A a, B b) noexcept { - return std::in_range(a ^ b); + return std::in_range(a ^ b); } template constexpr bool can_bitwise_or_in_place(A a, B b) noexcept { - return std::in_range(a | b); + return std::in_range(a | b); } template constexpr bool can_add_in_place_modular(A a, B b) noexcept { - if constexpr (std::unsigned_integral) return true; - return can_add_in_place(a, b); + if constexpr (std::unsigned_integral) + return true; + return can_add_in_place(a, b); } template constexpr bool can_subtract_in_place_modular(A a, B b) noexcept { - if constexpr (std::unsigned_integral) return true; - return can_subtract_in_place(a, b); + if constexpr (std::unsigned_integral) + return true; + return can_subtract_in_place(a, b); } template @@ -243,27 +249,27 @@ constexpr bool can_multiply_in_place_modular(A a, B b) noexcept; template constexpr bool can_shift_left_in_place_modular(A a, B b) noexcept { - return ::beman::bounds_test::detail::can_shift(a, b, a); + return ::beman::bounds_test::detail::can_shift(a, b, a); }; template constexpr bool can_shift_right_in_place_modular(A a, B b) noexcept { - return ::beman::bounds_test::detail::can_shift(a, b, a); + return ::beman::bounds_test::detail::can_shift(a, b, a); }; template constexpr bool can_bitwise_and_in_place_modular(A /* a */, B /* b */) noexcept { - return true; + return true; } template constexpr bool can_bitwise_xor_in_place_modular(A /* a */, B /* b */) noexcept { - return true; + return true; } template constexpr bool can_bitwise_or_in_place_modular(A /* a */, B /* b */) noexcept { - return true; + return true; } } // namespace beman::bounds_test diff --git a/infra/.beman_submodule b/infra/.beman_submodule new file mode 100644 index 0000000..8463363 --- /dev/null +++ b/infra/.beman_submodule @@ -0,0 +1,3 @@ +[beman_submodule] +remote=https://github.com/bemanproject/infra.git +commit_hash=dfdb103b5fc9cccd3424c377130e318466f1dd89 diff --git a/infra/.github/CODEOWNERS b/infra/.github/CODEOWNERS new file mode 100644 index 0000000..4ff90a4 --- /dev/null +++ b/infra/.github/CODEOWNERS @@ -0,0 +1 @@ +* @ednolan @neatudarius @rishyak @wusatosi @JeffGarland diff --git a/.github/workflows/pre-commit.yml b/infra/.github/workflows/pre-commit.yml similarity index 98% rename from .github/workflows/pre-commit.yml rename to infra/.github/workflows/pre-commit.yml index f3c4332..9646831 100644 --- a/.github/workflows/pre-commit.yml +++ b/infra/.github/workflows/pre-commit.yml @@ -5,6 +5,8 @@ on: # enough permission for reviewdog pull_request_target: push: + branches: + - main jobs: pre-commit-push: diff --git a/infra/.github/workflows/reusable-beman-create-issue-when-fault.yml b/infra/.github/workflows/reusable-beman-create-issue-when-fault.yml new file mode 100644 index 0000000..024a51f --- /dev/null +++ b/infra/.github/workflows/reusable-beman-create-issue-when-fault.yml @@ -0,0 +1,28 @@ +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +name: 'Beman issue creation workflow' +on: + workflow_call: + workflow_dispatch: +jobs: + create-issue: + runs-on: ubuntu-latest + steps: + # 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] infra repo CI job failure" -L 1 --json number | jq 'if length == 0 then -1 else .[0].number end') + body="**CI job 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 job 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] infra repo CI job failure" --body "$body" --assignee ${{ github.actor }} + else + gh issue comment --repo ${{ github.repository }} $issue_num --body "$body" + fi + env: + GH_TOKEN: ${{ github.token }} diff --git a/infra/.gitignore b/infra/.gitignore new file mode 100644 index 0000000..b7cdbb5 --- /dev/null +++ b/infra/.gitignore @@ -0,0 +1,59 @@ +# 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 + +# Python +__pycache__/ +.pytest_cache/ +*.pyc +*.pyo +*.pyd +*.pyw +*.pyz +*.pywz +*.pyzw +*.pyzwz +*.delete_me + +# MAC OS +*.DS_Store + +# Editor files +.vscode/ +.idea/ + +# Build directories +infra.egg-info/ +beman_tidy.egg-info/ +*.egg-info/ +build/ +dist/ diff --git a/infra/.pre-commit-config.yaml b/infra/.pre-commit-config.yaml new file mode 100644 index 0000000..8052e18 --- /dev/null +++ b/infra/.pre-commit-config.yaml @@ -0,0 +1,21 @@ +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v6.0.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-yaml + - id: check-added-large-files + + - repo: https://github.com/codespell-project/codespell + rev: v2.4.2 + hooks: + - id: codespell + + # CMake linting and formatting + - repo: https://github.com/BlankSpruce/gersemi-pre-commit + rev: 0.27.2 + hooks: + - id: gersemi + name: CMake linting + exclude: ^.*/tests/.*/data/ # Exclude test data directories diff --git a/infra/LICENSE b/infra/LICENSE new file mode 100644 index 0000000..f6db814 --- /dev/null +++ b/infra/LICENSE @@ -0,0 +1,219 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. + + +---- LLVM Exceptions to the Apache 2.0 License ---- + +As an exception, if, as a result of your compiling your source code, portions +of this Software are embedded into an Object form of such source code, you +may redistribute such embedded portions in such Object form without complying +with the conditions of Sections 4(a), 4(b) and 4(d) of the License. + +In addition, if you combine or link compiled forms of this Software with +software that is licensed under the GPLv2 ("Combined Software") and if a +court of competent jurisdiction determines that the patent provision (Section +3), the indemnity provision (Section 9) or other Section of the License +conflicts with the conditions of the GPLv2, you may retroactively and +prospectively choose to deem waived or otherwise exclude such Section(s) of +the License, but only in their entirety and only with respect to the Combined +Software. diff --git a/infra/README.md b/infra/README.md new file mode 100644 index 0000000..bf9bbb0 --- /dev/null +++ b/infra/README.md @@ -0,0 +1,88 @@ +# Beman Project Infrastructure Repository + + + +This repository contains the infrastructure for The Beman Project. This is NOT a library repository, +so it does not respect the usual structure of a Beman library repository nor The Beman Standard! + +## Description + +* `cmake/`: CMake modules and toolchain files used by Beman libraries. +* `containers/`: Containers used for CI builds and tests in the Beman org. + +## Usage + +This repository is intended to be used as a beman-submodule in other Beman repositories. See +[the beman-submodule documentation](https://github.com/bemanproject/beman-submodule) for details. + + +### CMake Modules + + +#### `beman_install_library` + +The CMake modules in this repository are intended to be used by Beman libraries. Use the +`beman_add_install_library_config()` function to install your library, along with header +files, any metadata files, and a CMake config file for `find_package()` support. + +```cmake +add_library(beman.something) +add_library(beman::something ALIAS beman.something) + +# ... configure your target as needed ... + +find_package(beman-install-library REQUIRED) +beman_install_library(beman.something) +``` + +Note that the target must be created before calling `beman_install_library()`. The module +also assumes that the target is named using the `beman.something` convention, and it +uses that assumption to derive the names to match other Beman standards and conventions. +If your target does not follow that convention, raise an issue or pull request to add +more configurability to the module. + +The module will configure the target to install: + +* The library target itself +* Any public headers associated with the target +* CMake files for `find_package(beman.something)` support + +Some options for the project and target will also be supported: + +* `BEMAN_INSTALL_CONFIG_FILE_PACKAGES` - a list of package names (e.g., `beman.something`) for which to install the config file + (default: all packages) +* `_INSTALL_CONFIG_FILE_PACKAGE` - a per-project option to enable/disable config file installation (default: `ON` if the project is top-level, `OFF` otherwise). For instance for `beman.something`, the option would be `BEMAN_SOMETHING_INSTALL_CONFIG_FILE_PACKAGE`. + +# BuildTelemetry + +The cmake modules in this library provide access to CMake instrumentation data in Google Trace format which is visualizable with chrome://tracing and https://ui.perfetto.dev. + +Telemetry may be enabled in several ways: + +## `include` + +```cmake +include (infra/cmake/BuildTelemetry.cmake) +configure_build_telemetry() +``` + +## `find_package` + +```cmake +find_package(BuildTelemetry) +configure_build_telemetry() +``` + +as long as [BuildTelemetryConfig.cmake](./cmake/BuildTelemetryConfig.cmake) is in your module path. + +## `CMAKE_PROJECT_TOP_LEVEL_INCLUDES` +A non-invasive way to inject this telemetry into a CMake build you do not want to modify. +Add: +```sh +-DCMAKE_PROJECT_TOP_LEVEL_INCLUDES=infra/cmake/BuildTelemetry.cmake +``` +To the cmake invocation. + +In any form, CMake will call `telemetry.sh` which will copy the trace data in json format into a `.trace` subdirectory within the build directory. + +Multiple calls to `configure_build_telemetry` will only configure the callback hooks once, so it is safe to enable multiple times, including by TOP_LEVEL_INCLUDE. diff --git a/infra/cmake/BuildTelemetry.cmake b/infra/cmake/BuildTelemetry.cmake new file mode 100755 index 0000000..c2ff343 --- /dev/null +++ b/infra/cmake/BuildTelemetry.cmake @@ -0,0 +1,4 @@ +include_guard(GLOBAL) + +include(${CMAKE_CURRENT_LIST_DIR}/BuildTelemetryConfig.cmake) +configure_build_telemetry() diff --git a/infra/cmake/BuildTelemetryConfig.cmake b/infra/cmake/BuildTelemetryConfig.cmake new file mode 100755 index 0000000..15aae48 --- /dev/null +++ b/infra/cmake/BuildTelemetryConfig.cmake @@ -0,0 +1,58 @@ +include_guard(GLOBAL) + +set(BUILD_TELEMETRY_DIR ${CMAKE_CURRENT_LIST_DIR}) + +function(configure_build_telemetry) + if(NOT BUILD_TELEMETRY_CONFIGURATION) + # Check if the CMake version is at least 4.3 + if(CMAKE_VERSION VERSION_LESS "4.3") + message( + STATUS + "CMake version is less than 4.3, configuring cmake_instrumentation is unavailable." + ) + return() + else() + message(STATUS "Configuring Build Telemetry") + endif() + + # Find bash and jq for the telemetry callback script. + # On Windows, Git for Windows provides bash if available. + find_program(BEMAN_BASH bash) + find_program(BEMAN_JQ jq) + if(NOT BEMAN_BASH OR NOT BEMAN_JQ) + message( + STATUS + "bash or jq not found, build telemetry disabled on this platform." + ) + return() + endif() + + # Telemetry query + cmake_instrumentation( + API_VERSION 1 + DATA_VERSION 1 + OPTIONS staticSystemInformation dynamicSystemInformation trace + HOOKS + postGenerate + preBuild + postBuild + preCMakeBuild + postCMakeBuild + postCMakeInstall + postCTest + CALLBACK ${BEMAN_BASH} + ${BUILD_TELEMETRY_DIR}/telemetry.sh + ) + message( + DEBUG + "using callback script ${BUILD_TELEMETRY_DIR}/telemetry.sh via ${BEMAN_BASH}" + ) + + # Mark configuration as done in cache + set(BUILD_TELEMETRY_CONFIGURATION + TRUE + CACHE INTERNAL + "Flag to ensure Build Telemetry configured only once" + ) + endif() +endfunction(configure_build_telemetry) diff --git a/infra/cmake/Config.cmake.in b/infra/cmake/Config.cmake.in new file mode 100644 index 0000000..3f1341c --- /dev/null +++ b/infra/cmake/Config.cmake.in @@ -0,0 +1,12 @@ +# cmake/Config.cmake.in -*-makefile-*- +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +include(CMakeFindDependencyMacro) + +@BEMAN_INSTALL_FIND_DEPENDENCIES@ + +@PACKAGE_INIT@ + +include(${CMAKE_CURRENT_LIST_DIR}/@BEMAN_INSTALL_BASE_PKG_NAME@-targets.cmake) + +check_required_components(@BEMAN_INSTALL_BASE_PKG_NAME@) diff --git a/cmake/appleclang-toolchain.cmake b/infra/cmake/appleclang-toolchain.cmake similarity index 85% rename from cmake/appleclang-toolchain.cmake rename to infra/cmake/appleclang-toolchain.cmake index e7a6cc4..70ef548 100644 --- a/cmake/appleclang-toolchain.cmake +++ b/infra/cmake/appleclang-toolchain.cmake @@ -16,6 +16,8 @@ include_guard(GLOBAL) +# Prevent PATH collision with an LLVM clang installation by using the system +# compiler shims set(CMAKE_C_COMPILER cc) set(CMAKE_CXX_COMPILER c++) @@ -37,3 +39,6 @@ set(CMAKE_CXX_FLAGS_RELWITHDEBINFO_INIT "${RELEASE_FLAGS}") set(CMAKE_C_FLAGS_RELEASE_INIT "${RELEASE_FLAGS}") set(CMAKE_CXX_FLAGS_RELEASE_INIT "${RELEASE_FLAGS}") + +# Add this dir to the module path so that `find_package(beman-install-library)` works +list(APPEND CMAKE_PREFIX_PATH "${CMAKE_CURRENT_LIST_DIR}") diff --git a/infra/cmake/beman-install-library.cmake b/infra/cmake/beman-install-library.cmake new file mode 100644 index 0000000..dc5a4d1 --- /dev/null +++ b/infra/cmake/beman-install-library.cmake @@ -0,0 +1,323 @@ +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +include_guard(GLOBAL) + +include(CMakePackageConfigHelpers) +include(GNUInstallDirs) + +# beman_install_library +# ===================== +# +# Installs a library (or set of targets) along with headers, C++ modules, +# and optional CMake package configuration files. +# +# Usage: +# ------ +# beman_install_library( +# TARGETS [ ...] +# [DEPENDENCIES [ ...]] +# [NAMESPACE ] +# [EXPORT_NAME ] +# [DESTINATION ] +# ) +# +# Arguments: +# ---------- +# +# name +# Logical package name (e.g. "beman.utility"). +# Used to derive config file names and cache variable prefixes. +# +# TARGETS (required) +# List of CMake targets to install. +# +# DEPENDENCIES (optional) +# Semicolon-separated list, one dependency per entry. +# Each entry is a valid find_dependency() argument list. +# Note: you must use the bracket form for quoting if not only a package name is used! +# "[===[beman.inplace_vector 1.0.0]===] [===[beman.scope 0.0.1 EXACT]===] fmt" +# +# NAMESPACE (optional) +# Namespace for exported targets. +# Defaults to "beman::". +# +# EXPORT_NAME (optional) +# Name of the CMake export set. +# Defaults to "-targets". +# +# DESTINATION (optional) +# The install destination for CXX_MODULES. +# Defaults to ${CMAKE_INSTALL_LIBDIR}/cmake/${name}/modules. +# +# Brief +# ----- +# +# This function installs the specified project TARGETS and its FILE_SET +# HEADERS to the default CMAKE install destination. +# +# It also handles the installation of the CMake config package files if +# needed. If the given targets has a PUBLIC FILE_SET CXX_MODULE, it will also +# installed to the given DESTINATION +# +# Cache variables: +# ---------------- +# +# BEMAN_INSTALL_CONFIG_FILE_PACKAGES +# List of package names for which config files should be installed. +# +# _INSTALL_CONFIG_FILE_PACKAGE +# Per-package override to enable/disable config file installation. +# is the uppercased package name with dots replaced by underscores. +# +# Caveats +# ------- +# +# **Only one `PUBLIC FILE_SET CXX_MODULES` is yet supported to install with this +# function!** +# +# **Only header files contained in a `PUBLIC FILE_SET TYPE HEADERS` will be +# install with this function!** + +function(beman_install_library name) + # ---------------------------- + # Argument parsing + # ---------------------------- + set(oneValueArgs NAMESPACE EXPORT_NAME DESTINATION) + set(multiValueArgs TARGETS DEPENDENCIES) + + cmake_parse_arguments( + BEMAN_INSTALL + "${options}" + "${oneValueArgs}" + "${multiValueArgs}" + ${ARGN} + ) + + if(NOT BEMAN_INSTALL_TARGETS) + message( + FATAL_ERROR + "beman_install_library(${name}): TARGETS must be specified" + ) + endif() + + if(CMAKE_SKIP_INSTALL_RULES) + message( + WARNING + "beman_install_library(${name}): not installing targets '${BEMAN_INSTALL_TARGETS}' due to CMAKE_SKIP_INSTALL_RULES" + ) + return() + endif() + + set(_config_install_dir "${CMAKE_INSTALL_LIBDIR}/cmake/${name}") + + # ---------------------------- + # Defaults + # ---------------------------- + if(NOT BEMAN_INSTALL_NAMESPACE) + set(BEMAN_INSTALL_NAMESPACE "beman::") + endif() + + if(NOT BEMAN_INSTALL_EXPORT_NAME) + set(BEMAN_INSTALL_EXPORT_NAME "${name}-targets") + endif() + + if(NOT BEMAN_INSTALL_DESTINATION) + set(BEMAN_INSTALL_DESTINATION "${_config_install_dir}/modules") + endif() + + string(REPLACE "beman." "" install_component_name "${name}") + message( + VERBOSE + "beman-install-library(${name}): COMPONENT '${install_component_name}'" + ) + + # -------------------------------------------------- + # Install each target with all of its file sets + # -------------------------------------------------- + foreach(_tgt IN LISTS BEMAN_INSTALL_TARGETS) + if(NOT TARGET "${_tgt}") + message( + WARNING + "beman_install_library(${name}): '${_tgt}' is not a target" + ) + continue() + endif() + + # Given foo.bar, the component name is bar + string(REPLACE "." ";" name_parts "${_tgt}") + # fail if the name doesn't look like foo.bar + list(LENGTH name_parts name_parts_length) + if(NOT name_parts_length EQUAL 2) + message( + FATAL_ERROR + "beman_install_library(${name}): expects a name of the form 'beman.', got '${_tgt}'" + ) + endif() + list(GET name_parts -1 component_name) + set_target_properties( + "${_tgt}" + PROPERTIES EXPORT_NAME "${component_name}" + ) + message( + VERBOSE + "beman_install_library(${name}): EXPORT_NAME ${component_name} for TARGET '${_tgt}'" + ) + + # Get the list of interface header sets, exact one expected! + set(_install_header_set_args) + get_target_property( + _available_header_sets + ${_tgt} + INTERFACE_HEADER_SETS + ) + if(_available_header_sets) + message( + VERBOSE + "beman-install-library(${name}): '${_tgt}' has INTERFACE_HEADER_SETS=${_available_header_sets}" + ) + foreach(_install_header_set IN LISTS _available_header_sets) + list( + APPEND _install_header_set_args + FILE_SET + "${_install_header_set}" + COMPONENT + "${install_component_name}_Development" + ) + endforeach() + else() + set(_install_header_set_args FILE_SET HEADERS) # Note: empty FILE_SET in this case! CK + endif() + + # Detect presence of PUBLIC C++ module file sets. Note: exact one is expected! + get_target_property(_module_sets "${_tgt}" INTERFACE_CXX_MODULE_SETS) + if(_module_sets) + message( + VERBOSE + "beman-install-library(${name}): '${_tgt}' has INTERFACE_CXX_MODULE_SETS=${_module_sets}" + ) + install( + TARGETS "${_tgt}" + EXPORT ${BEMAN_INSTALL_EXPORT_NAME} + ARCHIVE COMPONENT "${install_component_name}_Development" + LIBRARY + COMPONENT "${install_component_name}_Runtime" + NAMELINK_COMPONENT "${install_component_name}_Development" + RUNTIME COMPONENT "${install_component_name}_Runtime" + ${_install_header_set_args} + FILE_SET ${_module_sets} + DESTINATION "${BEMAN_INSTALL_DESTINATION}" + COMPONENT "${install_component_name}_Development" + # NOTE: There's currently no convention for this location! CK + CXX_MODULES_BMI + DESTINATION + ${_config_install_dir}/bmi-${CMAKE_CXX_COMPILER_ID}_$ + COMPONENT "${install_component_name}_Development" + ) + else() + install( + TARGETS "${_tgt}" + EXPORT ${BEMAN_INSTALL_EXPORT_NAME} + ARCHIVE COMPONENT "${install_component_name}_Development" + LIBRARY + COMPONENT "${install_component_name}_Runtime" + NAMELINK_COMPONENT "${install_component_name}_Development" + RUNTIME COMPONENT "${install_component_name}_Runtime" + ${_install_header_set_args} + ) + endif() + endforeach() + + # -------------------------------------------------- + # Export targets + # -------------------------------------------------- + # gersemi: off + install( + EXPORT ${BEMAN_INSTALL_EXPORT_NAME} + NAMESPACE ${BEMAN_INSTALL_NAMESPACE} + CXX_MODULES_DIRECTORY cxx-modules + DESTINATION ${_config_install_dir} + COMPONENT "${install_component_name}_Development" + ) + # gersemi: on + + # ---------------------------------------- + # Config file installation logic + # + # Precedence (highest to lowest): + # 1. Per-package variable _INSTALL_CONFIG_FILE_PACKAGE + # 2. Allow-list BEMAN_INSTALL_CONFIG_FILE_PACKAGES (if defined) + # 3. Default: ON + # ---------------------------------------- + string(TOUPPER "${name}" _pkg_upper) + string(REPLACE "." "_" _pkg_prefix "${_pkg_upper}") + + option( + ${_pkg_prefix}_INSTALL_CONFIG_FILE_PACKAGE + "Enable creating and installing a CMake config-file package. Default: ON. Values: { ON, OFF }." + ON + ) + + set(_pkg_var "${_pkg_prefix}_INSTALL_CONFIG_FILE_PACKAGE") + + # Default: install config files + set(_install_config ON) + + # If the allow-list is defined, only install for packages in the list + if(DEFINED BEMAN_INSTALL_CONFIG_FILE_PACKAGES) + if(NOT "${name}" IN_LIST BEMAN_INSTALL_CONFIG_FILE_PACKAGES) + set(_install_config OFF) + endif() + endif() + + # Per-package override takes highest precedence + if(DEFINED ${_pkg_var}) + set(_install_config ${${_pkg_var}}) + endif() + + # ---------------------------------------- + # expand dependencies + # ---------------------------------------- + set(_beman_find_deps "") + foreach(dep IN LISTS BEMAN_INSTALL_DEPENDENCIES) + message( + VERBOSE + "beman-install-library(${name}): Add find_dependency(${dep})" + ) + string(APPEND _beman_find_deps "find_dependency(${dep})\n") + endforeach() + set(BEMAN_INSTALL_FIND_DEPENDENCIES "${_beman_find_deps}") + + # ---------------------------------------- + # Generate + install config files + # ---------------------------------------- + if(_install_config) + set(BEMAN_INSTALL_BASE_PKG_NAME ${name}) + configure_package_config_file( + "${CMAKE_CURRENT_FUNCTION_LIST_DIR}/Config.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/${name}-config.cmake" + INSTALL_DESTINATION ${_config_install_dir} + ) + + write_basic_package_version_file( + "${CMAKE_CURRENT_BINARY_DIR}/${name}-config-version.cmake" + VERSION ${PROJECT_VERSION} + COMPATIBILITY SameMajorVersion + ) + + install( + FILES + "${CMAKE_CURRENT_BINARY_DIR}/${name}-config.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/${name}-config-version.cmake" + DESTINATION ${_config_install_dir} + COMPONENT "${install_component_name}_Development" + ) + else() + message( + WARNING + "beman-install-library(${name}): Not installing a config package for '${name}'" + ) + endif() +endfunction() + +set(CPACK_GENERATOR TGZ) +include(CPack) diff --git a/cmake/gnu-toolchain.cmake b/infra/cmake/gnu-toolchain.cmake similarity index 90% rename from cmake/gnu-toolchain.cmake rename to infra/cmake/gnu-toolchain.cmake index b6dddf6..d3b9f92 100644 --- a/cmake/gnu-toolchain.cmake +++ b/infra/cmake/gnu-toolchain.cmake @@ -36,3 +36,6 @@ set(CMAKE_CXX_FLAGS_RELWITHDEBINFO_INIT "${RELEASE_FLAGS}") set(CMAKE_C_FLAGS_RELEASE_INIT "${RELEASE_FLAGS}") set(CMAKE_CXX_FLAGS_RELEASE_INIT "${RELEASE_FLAGS}") + +# Add this dir to the module path so that `find_package(beman-install-library)` works +list(APPEND CMAKE_PREFIX_PATH "${CMAKE_CURRENT_LIST_DIR}") diff --git a/infra/cmake/llvm-libc++-toolchain.cmake b/infra/cmake/llvm-libc++-toolchain.cmake new file mode 100644 index 0000000..76264c6 --- /dev/null +++ b/infra/cmake/llvm-libc++-toolchain.cmake @@ -0,0 +1,20 @@ +# SPDX-License-Identifier: BSL-1.0 + +# This toolchain file is not meant to be used directly, +# but to be invoked by CMake preset and GitHub CI. +# +# This toolchain file configures for LLVM family of compiler. +# +# BEMAN_BUILDSYS_SANITIZER: +# This optional CMake parameter is not meant for public use and is subject to +# change. +# Possible values: +# - MaxSan: configures clang and clang++ to use all available non-conflicting +# sanitizers. +# - TSan: configures clang and clang++ to enable the use of thread sanitizer. + +include(${CMAKE_CURRENT_LIST_DIR}/llvm-toolchain.cmake) + +if(NOT CMAKE_CXX_FLAGS MATCHES "-stdlib=libc\\+\\+") + string(APPEND CMAKE_CXX_FLAGS " -stdlib=libc++") +endif() diff --git a/cmake/llvm-toolchain.cmake b/infra/cmake/llvm-toolchain.cmake similarity index 90% rename from cmake/llvm-toolchain.cmake rename to infra/cmake/llvm-toolchain.cmake index 5f5ee4b..f1623b7 100644 --- a/cmake/llvm-toolchain.cmake +++ b/infra/cmake/llvm-toolchain.cmake @@ -36,3 +36,6 @@ set(CMAKE_CXX_FLAGS_RELWITHDEBINFO_INIT "${RELEASE_FLAGS}") set(CMAKE_C_FLAGS_RELEASE_INIT "${RELEASE_FLAGS}") set(CMAKE_CXX_FLAGS_RELEASE_INIT "${RELEASE_FLAGS}") + +# Add this dir to the module path so that `find_package(beman-install-library)` works +list(APPEND CMAKE_PREFIX_PATH "${CMAKE_CURRENT_LIST_DIR}") diff --git a/cmake/msvc-toolchain.cmake b/infra/cmake/msvc-toolchain.cmake similarity index 87% rename from cmake/msvc-toolchain.cmake rename to infra/cmake/msvc-toolchain.cmake index 2802982..bdc24de 100644 --- a/cmake/msvc-toolchain.cmake +++ b/infra/cmake/msvc-toolchain.cmake @@ -20,10 +20,7 @@ include_guard(GLOBAL) set(CMAKE_C_COMPILER cl) set(CMAKE_CXX_COMPILER cl) -# Off until I figure out how I want to handle vcpkg (dependencies also need to -# be built with address sanitizer) -# if(BEMAN_BUILDSYS_SANITIZER STREQUAL "MaxSan") -if(OFF) +if(BEMAN_BUILDSYS_SANITIZER STREQUAL "MaxSan") # /Zi flag (add debug symbol) is needed when using address sanitizer # See C5072: https://learn.microsoft.com/en-us/cpp/error-messages/compiler-warnings/compiler-warning-c5072 set(SANITIZER_FLAGS "/fsanitize=address /Zi") @@ -39,3 +36,6 @@ set(CMAKE_CXX_FLAGS_RELWITHDEBINFO_INIT "${RELEASE_FLAGS}") set(CMAKE_C_FLAGS_RELEASE_INIT "${RELEASE_FLAGS}") set(CMAKE_CXX_FLAGS_RELEASE_INIT "${RELEASE_FLAGS}") + +# Add this dir to the module path so that `find_package(beman-install-library)` works +list(APPEND CMAKE_PREFIX_PATH "${CMAKE_CURRENT_LIST_DIR}") diff --git a/infra/cmake/telemetry.sh b/infra/cmake/telemetry.sh new file mode 100755 index 0000000..307cc94 --- /dev/null +++ b/infra/cmake/telemetry.sh @@ -0,0 +1,118 @@ +#!/usr/bin/env bash + +set -o nounset +set -o errexit +trap 'echo "Aborting due to errexit on line $LINENO. Exit code: $?" >&2' ERR +set -o errtrace +set -o pipefail +IFS=$'\n\t' + +############################################################################### +# Environment +############################################################################### + +# $_ME +# +# This program's basename. +_ME="$(basename "${0}")" + +############################################################################### +# Help +############################################################################### + +# _print_help() +# +# Usage: +# _print_help +# +# Print the program help information. +_print_help() { + cat <] + ${_ME} -h | --help + +Options: + -h --help Show this screen. + +Environment: + Setting DEBUG_TELEMETRY in the environment will enable DEBUG logging +HEREDOC +} + +############################################################################### +# Program Functions +############################################################################### +_debug_print() { + if [[ -n "${DEBUG_TELEMETRY:-}" ]]; then + printf "[DEBUG] $(date +'%H:%M:%S'): %s \n" "$1" >&2 + fi +} + +_check_file_exists() { + local file="$1" + if [[ ! -f "${file}" ]]; then + echo "Error: File not found: ${file}" >&2 + exit 1 # Exit the entire script with a non-zero status + fi +} + +_process_index() { + indexFile=${1:-} + _check_file_exists "${indexFile}" + _debug_print "$(cat "${indexFile}")" + + local buildDir + buildDir=$(jq -r '.buildDir' "${1:-}") + _debug_print "$(printf "buildDir is |%q|" "${buildDir}")" + + local dataDir + dataDir=$(jq -r '.dataDir' "${1:-}") + _debug_print "$(printf "dataDir is |%q|" "${dataDir}")" + + local hook + hook=$(jq -r '.hook' "${1:-}") + _debug_print "$(printf "hook is |%q|" "${hook}")" + + local trace + trace=$(jq -r '.trace' "${1:-}") + _debug_print "$(printf "trace is |%q|" "${trace}")" + + local outputDir + outputDir="${buildDir}/.trace" + _debug_print "$(printf "Copy trace to |%q|" "${outputDir}")" + mkdir -p "${outputDir}" + + local traceDestFile + traceDestFile="${outputDir}/${hook}-$(basename "${trace}")" + _debug_print "$(printf "traceDestFile: |%q|" "${traceDestFile}")" + cp "${dataDir}/${trace}" "${outputDir}/${hook}-$(basename "${trace}")" +} + +############################################################################### +# Main +############################################################################### + +# _main() +# +# Usage: +# _main [] [] +# +# Description: +# Entry point for the program, handling basic option parsing and dispatching. +_main() { + # Avoid complex option parsing when only one program option is expected. + if [[ "${1:-}" =~ ^-h|--help$ ]] + then + _print_help + else + _process_index "$@" + fi +} + +# Call `_main` after everything has been defined. +_main "$@" diff --git a/infra/cmake/use-fetch-content.cmake b/infra/cmake/use-fetch-content.cmake new file mode 100644 index 0000000..f2428b5 --- /dev/null +++ b/infra/cmake/use-fetch-content.cmake @@ -0,0 +1,188 @@ +cmake_minimum_required(VERSION 3.24) + +include(FetchContent) + +if(NOT BEMAN_EXEMPLAR_LOCKFILE) + set(BEMAN_EXEMPLAR_LOCKFILE + "lockfile.json" + CACHE FILEPATH + "Path to the dependency lockfile for the Beman Exemplar." + ) +endif() + +set(BemanExemplar_projectDir "${CMAKE_CURRENT_LIST_DIR}/../..") +message(TRACE "BemanExemplar_projectDir=\"${BemanExemplar_projectDir}\"") + +message(TRACE "BEMAN_EXEMPLAR_LOCKFILE=\"${BEMAN_EXEMPLAR_LOCKFILE}\"") +file( + REAL_PATH "${BEMAN_EXEMPLAR_LOCKFILE}" + BemanExemplar_lockfile + BASE_DIRECTORY "${BemanExemplar_projectDir}" + EXPAND_TILDE +) +message(DEBUG "Using lockfile: \"${BemanExemplar_lockfile}\"") + +# Force CMake to reconfigure the project if the lockfile changes +set_property( + DIRECTORY "${BemanExemplar_projectDir}" + APPEND + PROPERTY CMAKE_CONFIGURE_DEPENDS "${BemanExemplar_lockfile}" +) + +# For more on the protocol for this function, see: +# https://cmake.org/cmake/help/latest/command/cmake_language.html#provider-commands +function(BemanExemplar_provideDependency method package_name) + # Read the lockfile + file(READ "${BemanExemplar_lockfile}" BemanExemplar_rootObj) + + # Get the "dependencies" field and store it in BemanExemplar_dependenciesObj + string( + JSON BemanExemplar_dependenciesObj + ERROR_VARIABLE BemanExemplar_error + GET "${BemanExemplar_rootObj}" + "dependencies" + ) + if(BemanExemplar_error) + message(FATAL_ERROR "${BemanExemplar_lockfile}: ${BemanExemplar_error}") + endif() + + # Get the length of the libraries array and store it in BemanExemplar_dependenciesObj + string( + JSON BemanExemplar_numDependencies + ERROR_VARIABLE BemanExemplar_error + LENGTH "${BemanExemplar_dependenciesObj}" + ) + if(BemanExemplar_error) + message(FATAL_ERROR "${BemanExemplar_lockfile}: ${BemanExemplar_error}") + endif() + + if(BemanExemplar_numDependencies EQUAL 0) + return() + endif() + + # Loop over each dependency object + math(EXPR BemanExemplar_maxIndex "${BemanExemplar_numDependencies} - 1") + foreach(BemanExemplar_index RANGE "${BemanExemplar_maxIndex}") + set(BemanExemplar_errorPrefix + "${BemanExemplar_lockfile}, dependency ${BemanExemplar_index}" + ) + + # Get the dependency object at BemanExemplar_index + # and store it in BemanExemplar_depObj + string( + JSON BemanExemplar_depObj + ERROR_VARIABLE BemanExemplar_error + GET "${BemanExemplar_dependenciesObj}" + "${BemanExemplar_index}" + ) + if(BemanExemplar_error) + message( + FATAL_ERROR + "${BemanExemplar_errorPrefix}: ${BemanExemplar_error}" + ) + endif() + + # Get the "name" field and store it in BemanExemplar_name + string( + JSON BemanExemplar_name + ERROR_VARIABLE BemanExemplar_error + GET "${BemanExemplar_depObj}" + "name" + ) + if(BemanExemplar_error) + message( + FATAL_ERROR + "${BemanExemplar_errorPrefix}: ${BemanExemplar_error}" + ) + endif() + + # Get the "package_name" field and store it in BemanExemplar_pkgName + string( + JSON BemanExemplar_pkgName + ERROR_VARIABLE BemanExemplar_error + GET "${BemanExemplar_depObj}" + "package_name" + ) + if(BemanExemplar_error) + message( + FATAL_ERROR + "${BemanExemplar_errorPrefix}: ${BemanExemplar_error}" + ) + endif() + + # Get the "git_repository" field and store it in BemanExemplar_repo + string( + JSON BemanExemplar_repo + ERROR_VARIABLE BemanExemplar_error + GET "${BemanExemplar_depObj}" + "git_repository" + ) + if(BemanExemplar_error) + message( + FATAL_ERROR + "${BemanExemplar_errorPrefix}: ${BemanExemplar_error}" + ) + endif() + + # Get the "git_tag" field and store it in BemanExemplar_tag + string( + JSON BemanExemplar_tag + ERROR_VARIABLE BemanExemplar_error + GET "${BemanExemplar_depObj}" + "git_tag" + ) + if(BemanExemplar_error) + message( + FATAL_ERROR + "${BemanExemplar_errorPrefix}: ${BemanExemplar_error}" + ) + endif() + + if(method STREQUAL "FIND_PACKAGE") + if(package_name STREQUAL BemanExemplar_pkgName) + string( + APPEND BemanExemplar_debug + "Redirecting find_package calls for ${BemanExemplar_pkgName} " + "to FetchContent logic.\n" + ) + string( + APPEND BemanExemplar_debug + "Fetching ${BemanExemplar_repo} at " + "${BemanExemplar_tag} according to ${BemanExemplar_lockfile}." + ) + message(DEBUG "${BemanExemplar_debug}") + FetchContent_Declare( + "${BemanExemplar_name}" + GIT_REPOSITORY "${BemanExemplar_repo}" + GIT_TAG "${BemanExemplar_tag}" + EXCLUDE_FROM_ALL + ) + set(INSTALL_GTEST OFF) # Disable GoogleTest installation + FetchContent_MakeAvailable("${BemanExemplar_name}") + + # Catch2's CTest integration module isn't on CMAKE_MODULE_PATH + # when brought in via FetchContent. Add it so that + # `include(Catch)` works. + if(BemanExemplar_pkgName STREQUAL "Catch2") + list( + APPEND CMAKE_MODULE_PATH + "${${BemanExemplar_name}_SOURCE_DIR}/extras" + ) + set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH}" PARENT_SCOPE) + endif() + + # Important! _FOUND tells CMake that `find_package` is + # not needed for this package anymore + set("${BemanExemplar_pkgName}_FOUND" TRUE PARENT_SCOPE) + endif() + endif() + endforeach() +endfunction() + +cmake_language( + SET_DEPENDENCY_PROVIDER BemanExemplar_provideDependency + SUPPORTED_METHODS FIND_PACKAGE +) + +# Add this dir to the module path so that `find_package(beman-install-library)` works +list(APPEND CMAKE_PREFIX_PATH "${CMAKE_CURRENT_LIST_DIR}") diff --git a/lockfile.json b/lockfile.json new file mode 100644 index 0000000..a1f3d8f --- /dev/null +++ b/lockfile.json @@ -0,0 +1,10 @@ +{ + "dependencies": [ + { + "name": "Catch2", + "package_name": "Catch2", + "git_repository": "https://github.com/catchorg/Catch2.git", + "git_tag": "25319fd3047c6bdcf3c0170e76fa526c77f99ca9" + } + ] +} diff --git a/tests/beman/bounds_test/CMakeLists.txt b/tests/beman/bounds_test/CMakeLists.txt index 677f22b..1ea5f00 100644 --- a/tests/beman/bounds_test/CMakeLists.txt +++ b/tests/beman/bounds_test/CMakeLists.txt @@ -1,14 +1,13 @@ # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -find_package(Catch2 3 REQUIRED CONFIG) +find_package(Catch2 3 REQUIRED) -add_executable(beman.bounds_test.tests) -target_sources(beman.bounds_test.tests PRIVATE bounds_test.tests.cpp) -target_compile_features(beman.bounds_test.tests PRIVATE cxx_std_20) +add_executable(beman.bounds_test.tests.bounds_test) +target_sources(beman.bounds_test.tests.bounds_test PRIVATE bounds_test.test.cpp) target_link_libraries( - beman.bounds_test.tests + beman.bounds_test.tests.bounds_test PRIVATE beman::bounds_test Catch2::Catch2WithMain ) include(Catch) -catch_discover_tests(beman.bounds_test.tests) +catch_discover_tests(beman.bounds_test.tests.bounds_test) diff --git a/vcpkg.json b/vcpkg.json deleted file mode 100644 index 78394b8..0000000 --- a/vcpkg.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "name": "beman-bounds-test", - "version": "0.0.1", - "description": "A library for checking integer operation boundary conditions", - "maintainers": [ - "Rishyak Panchal ", - "Vito Gamberini " - ], - "license": "Apache-2.0 WITH LLVM-exception", - "dependencies": [ - "catch2" - ], - "vcpkg-configuration": { - "default-registry": { - "kind": "git", - "baseline": "7e21420f775f72ae938bdeb5e6068f722088f06a", - "repository": "https://github.com/microsoft/vcpkg.git" - } - } -}