diff --git a/.github/workflows/sonar.yml b/.github/workflows/sonar.yml index 530e1e1ce..e2f8bfccf 100644 --- a/.github/workflows/sonar.yml +++ b/.github/workflows/sonar.yml @@ -17,14 +17,14 @@ jobs: runs-on: ubuntu-latest container: ghcr.io/opencyphal/toolshed:ts24.4.3 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis - name: get nunavut run: | pip install --break-system-packages git+https://github.com/OpenCyphal/nunavut.git@3.0.preview - - name: Install sonar-scanner and build-wrapper - uses: SonarSource/sonarcloud-github-c-cpp@v2 + - name: Install build-wrapper + uses: SonarSource/sonarqube-scan-action/install-build-wrapper@v6 - name: Run tests env: GTEST_COLOR: yes @@ -43,7 +43,7 @@ jobs: cmake --build . --target test/unittest/coverage.xml cmake --build . --target docs/examples/coverage.xml - name: upload-artifacts - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: Coverage-14-gcc path: | @@ -53,27 +53,28 @@ jobs: build/*/**/gcovr_html/*.* if-no-files-found: error - name: Run sonar-scanner + uses: SonarSource/sonarqube-scan-action@v6 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} - run: > - sonar-scanner - -X - --define sonar.organization=opencyphal-garage - --define sonar.projectKey=OpenCyphal-Garage_libcyphal - --define sonar.projectName=libcyphal - --define sonar.projectVersion=1.0 - --define sonar.sources=include,test/unittest/sonar.cpp - --define sonar.tests=test/unittest,docs/examples - --define sonar.test.inclusions=test_*.cpp,example_*.cpp - --define sonar.sourceEncoding=UTF-8 - --define sonar.host.url=https://sonarcloud.io - --define sonar.cfamily.ignoreHeaderComments=false - --define sonar.coverage.exclusions="test/unittest/**/*,docs/examples/**/*,**/sonar.cpp" - --define sonar.cpd.exclusions="test/unittest/**/*,docs/examples/**/*,**/sonar.cpp" - --define sonar.cfamily.compile-commands="build/compile_commands.json" - --define sonar.cfamily.reportingCppStandardOverride=c++14 - --define sonar.coverageReportPaths="build/test/unittest/coverage.xml,build/docs/examples/coverage.xml" - --define sonar.issue.ignore.multicriteria=r1 - --define sonar.issue.ignore.multicriteria.r1.ruleKey=cpp:S3230 - --define sonar.issue.ignore.multicriteria.r1.resourceKey=** + with: + args: > + -X + --define sonar.organization=opencyphal-garage + --define sonar.projectKey=OpenCyphal-Garage_libcyphal + --define sonar.projectName=libcyphal + --define sonar.projectVersion=1.0 + --define sonar.sources=include,test/unittest/sonar.cpp + --define sonar.tests=test/unittest,docs/examples + --define sonar.test.inclusions=test_*.cpp,example_*.cpp + --define sonar.sourceEncoding=UTF-8 + --define sonar.host.url=https://sonarcloud.io + --define sonar.cfamily.ignoreHeaderComments=false + --define sonar.coverage.exclusions="test/unittest/**/*,docs/examples/**/*,**/sonar.cpp" + --define sonar.cpd.exclusions="test/unittest/**/*,docs/examples/**/*,**/sonar.cpp" + --define sonar.cfamily.compile-commands=build/compile_commands.json + --define sonar.cfamily.reportingCppStandardOverride=c++14 + --define sonar.cfamily.cobertura.reportPaths=build/test/unittest/coverage.xml,build/docs/examples/coverage.xml + --define sonar.issue.ignore.multicriteria=r1 + --define sonar.issue.ignore.multicriteria.r1.ruleKey=cpp:S3230 + --define sonar.issue.ignore.multicriteria.r1.resourceKey=** diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index f3cf995a5..8ed850d5b 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -18,7 +18,7 @@ jobs: runs-on: ubuntu-latest container: ghcr.io/opencyphal/toolshed:ts24.4.3 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Cache ext modules id: libcyphal-ext uses: actions/cache@v4 @@ -59,7 +59,7 @@ jobs: std: 14 toolchain: gcc steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Cache ext modules id: libcyphal-ext uses: actions/cache@v4 @@ -86,7 +86,7 @@ jobs: if: ${{ runner.debug == '1' }} run: ls -lAhR build/ - name: upload-artifacts - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: ${{ matrix.build_flavor }}-${{ matrix.std }}-${{ matrix.toolchain }} path: | @@ -104,7 +104,7 @@ jobs: container: ghcr.io/opencyphal/toolshed:ts24.4.3 needs: [warmup] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Cache ext modules id: libcyphal-ext uses: actions/cache@v4 @@ -129,12 +129,12 @@ jobs: uses: actions/configure-pages@v5 - name: Upload docs if: ${{ github.event_name != 'pull_request' }} - uses: actions/upload-pages-artifact@v3 + uses: actions/upload-pages-artifact@v4 with: path: "build/docs/html/" - name: upload-pr-docs if: ${{ github.event_name == 'pull_request' }} - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v5 with: name: pr-docs path: "build/docs/html/" diff --git a/CMakeLists.txt b/CMakeLists.txt index 7f81e2e9d..4f33aade5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,11 +10,12 @@ include(cmake/CMakeCommon.cmake REQUIRED) project(libcyphal VERSION ${LIBCYPHAL_VERSION} LANGUAGES CXX C - HOMEPAGE_URL https://github.com/OpenCyphal-Garage/libcyphal + HOMEPAGE_URL https://github.com/OpenCyphal/libcyphal ) # Use -DNO_STATIC_ANALYSIS=1 to suppress static analysis. # If not suppressed, the tools used here shall be available, otherwise the build will fail. +option(NO_STATIC_ANALYSIS "Disable static analysis (e.g. clang-tidy). If OFF, the tools must be available." OFF) if (NOT NO_STATIC_ANALYSIS) # clang-tidy (separate config files per directory) find_program(clang_tidy NAMES clang-tidy) diff --git a/README.md b/README.md index 06c946569..2fafc7f9d 100644 --- a/README.md +++ b/README.md @@ -12,3 +12,18 @@ Portable reference implementation of the [Cyphal protocol stack](https://opencyphal.org) in C++ for embedded systems, Linux, and POSIX-compliant RTOSs. Cyphal is a lightweight protocol designed for reliable communication in aerospace and robotic applications over robust vehicular networks. + +## Building + +**You don't need to build LibCyphal to use it** since this is a header-only library. You will need to build the transport libraries though (libcanard, libudpard, etc), which is covered in their respective documentation. + +If you want to build libcyphal for development purposes, you may use containerized toolchains as covered in CONTRIBUTING.md. Otherwise, you may want to disable static analysis: + +```shell +mkdir build +cd build +cmake .. -DNO_STATIC_ANALYSIS=1 +make -j16 +``` + +If you're facing obscure DSDL compilation issues, ensure you have Nunavut installed and unset `CYPHAL_PATH`. diff --git a/cmake/compiler_flag_sets/default.cmake b/cmake/compiler_flag_sets/default.cmake index f0774e9dd..fc30ddbe6 100644 --- a/cmake/compiler_flag_sets/default.cmake +++ b/cmake/compiler_flag_sets/default.cmake @@ -32,6 +32,7 @@ list(APPEND C_FLAG_SET "-Wswitch-enum" "-Wtype-limits" "-Wno-error=array-bounds" + "-Wno-error=attributes" ) set(CXX_FLAG_SET ${C_FLAG_SET}) diff --git a/cmake/modules/Findcavl.cmake b/cmake/modules/Findcavl.cmake new file mode 100644 index 000000000..3c0fe4b76 --- /dev/null +++ b/cmake/modules/Findcavl.cmake @@ -0,0 +1,44 @@ +# +# Copyright (C) OpenCyphal Development Team +# Copyright Amazon.com Inc. or its affiliates. +# SPDX-License-Identifier: MIT +# + +include(FetchContent) +set(cavl_GIT_REPOSITORY "https://github.com/pavel-kirienko/cavl") +set(cavl_GIT_TAG "c-2.1.0") + +FetchContent_Declare( + cavl + GIT_REPOSITORY ${cavl_GIT_REPOSITORY} + GIT_TAG ${cavl_GIT_TAG} +) + +# +--------------------------------------------------------------------------------------------------------------------+ +# Because we use FetchContent_Populate to specify a source directory other than the default we have +# to manually manage the _POPULATED, _SOURCE_DIR, and _BINARY_DIR +# variables normally set by this method. +# See https://cmake.org/cmake/help/latest/module/FetchContent.html?highlight=fetchcontent#command:fetchcontent_populate +# for more information. +# This is not ideal, to copy-and-paste this code, but it is the only way to redirect fetch content to an in-source +# directory. An upstream patch to cmake is needed to fix this. +get_property(cavl_POPULATED GLOBAL PROPERTY cavl_POPULATED) +if(NOT cavl_POPULATED) + cmake_path(APPEND CETLVAST_EXTERNAL_ROOT "cavl" OUTPUT_VARIABLE LOCAL_cavl_SOURCE_DIR) + if (NOT ${FETCHCONTENT_FULLY_DISCONNECTED}) + FetchContent_Populate( + cavl + SOURCE_DIR ${LOCAL_cavl_SOURCE_DIR} + GIT_REPOSITORY ${cavl_GIT_REPOSITORY} + GIT_TAG ${cavl_GIT_TAG} + ) + else() + set(cavl_SOURCE_DIR ${LOCAL_cavl_SOURCE_DIR}) + endif() + set_property(GLOBAL PROPERTY cavl_POPULATED true) +endif() + +add_project_library( + NAME cavl2_c + HEADER_PATH "${cavl_SOURCE_DIR}/c" +) diff --git a/cmake/modules/Findcyphal.cmake b/cmake/modules/Findcyphal.cmake index 9ea6cbc37..083547f39 100644 --- a/cmake/modules/Findcyphal.cmake +++ b/cmake/modules/Findcyphal.cmake @@ -3,6 +3,7 @@ if (NOT TARGET cyphal) include(ProjectLibrary) +find_package(cavl REQUIRED) find_package(libcanard REQUIRED) find_package(libudpard REQUIRED) find_package(cetl REQUIRED) @@ -23,23 +24,19 @@ add_project_library( ) find_package(clangformat) - -if (clangformat_FOUND) - -# define a dry-run version that we always run. -enable_clang_format_check_for_directory( - DIRECTORY ${LIBCYPHAL_INCLUDE} - GLOB_PATTERN "**/*.hpp" - ADD_TO_ALL -) - -# provide an in-place format version as a helper that must be manually run. -enable_clang_format_check_for_directory( - DIRECTORY ${LIBCYPHAL_INCLUDE} - GLOB_PATTERN "**/*.hpp" - FORMAT_IN_PLACE -) - +if (clangformat_FOUND AND NOT NO_STATIC_ANALYSIS) + # define a dry-run version that we always run. + enable_clang_format_check_for_directory( + DIRECTORY ${LIBCYPHAL_INCLUDE} + GLOB_PATTERN "**/*.hpp" + ADD_TO_ALL + ) + # provide an in-place format version as a helper that must be manually run. + enable_clang_format_check_for_directory( + DIRECTORY ${LIBCYPHAL_INCLUDE} + GLOB_PATTERN "**/*.hpp" + FORMAT_IN_PLACE + ) endif() # +---------------------------------------------------------------------------+ diff --git a/cmake/modules/Findlibcanard.cmake b/cmake/modules/Findlibcanard.cmake index b280578fc..fcbe0242e 100644 --- a/cmake/modules/Findlibcanard.cmake +++ b/cmake/modules/Findlibcanard.cmake @@ -9,7 +9,7 @@ include(FetchContent) set(libcanard_GIT_REPOSITORY "https://github.com/OpenCyphal/libcanard.git") -set(libcanard_GIT_TAG "v4") +set(libcanard_GIT_TAG "master") # TODO: update to a release tag FetchContent_Declare( libcanard @@ -73,5 +73,6 @@ add_project_library( STATIC FPIC ) +target_link_libraries(canard PRIVATE cavl2_c) -endif() \ No newline at end of file +endif() diff --git a/cmake/modules/Findlibudpard.cmake b/cmake/modules/Findlibudpard.cmake index d3cb73194..e6777d861 100644 --- a/cmake/modules/Findlibudpard.cmake +++ b/cmake/modules/Findlibudpard.cmake @@ -9,7 +9,7 @@ include(FindPackageHandleStandardArgs) include(ProjectLibrary) set(libudpard_GIT_REPOSITORY "https://github.com/OpenCyphal-garage/libudpard.git") -set(libudpard_GIT_TAG "v2") +set(libudpard_GIT_TAG "main") # TODO: update to a release tag FetchContent_Declare( libudpard diff --git a/include/libcyphal/transport/can/can_transport_impl.hpp b/include/libcyphal/transport/can/can_transport_impl.hpp index 9a6e2c30b..58ff3c8b3 100644 --- a/include/libcyphal/transport/can/can_transport_impl.hpp +++ b/include/libcyphal/transport/can/can_transport_impl.hpp @@ -337,7 +337,8 @@ class TransportImpl final : private TransportDelegate, public ICanTransport static_cast(deadline_us.count()), &metadata, {payload.size(), payload.data()}, // NOSONAR cpp:S5356 - static_cast(now_us.count())); + static_cast(now_us.count()), + &tx_frames_expired_); cetl::optional failure = tryHandleTransientCanardResult(media, result); @@ -691,7 +692,9 @@ class TransportImpl final : private TransportDelegate, public ICanTransport auto* const frame_handler_ptr = static_cast(user_reference); // NOSONAR cpp:S5356, cpp:S5357 return (*frame_handler_ptr)(deadline, *frame); - }); + }, + &tx_frames_expired_, + &tx_frames_failed_); } if ((result == 0) && (media.canard_tx_queue().size == 0)) @@ -781,6 +784,8 @@ class TransportImpl final : private TransportDelegate, public ICanTransport TransientErrorHandler transient_error_handler_; Callback::Any configure_filters_callback_; SessionTree svc_response_rx_session_nodes_; + std::uint64_t tx_frames_expired_ = 0; + std::uint64_t tx_frames_failed_ = 0; }; // TransportImpl