diff --git a/cmake/modules/CbmTargets.cmake b/cmake/modules/CbmTargets.cmake index ab37a0728e2674256e49db1d51aa91e91c9b4bca..3aac007772a5a7555ec680368e2a939fd5904d10 100644 --- a/cmake/modules/CbmTargets.cmake +++ b/cmake/modules/CbmTargets.cmake @@ -26,7 +26,7 @@ macro(define_additional_targets) # Create a list C, C++ and header files which have been changed in the # current commit - execute_process(COMMAND ${CMAKE_SOURCE_DIR}/cmake/scripts/find_files.sh + execute_process(COMMAND ${CMAKE_SOURCE_DIR}/cmake/scripts/find_files.sh ${BASE_COMMIT} WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} OUTPUT_VARIABLE FileList OUTPUT_STRIP_TRAILING_WHITESPACE @@ -58,6 +58,50 @@ macro(define_additional_targets) endif() + find_package2(PRIVATE ClangTidy) + if(ClangTidy_FOUND AND EXISTS ${CMAKE_SOURCE_DIR}/.git) + if (FAIRROOT_TIDY_BASE) + set(BASE_COMMIT ${FAIRROOT_TIDY_BASE}) + else() + set(BASE_COMMIT upstream/master) + endif() + + # Create a list C, C++ and header files which have been changed in the + # current commit + execute_process(COMMAND ${CMAKE_SOURCE_DIR}/cmake/scripts/find_files.sh ${BASE_COMMIT} + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + OUTPUT_VARIABLE FileList + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + string(REGEX REPLACE " " ";" FileList "${FileList}") + + # Loop over the files and create the code whch is executed when running + # "make FormatCheck". + # The produced code will run clang-format on one of the files. If + # clang-format finds code which are not satisfying our code rules a + # detailed error message is created. This error message can be checked on + # our CDash web page. + foreach(file ${FileList}) + + set(file1 ${CMAKE_BINARY_DIR}/${file}.ct_out) + + list(APPEND myfilelist1 ${file1}) + add_custom_command(OUTPUT ${file1} + COMMAND ${CMAKE_SOURCE_DIR}/cmake/scripts/check-tidy.sh ${file} ${file1} ${CMAKE_BINARY_DIR} + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + ) + endforeach() + + # Create the target FormatCheck which only depends on the files creted in + # previous step. When running "make FormatCheck" clang-format is executed + # for all C, C++ and header files which have changed in the commit. + add_custom_target(TidyCheck + DEPENDS ${myfilelist1} + ) + + endif() + + # TODO: check if still needed # if(RULE_CHECKER_FOUND) # ADD_CUSTOM_TARGET(RULES diff --git a/cmake/modules/FindClangTidy.cmake b/cmake/modules/FindClangTidy.cmake new file mode 100644 index 0000000000000000000000000000000000000000..885261d86467fb0a63f0b77ca2cfdf58d237d719 --- /dev/null +++ b/cmake/modules/FindClangTidy.cmake @@ -0,0 +1,85 @@ +# Defines the following variables: +# +# ClangTidy_FOUND - Found clang-tidy +# CLANG_TIDY_BIN - clang-tidy executable + +find_program(CLANG_TIDY_BIN + NAMES clang-tidy + clang-tidy-16 + clang-tidy-15 + clang-tidy-14 + clang-tidy-13 + clang-tidy-12 + clang-tidy-11 +) + +Message("CLANG_TIDY_BIN: ${CLANG_TIDY_BIN}") + +#list(APPEND required_tidy_checks +# modernize-deprecated-headers +# modernize-use-nullptr +#) + +# Extract the list of checks from the clang-tidy configuration +# The line looks like: "Checks: '-*,modernize-deprecated-headers'" +# Remove everything beside the part between the quotes and convert the +# string to a list +# The checks must not contain any wildcards +# IDEA: Check if one can extract the same information from clang-tidy --dump-version + +file(STRINGS ${CMAKE_SOURCE_DIR}/.clang-tidy required_tidy_checks_from_file REGEX "Checks.*") + +string(LENGTH ${required_tidy_checks_from_file} _length) +string(FIND ${required_tidy_checks_from_file} "'" _pos_first_quote) +math(EXPR _pos_first_quote ${_pos_first_quote}+1) +string(SUBSTRING ${required_tidy_checks_from_file} ${_pos_first_quote} ${_length} required_tidy_checks_from_file) + +string(FIND ${required_tidy_checks_from_file} "'" _pos_last_quote) +string(SUBSTRING ${required_tidy_checks_from_file} 0 ${_pos_last_quote} required_tidy_checks_from_file) + +string(REPLACE "," ";" required_tidy_checks ${required_tidy_checks_from_file}) + +message(VERBOSE "required_tidy_checks: ${required_tidy_checks}") +if (CLANG_TIDY_BIN) + # Loop over list of required checks + # Succeed if all required checks are supported by the clang version + + execute_process(COMMAND ${CLANG_TIDY_BIN} -checks=-*,modernize-* --list-checks + OUTPUT_VARIABLE available_tidy_checks + ) + message(VERBOSE "Available clang-tidy checks: ${available_tidy_checks}") + + set(CLANG_TIDY_CHECKS_SUPPORTED TRUE) + foreach(check IN LISTS required_tidy_checks) + # Skip all checks which contain wildcards + string(REGEX MATCH "\\*" _wildcard ${check}) + if(_wildcard) + message("The checker doesn't support wildcards in the check") + string(REGEX MATCH "^-\\*" _wildcard ${check}) + if(_wildcard) + message("The check ${check} is droped.") + continue() + else() + message("The check ${check} violates this. clang-tidy support is disabled.") + set(CLANG_TIDY_CHECKS_SUPPORTED FALSE) + break() + endif() + endif() + string(FIND "${available_tidy_checks}" "${check}" check_avail) + if(${check_avail} EQUAL -1) + message("clang-tidy doesn't support the needed Check ${check}. clang-tidy support is disabled.") + set(CLANG_TIDY_CHECKS_SUPPORTED FALSE) + break() + endif() + message("Used check: ${check}") + endforeach() +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(ClangTidy + REQUIRED_VARS CLANG_TIDY_BIN CLANG_TIDY_CHECKS_SUPPORTED +) + +if(ClangTidy_FOUND) + message("The found clang tidy supports all requested checks.") +endif() diff --git a/cmake/scripts/check-tidy.sh b/cmake/scripts/check-tidy.sh new file mode 100755 index 0000000000000000000000000000000000000000..30d3109ad156a0f45c65575755632636691a6a37 --- /dev/null +++ b/cmake/scripts/check-tidy.sh @@ -0,0 +1,61 @@ +#!/bin/bash +# Copyright (C) 2023 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt +# SPDX-License-Identifier: GPL-3.0-only +# First commited by Florian Uhlig + +infile=$1 +outfile=$2 +builddir=$3 + +CLANG_TIDY_BIN=${CLANG_TIDY_BIN:-clang-tidy} + +# special case when a file was deleted +# don't run the test in such a case +if [ ! -e $infile ]; then + exit 0 +fi + +extension=${infile##*.} + +# Only check source or header files +if [ "$extension" == "h" -o "$extension" == "cxx" ]; then + echo "Checking file $infile" +else + exit 0 +fi + +# Don't do anythink for LinkDef files +if [[ $infile =~ "LinkDef" ]]; then + exit 0 +fi + +# Check if the file is a target in the compilation database or a header file +# for which the corresponding source file is in the compilation database +if [[ "$extension" == "h" ]]; then + checkfile=${infile%.*}.cxx +else + checkfile=$infile +fi + +file=$(grep '"file"' $builddir/compile_commands.json | grep "$checkfile" | cut -d: -f2 | cut -d\" -f2) + +if [[ "$file" != "" ]]; then + # create directory if it not yet exist + dir="$(dirname $outfile)" + mkdir -p $dir + OUTPUT=$($CLANG_TIDY_BIN -p $builddir $file 2> $outfile) +else + OUTPUT="" +fi + +if [ "$OUTPUT" == "" ]; then + exit 0 +else + echo "ERROR: clang-tidy check failed for file $infile. Suggested changes:" > $outfile + echo >> $outfile + echo "$OUTPUT" >> $outfile + echo "ERROR: clang-tidy check failed for file $infile. Suggested changes:" + echo + echo "$OUTPUT" + exit 1 +fi diff --git a/cmake/scripts/checktidy.cmake b/cmake/scripts/checktidy.cmake new file mode 100644 index 0000000000000000000000000000000000000000..d9e932863aedfe2cb0959ad8ae41623507ab7828 --- /dev/null +++ b/cmake/scripts/checktidy.cmake @@ -0,0 +1,34 @@ +cmake_host_system_information(RESULT fqdn QUERY FQDN) + +Set(CTEST_SOURCE_DIRECTORY $ENV{SOURCEDIR}) +Set(CTEST_BINARY_DIRECTORY $ENV{BUILDDIR}) +Set(CTEST_PROJECT_NAME "CbmRoot") +set(CTEST_CMAKE_GENERATOR "Unix Makefiles") +set(CTEST_USE_LAUNCHERS ON) + +Set(CTEST_CONFIGURE_COMMAND " \"${CMAKE_EXECUTABLE_NAME}\" \"-G${CTEST_CMAKE_GENERATOR}\" \"-DCTEST_USE_LAUNCHERS=${CTEST_USE_LAUNCHERS}\" \"-DBUILD_FOR_TIDY=ON\" \"${CTEST_SOURCE_DIRECTORY}\" ") + +if ("$ENV{CTEST_SITE}" STREQUAL "") + set(CTEST_SITE "${fqdn}") +else() + set(CTEST_SITE $ENV{CTEST_SITE}) +endif() + +if ("$ENV{LABEL}" STREQUAL "") + set(CTEST_BUILD_NAME "tidy-check") +else() + set(CTEST_BUILD_NAME $ENV{LABEL}) +endif() + +ctest_start(Experimental) + +ctest_configure(BUILD "${CTEST_BINARY_DIRECTORY}") + +ctest_build(TARGET KFPARTICLE PARALLEL_LEVEL $ENV{NCPU}) +ctest_build(TARGET ANALYSISTREE PARALLEL_LEVEL $ENV{NCPU}) +ctest_build(TARGET GTEST PARALLEL_LEVEL $ENV{NCPU}) +ctest_build(TARGET NICAFEMTO PARALLEL_LEVEL $ENV{NCPU}) + +ctest_build(TARGET TidyCheck PARALLEL_LEVEL $ENV{NCPU}) + +ctest_submit() diff --git a/cmake/scripts/find_files.sh b/cmake/scripts/find_files.sh index 4ebd9ea054c79bd67c08360b8abe20d51a58ef51..55cc481eba63d27d9f9b326bcc535bf5235a5332 100755 --- a/cmake/scripts/find_files.sh +++ b/cmake/scripts/find_files.sh @@ -3,9 +3,7 @@ # SPDX-License-Identifier: GPL-3.0-only # First commited by Florian Uhlig - -BASE_COMMIT=${FAIRROOT_FORMAT_BASE:-HEAD} -GIT_CLANG_FORMAT_BIN=${FAIRROOT_GIT_CLANG_FORMAT_BIN:-git-clang-format} +BASE_COMMIT=${1:-HEAD} FILES=$(git diff --name-only $BASE_COMMIT | grep -E '.*\.(h|hpp|c|C|cpp|cxx|tpl)$' | grep -viE '.*LinkDef.h$')