diff --git a/.clang-tidy b/.clang-tidy index 2cbeea94..5b53f3ac 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -1,12 +1,60 @@ -Checks: "-*, - bugprone*, - -bugprone-easily-swappable-parameters, - -bugprone-macro-parentheses, - clang-analyzer*, - readability*, - -readability-magic-numbers, - -readability-identifier-length" +Checks: ' +-*, +bugprone-argument-comment, +bugprone-sizeof-*, +bugprone-use-after-move, +clang-diagnostic-*, +-clang-diagnostic-nrvo, +-clang-diagnostic-missing-designated-field-initializers, +cppcoreguidelines-pro-type-member-init, +cppcoreguidelines-special-member-functions, +google-build-using-namespace, +misc-definitions-in-headers, +modernize-use-emplace, +modernize-use-using, +performance-faster-string-find, +performance-for-range-copy, +performance-implicit-conversion-in-loop, +performance-inefficient-algorithm, +performance-inefficient-string-concatenation, +performance-inefficient-vector-operation, +performance-move-const-arg, +performance-move-constructor-init, +performance-no-automatic-move, +performance-no-int-to-ptr, +performance-noexcept-move-constructor, +performance-trivially-destructible, +performance-type-promotion-in-math-fn, +performance-unnecessary-copy-initialization, +performance-unnecessary-value-param, +readability-operators-representation, +readability-redundant-string-init, +readability-braces-around-statements, +' + +WarningsAsErrors: ' +bugprone-use-after-move, +' CheckOptions: - - key: readability-implicit-bool-conversion.AllowIntegerConditions - value: '1' +- key: bugprone-easily-swappable-parameters.MinimumLength + value: 4 +- key: cppcoreguidelines-special-member-functions.AllowSoleDefaultDtor + value: true +- key: cppcoreguidelines-special-member-functions.AllowImplicitlyDeletedCopyOrMove + value: 1 +- key: modernize-use-using.IgnoreExternC + value: true +- key: performance-move-const-arg.CheckTriviallyCopyableMove + value: false +- key: performance-unnecessary-value-param.AllowedTypes + value: '[Pp]ointer$;[Pp]tr$;[Rr]ef(erence)?$' +- key: performance-unnecessary-copy-initialization.AllowedTypes + value: '[Pp]ointer$;[Pp]tr$;[Rr]ef(erence)?$' +- key: readability-operators-representation.BinaryOperators + value: '&&;&=;&;|;~;!;!=;||;|=;^;^=' +- key: readability-redundant-string-init.StringNames + value: '::std::basic_string' +- key: readability-named-parameter.InsertPlainNamesInForwardDecls + value: true +... diff --git a/.github/scripts/build-cpp-runtime-bindings.sh b/.github/scripts/build-cpp-runtime-bindings.sh index 2cb31227..74ab7990 100644 --- a/.github/scripts/build-cpp-runtime-bindings.sh +++ b/.github/scripts/build-cpp-runtime-bindings.sh @@ -41,6 +41,7 @@ CMAKE_ARGS=( "-DCMAKE_INSTALL_LIBDIR=lib" "-DSVS_RUNTIME_ENABLE_LVQ_LEANVEC=${ENABLE_LVQ_LEANVEC:-ON}" "-DSVS_RUNTIME_ENABLE_IVF=ON" + "-DSVS_EXPERIMENTAL_CLANG_TIDY=ON" ) if [ -n "$SVS_URL" ]; then @@ -48,12 +49,7 @@ if [ -n "$SVS_URL" ]; then fi # Build and install runtime bindings library (from bindings/cpp) -if [ -n "$CC" ]; then - echo "Using CC=${CC} and CXX=${CXX} for building cpp runtime bindings" -else - echo "Using default compiler for building cpp runtime bindings" -fi -CC=${CC:-gcc} CXX=${CXX:-g++} cmake .. "${CMAKE_ARGS[@]}" +CC=gcc CXX=g++ cmake .. "${CMAKE_ARGS[@]}" cmake --build . -j cmake --install . diff --git a/.github/workflows/build-cpp-runtime-bindings.yml b/.github/workflows/build-cpp-runtime-bindings.yml index 351a47d2..468789bf 100644 --- a/.github/workflows/build-cpp-runtime-bindings.yml +++ b/.github/workflows/build-cpp-runtime-bindings.yml @@ -39,18 +39,9 @@ jobs: - name: "with static library" enable_lvq_leanvec: "ON" suffix: "" - cc: "gcc" - cxx: "g++" - name: "public only" enable_lvq_leanvec: "OFF" suffix: "-public-only" - cc: "gcc" - cxx: "g++" - - name: public only with Clang - enable_lvq_leanvec: "OFF" - suffix: "-public-only-clang" - cc: "clang" - cxx: "clang++" fail-fast: false steps: @@ -67,8 +58,6 @@ jobs: -w /workspace \ -e ENABLE_LVQ_LEANVEC=${{ matrix.enable_lvq_leanvec }} \ -e SUFFIX=${{ matrix.suffix }} \ - -e CC=${{ matrix.cc }} \ - -e CXX=${{ matrix.cxx }} \ svs-manylinux228:latest \ /bin/bash .github/scripts/build-cpp-runtime-bindings.sh diff --git a/bindings/cpp/CMakeLists.txt b/bindings/cpp/CMakeLists.txt index 360fa91d..db6053f9 100644 --- a/bindings/cpp/CMakeLists.txt +++ b/bindings/cpp/CMakeLists.txt @@ -82,23 +82,21 @@ target_compile_options(${TARGET_NAME} PRIVATE -fvisibility=hidden ) -# Add extra warnings for GCC/Clang if supported -include(CheckCXXCompilerFlag) - -check_cxx_compiler_flag( - "-Winline-namespace-reopened-noninline" - SVS_RUNTIME_HAS_WINLINE_NAMESPACE_REOPENED_NONINLINE +# Optional clang-tidy integration. Reuses the top-level repo's +# cmake/clang-tidy.cmake module so the runtime build runs the same +# .clang-tidy config as the rest of the library. +option(SVS_EXPERIMENTAL_CLANG_TIDY + "Run the clang-tidy static analyzer on the cpp runtime bindings." + OFF ) -if(SVS_RUNTIME_HAS_WINLINE_NAMESPACE_REOPENED_NONINLINE) - target_compile_options(${TARGET_NAME} PRIVATE -Winline-namespace-reopened-noninline) -endif() - -check_cxx_compiler_flag( - "-Wunused-exception-parameter" - SVS_RUNTIME_HAS_WUNUSED_EXCEPTION_PARAMETER -) -if(SVS_RUNTIME_HAS_WUNUSED_EXCEPTION_PARAMETER) - target_compile_options(${TARGET_NAME} PRIVATE -Wunused-exception-parameter) +if(SVS_EXPERIMENTAL_CLANG_TIDY) + list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/../../cmake") + include(clang-tidy) + if(CLANG_TIDY_COMMAND) + set_target_properties(${TARGET_NAME} + PROPERTIES CXX_CLANG_TIDY "${CLANG_TIDY_COMMAND}" + ) + endif() endif() if(UNIX AND NOT APPLE) @@ -161,6 +159,19 @@ if (SVS_RUNTIME_ENABLE_LVQ_LEANVEC) FetchContent_MakeAvailable(svs) list(APPEND CMAKE_PREFIX_PATH "${svs_SOURCE_DIR}") find_package(svs REQUIRED) + + # Strip svs::svs_compile_options gcc-only flags for clang-tidy + if(SVS_EXPERIMENTAL_CLANG_TIDY AND TARGET svs::svs_compile_options) + get_target_property(_svs_co_opts svs::svs_compile_options INTERFACE_COMPILE_OPTIONS) + if(_svs_co_opts) + list(FILTER _svs_co_opts EXCLUDE REGEX "^-fconcepts-diagnostics-depth") + list(FILTER _svs_co_opts EXCLUDE REGEX "^-ftemplate-backtrace-limit") + set_property(TARGET svs::svs_compile_options + PROPERTY INTERFACE_COMPILE_OPTIONS "${_svs_co_opts}" + ) + endif() + endif() + target_link_libraries(${TARGET_NAME} PRIVATE svs::svs svs::svs_compile_options diff --git a/cmake/clang-tidy.cmake b/cmake/clang-tidy.cmake index 167f5b7c..6df315e7 100644 --- a/cmake/clang-tidy.cmake +++ b/cmake/clang-tidy.cmake @@ -17,18 +17,46 @@ ##### if(SVS_EXPERIMENTAL_CLANG_TIDY) - find_program(CLANG_TIDY_EXE NAMES clang-tidy-14 clang-tidy-13 clang-tidy-12 clang-tidy) + find_program(CLANG_TIDY_EXE + NAMES + clang-tidy-17 clang-tidy-18 clang-tidy + ) if(NOT CLANG_TIDY_EXE) message(WARNING "SVS_EXPERIMENTAL_CLANG_TIDY is ON but clang-tidy is not found!") set(CLANG_TIDY_COMMAND "" CACHE STRING "" FORCE) else() + # Walk up to find .clang-tidy when built standalone from bindings/cpp. + if(EXISTS "${CMAKE_SOURCE_DIR}/.clang-tidy") + set(SVS_CLANG_TIDY_CONFIG "${CMAKE_SOURCE_DIR}/.clang-tidy") + set(SVS_CLANG_TIDY_HEADER_FILTER "${CMAKE_SOURCE_DIR}/include/svs/.*") + elseif(EXISTS "${CMAKE_SOURCE_DIR}/../../.clang-tidy") + set(SVS_CLANG_TIDY_CONFIG "${CMAKE_SOURCE_DIR}/../../.clang-tidy") + set(SVS_CLANG_TIDY_HEADER_FILTER "${CMAKE_SOURCE_DIR}/include/svs/.*") + else() + message(FATAL_ERROR "SVS_EXPERIMENTAL_CLANG_TIDY is ON but no .clang-tidy was found") + endif() + set(CLANG_TIDY_COMMAND "${CLANG_TIDY_EXE}" "--format-style=file" - "--config-file=${CMAKE_SOURCE_DIR}/.clang-tidy" - "--header-filter=${CMAKE_SOURCE_DIR}/include/svs/*" + "--config-file=${SVS_CLANG_TIDY_CONFIG}" + "--header-filter=${SVS_CLANG_TIDY_HEADER_FILTER}" + "--warnings-as-errors=clang-diagnostic-*,bugprone-use-after-move" + # Suppress noise from args (e.g. --gcc-toolchain) that affect link-time + # paths but are unused during clang-tidy's parse-only invocation. + "--extra-arg=-Wno-unused-command-line-argument" ) - message("Clang tidy command: ${CLANG_TIDY_COMMAND}") + + # Point clang-tidy at gcc's toolchain so it can find libstdc++ headers. + if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + get_filename_component(_svs_gcc_bin_dir "${CMAKE_CXX_COMPILER}" DIRECTORY) + get_filename_component(_svs_gcc_toolchain "${_svs_gcc_bin_dir}" DIRECTORY) + list(APPEND CLANG_TIDY_COMMAND + "--extra-arg=--gcc-toolchain=${_svs_gcc_toolchain}" + ) + endif() + + message(STATUS "Clang tidy command: ${CLANG_TIDY_COMMAND}") endif() endif() diff --git a/cmake/options.cmake b/cmake/options.cmake index d83b179a..b02b6785 100644 --- a/cmake/options.cmake +++ b/cmake/options.cmake @@ -201,14 +201,19 @@ if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "In endif() endif() -# Provide better diagnostics for broken templates. if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - target_compile_options( - svs_compile_options - INTERFACE - -fconcepts-diagnostics-depth=10 - -ftemplate-backtrace-limit=0 - ) + # Provide better diagnostics for broken templates. + # These flags are gcc-only; clang-tidy uses the clang frontend to parse the + # compile command and would error with "unknown argument", so skip them when + # clang-tidy is enabled. + if (NOT SVS_EXPERIMENTAL_CLANG_TIDY) + target_compile_options( + svs_compile_options + INTERFACE + -fconcepts-diagnostics-depth=10 + -ftemplate-backtrace-limit=0 + ) + endif() if (CMAKE_CXX_COMPILER_VERSION GREATER_EQUAL 12.0) # GCC-12 throws errors in its own intrinsics library with uninitialized variables. diff --git a/docker/x86_64/manylinux228/Dockerfile b/docker/x86_64/manylinux228/Dockerfile index 708f3aec..491ca196 100644 --- a/docker/x86_64/manylinux228/Dockerfile +++ b/docker/x86_64/manylinux228/Dockerfile @@ -20,8 +20,17 @@ RUN yum install -y cmake-3.26.5 \ && (if [ -x /usr/local/bin/cmake ]; then rm /usr/local/bin/cmake; fi) \ && ln -sf /usr/bin/cmake3 /usr/local/bin/cmake -# Install gcc-11 and clang -RUN yum install -y gcc-toolset-11-11.1-1.el8 wget-1.19.5 clang-20.1.8 \ +# Install gcc-11 +RUN yum install -y gcc-toolset-11-11.1-1.el8 wget-1.19.5 \ + && yum clean all + +# Install clang-tidy via the LLVM 17 module; newer versions crash on our templates. +# The base image ships LLVM 21.x by default, so remove it first to ensure the +# module's older binary wins on PATH. +RUN yum remove -y clang-tools-extra clang || true \ + && dnf module reset -y llvm-toolset \ + && dnf module install -y llvm-toolset:rhel8 \ + && yum install -y clang-tools-extra \ && yum clean all # Configure environment setup properly without recursion