diff --git a/.cmake-format.json b/.cmake-format.json new file mode 100644 index 0000000..d34f8ce --- /dev/null +++ b/.cmake-format.json @@ -0,0 +1,5 @@ +{ + "line_width": 120, + "tab_size": 4, + "max_subgroups_hwrap": 3 +} diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 5350c9b..56deee7 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,15 +1,17 @@ FROM ubuntu:24.04 ENV DEBIAN_FRONTEND=noninteractive +ARG USER=ubuntu -RUN apt-get update && apt-get install -y \ +# Install packages and clean up after +RUN apt-get update && apt-get install -y --no-install-recommends \ bash-completion \ build-essential \ cmake \ clang \ - clangd \ clang-format \ clang-tidy \ + clangd \ curl \ doxygen \ gdb \ @@ -19,19 +21,24 @@ RUN apt-get update && apt-get install -y \ lldb \ llvm \ nano \ - software-properties-common \ + openssh-client \ + pipx \ sudo \ unzip \ wget \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* + && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Configure user environment and permissions +RUN passwd -d ${USER} \ + && mkdir -p /home/${USER}/.cache \ + && chown -R ${USER}:${USER} /home/${USER}/.cache \ + && echo "export PROMPT_COMMAND='history -a'" >> /home/${USER}/.bashrc \ + && echo "export HISTFILE=/home/${USER}/.cache/.bash_history" >> /home/${USER}/.bashrc -ARG USERNAME=ubuntu -RUN passwd -d ${USERNAME} \ - && mkdir -p /home/${USERNAME}/.cache && chown -R ${USERNAME}:${USERNAME} /home/${USERNAME}/.cache \ - && SNIPPET="export PROMPT_COMMAND='history -a' && export HISTFILE=/home/${USERNAME}/.cache/.bash_history" \ - && echo "$SNIPPET" >> "/home/${USERNAME}/.bashrc" +USER ${USER} -USER $USERNAME +# Install cmake-format +RUN pipx install cmake-format --include-deps \ + && pipx ensurepath WORKDIR /workspace diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index e6cfd58..feb1264 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -11,7 +11,9 @@ "twxs.cmake", "fredericbonnet.cmake-test-adapter", "llvm-vs-code-extensions.vscode-clangd", - "vadimcn.vscode-lldb" + "vadimcn.vscode-lldb", + "cheshirekow.cmake-format", + "ms-vscode.cpptools" ] } }, diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index 1c7e4f5..65b0a8b 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -125,15 +125,21 @@ jobs: token: ${{ secrets.CODECOV_TOKEN }} files: ${{github.workspace}}/build/coverage.info - clang-format: - name: Clang Format + format: + name: Format runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - name: Run Clang Format - run: clang-format --dry-run --Werror $(find include tests examples -type f -name *.*pp) + - name: C++ + run: clang-format --dry-run --Werror $(find . -name *.*pp) + + - name: CMake + run: | + pip install cmake-format + cmake-format -i $(find -name CMakeLists.txt) + git diff --exit-code clang-tidy: name: Clang Tidy diff --git a/.vscode/cmake-kits.json b/.vscode/cmake-kits.json new file mode 100644 index 0000000..3de6a7e --- /dev/null +++ b/.vscode/cmake-kits.json @@ -0,0 +1,18 @@ +[ + { + "name": "GCC", + "compilers": { + "C": "/usr/bin/gcc", + "CXX": "/usr/bin/g++" + }, + "isTrusted": true + }, + { + "name": "Clang", + "compilers": { + "C": "/usr/bin/clang", + "CXX": "/usr/bin/clang++" + }, + "isTrusted": true + } +] diff --git a/.vscode/launch.json b/.vscode/launch.json index b10c7f7..19707e7 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -2,7 +2,24 @@ "version": "0.2.0", "configurations": [ { - "name": "Launch (LLDB)", + "name": "Launch GDB", + "type": "cppdbg", + "request": "launch", + "program": "${command:cmake.launchTargetPath}", + "args": [], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ] + }, + { + "name": "Launch LLDB", "type": "lldb", "request": "launch", "program": "${command:cmake.launchTargetPath}", diff --git a/.vscode/settings.json b/.vscode/settings.json index 38bae1b..d95052a 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,11 +1,12 @@ { "cmake.configureArgs": [ - "-DCPP_CHANNEL_BUILD_EXAMPLES=ON", + "-DCMAKE_CXX_STANDARD=11", "-DCPP_CHANNEL_BUILD_TESTS=ON", + "-DCPP_CHANNEL_BUILD_BENCHMARKS=ON", + "-DCPP_CHANNEL_BUILD_EXAMPLES=ON", "-DCPP_CHANNEL_COVERAGE=ON", "-DCPP_CHANNEL_SANITIZERS=ON", "-DCPP_CHANNEL_SANITIZE_THREADS=OFF", - "-DCMAKE_CXX_STANDARD=11", "-DCMAKE_INSTALL_PREFIX=${workspaceFolder}/install" ], "editor.formatOnSave": true, @@ -17,9 +18,43 @@ "[jsonc]": { "editor.defaultFormatter": "vscode.json-language-features" }, + "[cmake]": { + "editor.defaultFormatter": "cheshirekow.cmake-format" + }, "clangd.arguments": [ "--compile-commands-dir=${workspaceFolder}/build", "--background-index", "--clang-tidy" + ], + "C_Cpp.intelliSenseEngine": "disabled", + "cSpell.words": [ + "ARGN", + "clangd", + "cppcoreguidelines", + "cpupower", + "ctest", + "DCMAKE", + "DCPP", + "endfunction", + "ensurepath", + "fcoverage", + "fprofile", + "fsanitize", + "genhtml", + "googletest", + "graphviz", + "gtest", + "HISTFILE", + "hwrap", + "lgcov", + "lldb", + "ltsan", + "lubsan", + "MSVC", + "noninteractive", + "pipx", + "powersave", + "STREQUAL", + "TESTNAME" ] } diff --git a/CMakeLists.txt b/CMakeLists.txt index 06885ae..91d90a5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.12) +cmake_minimum_required(VERSION 3.15) project(cpp_channel) set(PROJECT_VERSION 1.3.0) @@ -15,17 +15,22 @@ add_library(msd_channel INTERFACE) target_include_directories(msd_channel INTERFACE include) option(CPP_CHANNEL_BUILD_TESTS "Build all of cpp_channel's own tests." OFF) +option(CPP_CHANNEL_BUILD_BENCHMARKS "Build all of cpp_channel's own benchmark tests." OFF) option(CPP_CHANNEL_BUILD_EXAMPLES "Build cpp_channel's example programs." OFF) option(CPP_CHANNEL_COVERAGE "Generate test coverage." OFF) option(CPP_CHANNEL_SANITIZERS "Build with sanitizers." OFF) option(CPP_CHANNEL_SANITIZE_THREADS "Build with thread sanitizer." OFF) -if (CPP_CHANNEL_BUILD_TESTS) +if(CPP_CHANNEL_BUILD_TESTS) enable_testing() add_subdirectory(tests) endif() -if (CPP_CHANNEL_BUILD_EXAMPLES) +if(CPP_CHANNEL_BUILD_BENCHMARKS) + add_subdirectory(benchmarks) +endif() + +if(CPP_CHANNEL_BUILD_EXAMPLES) add_subdirectory(examples) endif() diff --git a/Makefile b/Makefile index aae49df..7fcebd6 100644 --- a/Makefile +++ b/Makefile @@ -1,17 +1,23 @@ +PROJECT_DIRS = include tests examples +BUILD_DIR = build +BENCH_DIR = build/bench +COV_DIR = build/coverage +DOCS_DIR = docs + all: bench: - sudo cpupower frequency-set --governor performance - mkdir -p build/bench && cd build/bench \ - && cmake ../.. -DCMAKE_BUILD_TYPE=Release -DCPP_CHANNEL_BUILD_TESTS=ON \ + rm -rf $(BENCH_DIR) && mkdir $(BENCH_DIR) && cd $(BENCH_DIR) \ + && cmake ../.. -DCMAKE_BUILD_TYPE=Release -DCPP_CHANNEL_BUILD_BENCHMARKS=ON \ && cmake --build . --config Release --target channel_benchmark -j \ - && ./tests/channel_benchmark + && ./benchmarks/channel_benchmark - sudo cpupower frequency-set --governor powersave coverage: - mkdir -p build/coverage && cd build/coverage \ + rm -rf $(COV_DIR) && mkdir $(COV_DIR) && cd $(COV_DIR) \ && cmake ../.. -DCMAKE_BUILD_TYPE=Debug -DCPP_CHANNEL_BUILD_TESTS=ON -DCPP_CHANNEL_COVERAGE=ON \ && cmake --build . --config Debug --target channel_tests -j \ && ctest -C Debug --verbose -L channel_tests --output-on-failure -j \ @@ -24,4 +30,11 @@ coverage: doc: doxygen - cd docs && python3 -m http.server 8000 + cd $(DOCS_DIR) && python3 -m http.server 8000 + +format: + clang-format -i $(shell find $(PROJECT_DIRS) -name *.*pp) + cmake-format -i $(shell find $(PROJECT_DIRS) -name CMakeLists.txt) + +clean: + rm -rf $(BUILD_DIR) $(DOCS_DIR) diff --git a/benchmarks/.clang-tidy b/benchmarks/.clang-tidy new file mode 100644 index 0000000..2406173 --- /dev/null +++ b/benchmarks/.clang-tidy @@ -0,0 +1,9 @@ +InheritParentConfig: true + +Checks: > + -cppcoreguidelines-avoid-magic-numbers, + -readability-magic-numbers, + -fuchsia-default-arguments-calls + +CheckOptions: + - { key: readability-identifier-length.MinimumVariableNameLength, value: '1' } diff --git a/benchmarks/CMakeLists.txt b/benchmarks/CMakeLists.txt new file mode 100644 index 0000000..73c807e --- /dev/null +++ b/benchmarks/CMakeLists.txt @@ -0,0 +1,16 @@ +include(FetchContent) +if(NOT benchmark_POPULATED) + set(BENCHMARK_ENABLE_TESTING OFF CACHE BOOL "" FORCE) + set(BENCHMARK_ENABLE_GTEST_TESTS OFF CACHE BOOL "" FORCE) + FetchContent_Declare(benchmark URL https://github.com/google/benchmark/archive/refs/tags/v1.9.4.zip + DOWNLOAD_EXTRACT_TIMESTAMP TRUE) + FetchContent_MakeAvailable(benchmark) +endif() + +function(package_add_benchmark TESTNAME) + add_executable(${TESTNAME} ${ARGN}) + set_target_warnings(${TESTNAME} PRIVATE) + target_link_libraries(${TESTNAME} benchmark) +endfunction() + +package_add_benchmark(channel_benchmark channel_benchmark.cpp) diff --git a/tests/channel_benchmark.cpp b/benchmarks/channel_benchmark.cpp similarity index 73% rename from tests/channel_benchmark.cpp rename to benchmarks/channel_benchmark.cpp index c57df59..e5c6422 100644 --- a/tests/channel_benchmark.cpp +++ b/benchmarks/channel_benchmark.cpp @@ -20,12 +20,12 @@ ------------------------------------------------------------------------------ Benchmark Time CPU Iterations ------------------------------------------------------------------------------ - bm_channel_with_queue_storage 42602 ns 42598 ns 16407 - bm_channel_with_vector_storage 42724 ns 42723 ns 16288 - bm_channel_with_vector_storage 51332 ns 51328 ns 11776 + channel_with_queue_storage 42602 ns 42598 ns 16407 + channel_with_vector_storage 42724 ns 42723 ns 16288 + channel_with_vector_storage 51332 ns 51328 ns 11776 */ -static void bm_channel_with_queue_storage(benchmark::State& state) +static void channel_with_queue_storage(benchmark::State& state) { msd::channel> channel{10}; @@ -39,14 +39,14 @@ static void bm_channel_with_queue_storage(benchmark::State& state) } } -BENCHMARK(bm_channel_with_queue_storage); +BENCHMARK(channel_with_queue_storage); -static void bm_channel_with_vector_storage(benchmark::State& state) +static void channel_with_vector_storage(benchmark::State& state) { msd::channel> channel{10}; std::string input(1000000, 'x'); - std::string out = ""; + std::string out{}; out.resize(input.size()); for (auto _ : state) { @@ -55,9 +55,9 @@ static void bm_channel_with_vector_storage(benchmark::State& state) } } -BENCHMARK(bm_channel_with_vector_storage); +BENCHMARK(channel_with_vector_storage); -static void bm_channel_with_array_storage(benchmark::State& state) +static void channel_with_array_storage(benchmark::State& state) { msd::channel> channel{}; @@ -71,6 +71,6 @@ static void bm_channel_with_array_storage(benchmark::State& state) } } -BENCHMARK(bm_channel_with_array_storage); +BENCHMARK(channel_with_array_storage); BENCHMARK_MAIN(); diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 6303d07..48a2897 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -1,14 +1,12 @@ function(add_example NAME) add_executable(${NAME} ${ARGN}) - target_compile_options(${NAME} PRIVATE -std=c++${CMAKE_CXX_STANDARD}) - set_target_warnings(${NAME} PRIVATE) - if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - target_link_libraries(${NAME} -ltsan) + if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") target_compile_options(${NAME} PRIVATE -fsanitize=thread) - endif() + target_link_libraries(${NAME} -ltsan) + endif() add_dependencies(examples ${NAME}) endfunction() @@ -18,8 +16,7 @@ function(run_example NAME) TARGET ${NAME} POST_BUILD COMMAND ${NAME} - COMMENT "Running example: ${NAME}" - ) + COMMENT "Running example: ${NAME}") endfunction() add_custom_target(examples) diff --git a/examples/cmake-project/CMakeLists.txt b/examples/cmake-project/CMakeLists.txt index 74b65b5..c130e48 100644 --- a/examples/cmake-project/CMakeLists.txt +++ b/examples/cmake-project/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.16) +cmake_minimum_required(VERSION 3.15) project(cmake_project) set(PROJECT_VERSION 0.1.0) @@ -10,11 +10,10 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wpedantic -Werror --cover add_executable(cmake_project src/main.cpp) include(FetchContent) -if (NOT channel_POPULATED) - FetchContent_Declare(channel URL https://github.com/andreiavrammsd/cpp-channel/archive/v1.3.0.zip DOWNLOAD_EXTRACT_TIMESTAMP TRUE) +if(NOT channel_POPULATED) + FetchContent_Declare(channel URL https://github.com/andreiavrammsd/cpp-channel/archive/v1.3.0.zip + DOWNLOAD_EXTRACT_TIMESTAMP TRUE) FetchContent_Populate(channel) include_directories(${channel_SOURCE_DIR}/include) - # OR - # add_subdirectory(${channel_SOURCE_DIR}/) - # target_link_libraries(cmake_project msd_channel) -endif () + # OR add_subdirectory(${channel_SOURCE_DIR}/) target_link_libraries(cmake_project msd_channel) +endif() diff --git a/examples/move.cpp b/examples/move.cpp index 4aa2f99..d4d5738 100644 --- a/examples/move.cpp +++ b/examples/move.cpp @@ -3,31 +3,31 @@ #include class Data final { - int i_{}; + int value_{}; public: Data() = default; - explicit Data(int i) : i_{i} {} + explicit Data(int value) : value_{value} {} - int getI() const { return i_; } + int get_value() const { return value_; } - Data(const Data& other) noexcept : i_{other.i_} { std::cout << "copy " << i_ << '\n'; } + Data(const Data& other) noexcept : value_{other.value_} { std::cout << "copy " << value_ << '\n'; } Data& operator=(const Data& other) { if (this != &other) { - i_ = other.i_; + value_ = other.value_; } - std::cout << "copy " << i_ << '\n'; + std::cout << "copy " << value_ << '\n'; return *this; } - Data(Data&& other) noexcept : i_{other.i_} { std::cout << "move " << i_ << '\n'; } + Data(Data&& other) noexcept : value_{other.value_} { std::cout << "move " << value_ << '\n'; } Data& operator=(Data&& other) noexcept { if (this != &other) { - i_ = other.i_; - std::cout << "move " << i_ << '\n'; + value_ = other.value_; + std::cout << "move " << value_ << '\n'; } return *this; @@ -49,7 +49,7 @@ int main() chan << std::move(in3); for (const auto& out : chan) { - std::cout << out.getI() << '\n'; + std::cout << out.get_value() << '\n'; if (chan.empty()) { break; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 44281f7..3d449b3 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,53 +1,49 @@ -if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.24.0") +if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.24.0") cmake_policy(SET CMP0135 NEW) endif() # Testing framework -if (MSVC) - option( - gtest_force_shared_crt - "Use shared (DLL) run-time lib even when Google Test is built as static lib." - ON) +if(MSVC) + option(gtest_force_shared_crt "Use shared (DLL) run-time lib even when Google Test is built as static lib." ON) endif() include(FetchContent) -if (NOT googletest_POPULATED) - FetchContent_Declare(googletest URL https://github.com/google/googletest/archive/refs/tags/release-1.12.1.zip) - FetchContent_MakeAvailable(googletest) +if(NOT googletest_POPULATED) set(INSTALL_GTEST OFF CACHE BOOL "" FORCE) set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE) set(BUILD_GTEST_TESTS OFF CACHE BOOL "" FORCE) -endif () + FetchContent_Declare(googletest URL https://github.com/google/googletest/archive/refs/tags/release-1.12.1.zip + DOWNLOAD_EXTRACT_TIMESTAMP TRUE) + FetchContent_MakeAvailable(googletest) +endif() # Test macro function(package_add_test TESTNAME) add_executable(${TESTNAME} ${ARGN}) - target_compile_options(${TESTNAME} PRIVATE -std=c++${CMAKE_CXX_STANDARD}) - set_target_warnings(${TESTNAME} PRIVATE) target_link_libraries(${TESTNAME} gtest gtest_main) - if (CPP_CHANNEL_COVERAGE) - target_link_libraries(${TESTNAME} -lgcov) + if(CPP_CHANNEL_COVERAGE) target_compile_options(${TESTNAME} PRIVATE --coverage) - endif () - - if (CPP_CHANNEL_SANITIZERS) - target_link_libraries(${TESTNAME} -lubsan) - target_compile_options(${TESTNAME} PRIVATE -fsanitize=undefined) + target_link_libraries(${TESTNAME} -lgcov) if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") set(COVERAGE_FLAGS -fprofile-instr-generate -fcoverage-mapping) - target_link_libraries(${TESTNAME} ${COVERAGE_FLAGS}) target_compile_options(${TESTNAME} PRIVATE ${COVERAGE_FLAGS}) + target_link_libraries(${TESTNAME} ${COVERAGE_FLAGS}) endif() - endif () + endif() + + if(CPP_CHANNEL_SANITIZERS) + target_compile_options(${TESTNAME} PRIVATE -fsanitize=undefined) + target_link_libraries(${TESTNAME} -lubsan) + endif() - if (CPP_CHANNEL_SANITIZE_THREADS) - target_link_libraries(${TESTNAME} -fsanitize=thread) + if(CPP_CHANNEL_SANITIZE_THREADS) target_compile_options(${TESTNAME} PRIVATE -fsanitize=thread) - endif () + target_link_libraries(${TESTNAME} -fsanitize=thread) + endif() add_test(NAME ${TESTNAME} COMMAND ${TESTNAME}) set_tests_properties(${TESTNAME} PROPERTIES LABELS "channel_tests") @@ -62,20 +58,7 @@ package_add_test(channel_test channel_test.cpp) package_add_test(blocking_iterator_test blocking_iterator_test.cpp) package_add_test(storage_test storage_test.cpp) -if (CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") +if(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") # Disable warnings about C++17 extensions target_compile_options(channel_test PRIVATE -Wno-c++17-extensions) endif() - -# Benchmark -if (NOT benchmark_POPULATED) - FetchContent_Declare(benchmark URL https://github.com/google/benchmark/archive/refs/tags/v1.9.4.zip) - FetchContent_MakeAvailable(benchmark) - set(BENCHMARK_ENABLE_TESTING OFF CACHE BOOL "" FORCE) - set(BENCHMARK_ENABLE_GTEST_TESTS OFF CACHE BOOL "" FORCE) -endif () - -add_executable(channel_benchmark channel_benchmark.cpp) -target_link_libraries(channel_benchmark benchmark) -set_target_warnings(channel_benchmark PRIVATE) -target_compile_options(channel_benchmark PRIVATE -std=c++${CMAKE_CXX_STANDARD})