diff --git a/.ci/docker/rockylinux.Dockerfile b/.ci/docker/rockylinux.Dockerfile index 9821de2..c9c17d3 100644 --- a/.ci/docker/rockylinux.Dockerfile +++ b/.ci/docker/rockylinux.Dockerfile @@ -1,29 +1,34 @@ +# SPDX-License-Identifier: MIT + FROM rockylinux:9 -# Enable EPEL +# Enable EPEL. RUN dnf update -y RUN dnf install -y 'dnf-command(config-manager)' RUN dnf config-manager --set-enabled crb -y RUN dnf install epel-release -y -# Install dependencies +# Install dependencies. RUN dnf install -y \ clang \ g++ \ ninja-build \ - cmake + cmake \ + git RUN dnf clean all -# Copy code +# Copy code. WORKDIR /workarea COPY ./ ./ +# Set build arguments. ARG cc=gcc ARG cxx=g++ ARG cmake_args= +# Build. ENV CC="$cc" CXX="$cxx" CMAKE_GENERATOR="Ninja" CMAKE_EXPORT_COMPILE_COMMANDS=on -RUN cmake -B build -S . "$cmake_args" && \ - cmake --build build --verbose && \ - DESTDIR=build/staging cmake --install build --prefix /opt/example --component libexample-dev && \ - find build/staging -type f +RUN cmake -B build -S . "$cmake_args" +RUN cmake --build build --verbose +RUN DESTDIR=build/staging cmake --install build --prefix /opt/beman/example --component libbeman_example-dev +RUN find build/staging -type f diff --git a/.ci/docker/ubuntu.Dockerfile b/.ci/docker/ubuntu.Dockerfile index fbfe4ed..a3d4f33 100644 --- a/.ci/docker/ubuntu.Dockerfile +++ b/.ci/docker/ubuntu.Dockerfile @@ -1,19 +1,23 @@ -# Using a non-LTS Ubuntu, just until CMake 3.23 is available on Ubuntu 24.04 +# SPDX-License-Identifier: MIT + +# Using a non-LTS Ubuntu, just until CMake 3.23 is available on Ubuntu 24.04. FROM ubuntu:23.10 -# Install dependencies +# Install dependencies, RUN apt-get update RUN apt-get install -y \ clang \ clang-tidy \ g++ \ ninja-build \ - cmake + cmake \ + git RUN apt-get clean WORKDIR /workarea COPY ./ ./ +# Set build arguments. ARG cc=gcc ARG cxx=g++ ARG cmake_args= @@ -21,8 +25,9 @@ ARG cmake_args= # Workaround Ubuntu broken ASan RUN sysctl vm.mmap_rnd_bits=28 +# Build. ENV CC="$cc" CXX="$cxx" CMAKE_GENERATOR="Ninja" CMAKE_EXPORT_COMPILE_COMMANDS=on -RUN cmake -B build -S . "$cmake_args" && \ - cmake --build build --verbose && \ - DESTDIR=build/staging cmake --install build --prefix /opt/example --component libexample-dev && \ - find build/staging -type f +RUN cmake -B build -S . "$cmake_args" +RUN cmake --build build --verbose +RUN DESTDIR=build/staging cmake --install build --prefix /opt/beman/example --component libbeman_example-dev +RUN find build/staging -type f diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..58d82b8 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,3 @@ +# Codeowners for reviews on PRs + +* @bbrown105 @dustingooding @JamesAdkison @neatudarius diff --git a/.github/workflows/ci_tests.yml b/.github/workflows/ci_tests.yml new file mode 100644 index 0000000..ab056bc --- /dev/null +++ b/.github/workflows/ci_tests.yml @@ -0,0 +1,84 @@ +# SPDX-License-Identifier: MIT + +name: CI Test + +on: + push: + branches: [ main ] + paths: + - "src/**" + - "test/**" + - "CMakeLists.txt" + pull_request: + branches: [ main ] + paths: + - "src/**" + - "test/**" + - "CMakeLists.txt" + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +jobs: + test: + runs-on: ubuntu-latest + strategy: + matrix: + cfg: + - { id: ubuntu-gcc-werror, platform: ubuntu, cc: gcc, cpp: g++, cmake_args: "-DCMAKE_CXX_FLAGS='-Werror=all -Werror=extra'"} + - { id: ubuntu-gcc-aubsan, platform: ubuntu, cc: gcc, cpp: g++, cmake_args: "-DCMAKE_CXX_FLAGS=-fsanitize=address -fsanitize=undefined"} + - { id: ubuntu-gcc-tsan, platform: ubuntu, cc: gcc, cpp: g++, cmake_args: "-DCMAKE_CXX_FLAGS=-fsanitize=thread"} + - { id: ubuntu-gcc-static, platform: ubuntu, cc: gcc, cpp: g++, cmake_args: ""} + - { id: ubuntu-gcc-static-cxx98, platform: ubuntu, cc: gcc, cpp: g++, cmake_args: "-DCMAKE_CXX_STANDARD=98 -DCMAKE_CXX_STANDARD_REQUIRED=on"} + - { id: ubuntu-gcc-static-cxx11, platform: ubuntu, cc: gcc, cpp: g++, cmake_args: "-DCMAKE_CXX_STANDARD=11 -DCMAKE_CXX_STANDARD_REQUIRED=on"} + - { id: ubuntu-gcc-static-cxx14, platform: ubuntu, cc: gcc, cpp: g++, cmake_args: "-DCMAKE_CXX_STANDARD=14 -DCMAKE_CXX_STANDARD_REQUIRED=on"} + - { id: ubuntu-gcc-static-cxx17, platform: ubuntu, cc: gcc, cpp: g++, cmake_args: "-DCMAKE_CXX_STANDARD=17 -DCMAKE_CXX_STANDARD_REQUIRED=on"} + - { id: ubuntu-gcc-static-cxx20, platform: ubuntu, cc: gcc, cpp: g++, cmake_args: "-DCMAKE_CXX_STANDARD=20 -DCMAKE_CXX_STANDARD_REQUIRED=on"} + - { id: ubuntu-gcc-static-cxx23, platform: ubuntu, cc: gcc, cpp: g++, cmake_args: "-DCMAKE_CXX_STANDARD=23 -DCMAKE_CXX_STANDARD_REQUIRED=on"} + - { id: ubuntu-gcc-static-cxx26, platform: ubuntu, cc: gcc, cpp: g++, cmake_args: "-DCMAKE_CXX_STANDARD=26 -DCMAKE_CXX_STANDARD_REQUIRED=on"} + - { id: ubuntu-gcc-dynamic, platform: ubuntu, cc: gcc, cpp: g++, cmake_args: "-DBUILD_SHARED_LIBS=on"} + - { id: ubuntu-clang-static, platform: ubuntu, cc: clang, cpp: clang++, cmake_args: ""} + - { id: ubuntu-clang-dynamic, platform: ubuntu, cc: clang, cpp: clang++, cmake_args: "-DBUILD_SHARED_LIBS=on"} + # Disabling Rocky until it catches up on CMake versions or a way to sideload newer version of CMake + # is identified. + # - { id: rockylinux-gcc-static, platform: rockylinux, cc: gcc, cpp: g++, cmake_args: ""} + # - { id: rockylinux-clang-static, platform: rockylinux, cc: clang, cpp: clang++, cmake_args: ""} + + steps: + - uses: actions/checkout@v2 + # GitHub runners have updated the Ubuntu Linux Kernel to use strong ASLR, + # but LLVM is not configured for this change, and thus the address + # sanitizer breaks. + # + # The next image is supposed to fix this, so if the Ubuntu image has been + # updated, this work around is no longer required. + - name: get runner image version + id: runner-image-version + run: | + echo "image-version=$(echo $ImageVersion)" >> "$GITHUB_OUTPUT" + working-directory: . + - name: modify number of bits to use for ASLR entropy + if: ${{ steps.runner-image-version.outputs.ImageVersion }} == '20240310.1.0' + run: | + sudo sysctl -a | grep vm.mmap.rnd + sudo sysctl -w vm.mmap_rnd_bits=28 + working-directory: . + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Build and push + uses: docker/build-push-action@v5 + with: + context: . + push: false + load: true + tags: ${{ matrix.cfg.id }} + file: .ci/docker/${{ matrix.cfg.platform }}.Dockerfile + build-args: | + cc=${{ matrix.cfg.cc }} + cxx=${{ matrix.cfg.cpp }} + cmake_args=${{ matrix.cfg.cmake_args }} + cache-from: type=gha + cache-to: type=gha,mode=max + - name: Run tests + run: | + docker run ${{ matrix.cfg.id }} ctest --test-dir build diff --git a/.github/workflows/cxx.yml b/.github/workflows/cxx.yml index 2ac2e49..3a588b1 100644 --- a/.github/workflows/cxx.yml +++ b/.github/workflows/cxx.yml @@ -1,4 +1,3 @@ -# Copyright © 2024 Bret Brown # SPDX-License-Identifier: MIT name: Test diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..25fc4b4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +cmake.bld +compile_commands.json +/.build/ +/build/ +/.cache/ +*.pyg +.update-submodules +.vscode/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 9e06dca..f5e91cb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,16 +1,32 @@ -# Copyright © 2024 Bret Brown # SPDX-License-Identifier: MIT cmake_minimum_required(VERSION 3.23) -project(example + +project(beman_example VERSION 1.0.0 DESCRIPTION "An example Beman library" LANGUAGES CXX ) -include(CTest) -add_subdirectory(src/example) +# Include the CTest module. +include(CTest) +# Build the tests only if enabled via the CLI flag: BUILD_TESTING. if (BUILD_TESTING) - add_subdirectory(test/example) + enable_testing() + + # Fetch GoogleTest + include(FetchContent) + FetchContent_Declare( + googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG f8d7d77c06936315286eb55f8de22cd23c188571 # release-1.14.0 + ) + FetchContent_MakeAvailable(googletest) endif () + +# Build the library. +add_subdirectory(src/Beman/Example) + +# Build the examples. +add_subdirectory(examples) diff --git a/LICENSE.txt b/LICENSE.txt index 3fa2497..9cf1062 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,7 +1,5 @@ MIT License -Copyright (c) 2024 Bret Brown - Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights diff --git a/README.md b/README.md index 69fcafa..81b2b7a 100644 --- a/README.md +++ b/README.md @@ -1,33 +1,51 @@ <!-- -Copyright © 2024 Bret Brown SPDX-License-Identifier: CC0-1.0 --> -# example +# Beman.Example -## About +![CI Tests](https://github.com/beman-project/Example/actions/workflows/ci_tests.yml/badge.svg) -`example` is an example Beman library. +`Beman.Example` is an example Beman library. `Beman.Example` is useful for nothing, though it might contain value as an experiment in modern and minimal C++ project structure. Please check [The Beman Standard](https://github.com/beman-project/beman/blob/main/docs/beman-standard.md). -`example` is useful for nothing, though it might contain value as an experiment in modern and minimal C++ project structure. +Implements: N/A for `Beman.Example`. +## License + +Source and docs are licenced with CC0 1.0 Universal. Copy the contents and incorporate in your own work as you see fit. + +// SPDX-License-Identifier: CC0-1.0 ## Building ### Dependencies -This project is mainly tested on Ubuntu 22.04, but it should be as portable as CMake is. +This project is mainly tested on Ubuntu `22.04` and `24.04`, but it should be as portable as CMake is. -This project has zero C or C++ depenendencies. +This project has no C or C++ dependencies. -It does require two tools as build-time dependencies: +Build-time dependencies: -- `cmake` +- `cmake` - `ninja`, `make`, or another CMake-supported build system - CMake defaults to "Unix Makefiles" on POSIX systems +Example of installation on `Ubuntu 24.04`: + +```shell +# Install tools: +apt-get install -y cmake make ninja-build + +# Example of toolchains: +apt-get install \ + g++-14 gcc-14 gcc-13 g++-14 \ + clang-18 clang++-18 clang-17 clang++-17 +``` + ### Instructions +Full set of supported toolchains can be found in [.github/workflows/ci_test.yml](.github/workflows/ci_test.yml). + #### Basic Build This project strives to be as normal and simple a CMake project as possible. This build workflow in particular will work, producing a static `example` library, ready to package: @@ -37,9 +55,52 @@ cmake -B /some/build/dir -S . cmake --build /some/build/dir ctest --test-dir /some/build/dir \ --output-junit build/xunit/results.xml -DESTDIR=/some/staging/dir cmake --install /some/build/dir --component libexample-dev --prefix /opt/example +DESTDIR=/some/staging/dir cmake --install /some/build/dir --component libbeman_example-dev --prefix /opt/example ``` +<details> +<summary> Build example </summary> + +```shell +# Configure example. +$ cmake -B .build -S . +-- The CXX compiler identification is GNU 13.2.0 +-- Detecting CXX compiler ABI info +-- Detecting CXX compiler ABI info - done +-- Check for working CXX compiler: /usr/bin/c++ - skipped +-- Detecting CXX compile features +-- Detecting CXX compile features - done +-- Configuring done (0.1s) +-- Generating done (0.0s) +-- Build files have been written to: /home/dariusn/git/Beman/Beman.Example/.build + +# Build example. +$ cmake --build .build/ +[ 14%] Built target gtest +[ 28%] Built target gmock +[ 42%] Built target gmock_main +[ 57%] Built target gtest_main +[ 71%] Built target beman_example +[ 78%] Building CXX object src/Beman/Example/tests/CMakeFiles/example_gtest.dir/example.t.cpp.o +[ 85%] Linking CXX executable example_gtest +[ 85%] Built target example_gtest +[100%] Built target sample_usage + +# Run tests example. +$ ctest --test-dir .build +Internal ctest changing into directory: /home/dariusn/git/Beman/Beman.Example/.build +Test project /home/dariusn/git/Beman/Beman.Example/.build + Start 1: ExampleTest.call_identity +1/1 Test #1: ExampleTest.call_identity ........ Passed 0.00 sec + +100% tests passed, 0 tests failed out of 1 + +# Run examples. +$ .build/examples/sample_usage +2024 +``` +</details> + If all of those steps complete successfully, you should see the library installed in your staging directory. An example command: @@ -63,6 +124,14 @@ You will see files like so: └── libexample.a ``` +#### Disable Tests Build + +To build this project with skiped tests and its dependencies, simply use `BUILD_TESTING=OFF` [as documented in upstream CMake documentation](https://cmake.org/cmake/help/latest/module/CTest.html: + +```shell +cmake -B /some/build/dir -S . -DBUILD_TESTING=OFF +``` + #### Manipulating Warnings To build this project with warnings enabled, simply use `CMAKE_CXX_FLAGS` [as documented in upstream CMake documentation](https://cmake.org/cmake/help/latest/variable/CMAKE_LANG_FLAGS.html): @@ -106,33 +175,32 @@ Otherwise follow the Basic Build workflow as described above. ### From C++ -If you *really* want to use `example` from your project (why???), you can include `example.hxx` from your C++ source files +If you *really* want to use `Beman.Example` from your project (why???), you can include `Beman/Example/example.hpp` from your C++ source files ```cxx -#include <example.hxx> +#include <Beman/Example/example.hpp> ``` -`example` supports C++98, C++03, C++11, C++14, C++17, C++20, and C++23. It has no known issues with C++26 or C++29, though there are no compilation toolchains available to test against in those build modes. +`Beman.Example` supports C++98, C++03, C++11, C++14, C++17, C++20, C+=23 and C++23. It has no known issues with C++29, though there are no compilation toolchains available to test against in those build modes. -Note that `example` is incidentally compatible with most C dialects, but that behavior is not regularly tested and should be considered unsupported. ### From CMake -For consumers using CMake, you will need to use the `example` CMake module to define the `example` CMake target: +For consumers using CMake, you will need to use the `beman_example` CMake module to define the `beman_example` CMake target: ```cmake -find_package(example REQUIRED) +find_package(beman_example REQUIRED) ``` -You will also need to add `example::example` to the link libraries of any libraries or executables that include `example.hxx` in their source or header file. +You will also need to add `beman::example` to the link libraries of any libraries or executables that include `example.hpp` in their source or header file. ```cmake -target_link_libraries(yourlib PUBLIC example::example) +target_link_libraries(yourlib PUBLIC beman::example) ``` ### From Other Build Systems -Build systems that support `pkg-config` by providing a `example.pc` file. Build systems that support interoperation via `pkg-config` should be able to detect `example` for you automatically. +Build systems that support `pkg-config` by providing a `beman_example.pc` file. Build systems that support interoperation via `pkg-config` should be able to detect `beman_example` for you automatically. ## Contributing diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt new file mode 100644 index 0000000..81e144f --- /dev/null +++ b/examples/CMakeLists.txt @@ -0,0 +1,23 @@ +# SPDX-License-Identifier: MIT + +# List of all buildable examples. +set(EXAMPLES + sample_usage +) + +foreach(EXAMPLE ${EXAMPLES}) + # Add example executable. + add_executable(${EXAMPLE} "") + + # Add example source file. + target_sources(${EXAMPLE} PRIVATE ${EXAMPLE}.cpp) + + # Link example with the library. + target_link_libraries(${EXAMPLE} beman::example) + + # Install . + install( + TARGETS ${EXAMPLE} + EXPORT ${TARGETS_EXPORT_NAME} + DESTINATION ${CMAKE_INSTALL_BINDIR}) +endforeach() diff --git a/examples/sample_usage.cpp b/examples/sample_usage.cpp new file mode 100644 index 0000000..216ef8e --- /dev/null +++ b/examples/sample_usage.cpp @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: MIT + +#include <Beman/Example/example.hpp> +#include <iostream> + +int main(int, char*[]) { + std::cout << beman::example::identity(2024) << std::endl; + return 0; +} diff --git a/include/Beman/Example/example.hpp b/include/Beman/Example/example.hpp new file mode 100644 index 0000000..20a0a78 --- /dev/null +++ b/include/Beman/Example/example.hpp @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: MIT + +namespace beman { +namespace example { +// C++ example API (C++98-26): `api` <=> identity function. +int identity(int x); + +} // namespace example +} // namespace beman diff --git a/src/Beman/Example/CMakeLists.txt b/src/Beman/Example/CMakeLists.txt new file mode 100644 index 0000000..dcde07c --- /dev/null +++ b/src/Beman/Example/CMakeLists.txt @@ -0,0 +1,52 @@ +# SPDX-License-Identifier: MIT + +# Add the library. +add_library(beman_example STATIC "") +add_library(beman::example ALIAS beman_example) + +# Using the full project version because example does not provide a stable ABI +# between shared library versions +set_property(TARGET beman_example PROPERTY SOVERSION "${PROJECT_VERSION}") + +# Set the C++ sources. +target_sources(beman_example PRIVATE + example.cpp +) +target_include_directories( + beman_example + PUBLIC + $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../../../include> + $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/${CMAKE_LOWER_PROJECT_NAME}> # <prefix>/include/scratch +) +# target_sources(example PUBLIC +# FILE_SET HEADERS +# BASE_DIRS ../../../ +# FILES ../../../include/ +# ) + +# GNUInstallDirs provides default definitions for CMAKE_INSTALL_LIBDIR +# See: https://cmake.org/cmake/help/latest/module/GNUInstallDirs.html +include(GNUInstallDirs) + +# Install the library. +install(TARGETS beman_example + EXPORT beman_example + ARCHIVE + COMPONENT libbeman_example-dev + FILE_SET HEADERS + COMPONENT libbeman_example-dev + LIBRARY + COMPONENT libbeman_example + NAMELINK_COMPONENT libbeman_example-dev +) +install(EXPORT beman_example + FILE beman_example-config.cmake + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${TARGET_LIBRARY}" + COMPONENT libbeman_example-dev + NAMESPACE beman:: +) + +# Build the tests only if enabled via the CLI flag: BUILD_TESTING. +if (BUILD_TESTING) + add_subdirectory(tests) +endif () diff --git a/src/Beman/Example/example.cpp b/src/Beman/Example/example.cpp new file mode 100644 index 0000000..ed9b78d --- /dev/null +++ b/src/Beman/Example/example.cpp @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: MIT + +#include <Beman/Example/example.hpp> + +namespace beman::example { + +int identity(int x) { return x; } + +} // namespace beman::example diff --git a/src/Beman/Example/tests/CMakeLists.txt b/src/Beman/Example/tests/CMakeLists.txt new file mode 100644 index 0000000..e160547 --- /dev/null +++ b/src/Beman/Example/tests/CMakeLists.txt @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: MIT + +# Include the GoogleTest CMake module. +include(GoogleTest) + +# Add the test executable. +add_executable(beman_example_test) + +# Set the C++ sources. +target_sources(beman_example_test PRIVATE + example.t.cpp +) + +# Link the library. +target_link_libraries(beman_example_test PRIVATE beman::example GTest::gtest GTest::gtest_main) + +# Add the tests. +gtest_add_tests(beman_example_test "" AUTO) diff --git a/src/Beman/Example/tests/example.t.cpp b/src/Beman/Example/tests/example.t.cpp new file mode 100644 index 0000000..897fa5f --- /dev/null +++ b/src/Beman/Example/tests/example.t.cpp @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT + +#include <Beman/Example/example.hpp> + +#include <gtest/gtest.h> + +TEST(ExampleTest, call_identity) { + for (int i = -100; i < 100; ++i) { + EXPECT_EQ(i, beman::example::identity(i)); + } +} diff --git a/src/example/CMakeLists.txt b/src/example/CMakeLists.txt deleted file mode 100644 index 1319831..0000000 --- a/src/example/CMakeLists.txt +++ /dev/null @@ -1,39 +0,0 @@ -# Copyright © 2024 Bret Brown -# SPDX-License-Identifier: MIT - -add_library(example) -add_library(example::example ALIAS example) - -# Using the full project version because example does not provide a stable ABI -# between shared library versions -set_property(TARGET example PROPERTY SOVERSION "${PROJECT_VERSION}") - -target_sources(example PRIVATE - example.cpp -) -target_sources(example PUBLIC - FILE_SET HEADERS - BASE_DIRS . - FILES example.hpp -) - -# GNUInstallDirs provides default definitions for CMAKE_INSTALL_LIBDIR -# See: https://cmake.org/cmake/help/latest/module/GNUInstallDirs.html -include(GNUInstallDirs) - -install(TARGETS example - EXPORT example - ARCHIVE - COMPONENT libexample-dev - FILE_SET HEADERS - COMPONENT libexample-dev - LIBRARY - COMPONENT libexample - NAMELINK_COMPONENT libexample-dev -) -install(EXPORT example - FILE example-config.cmake - DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/example" - COMPONENT libexample-dev - NAMESPACE example:: -) diff --git a/src/example/example.cpp b/src/example/example.cpp deleted file mode 100644 index 1aff651..0000000 --- a/src/example/example.cpp +++ /dev/null @@ -1,2 +0,0 @@ -// Copyright © 2024 Bret Brown -// SPDX-License-Identifier: MIT diff --git a/src/example/example.hpp b/src/example/example.hpp deleted file mode 100644 index 1aff651..0000000 --- a/src/example/example.hpp +++ /dev/null @@ -1,2 +0,0 @@ -// Copyright © 2024 Bret Brown -// SPDX-License-Identifier: MIT