Soby Mathew | b4c6df4 | 2022-11-09 11:13:29 +0000 | [diff] [blame^] | 1 | # |
| 2 | # SPDX-License-Identifier: BSD-3-Clause |
| 3 | # SPDX-FileCopyrightText: Copyright TF-RMM Contributors. |
| 4 | # |
| 5 | |
| 6 | # |
| 7 | # This script is called from main CMakeLists.txt to determine if header files |
| 8 | # included are in proper order |
| 9 | # |
| 10 | find_package(Git REQUIRED) |
| 11 | find_package(Python3 REQUIRED) |
| 12 | find_program(CHECKINCLUDES_EXECUTABLE "checkincludes.py" |
| 13 | PATHS ${CMAKE_SOURCE_DIR} |
| 14 | PATH_SUFFIXES tools/checkincludes |
| 15 | DOC "Path to checkincludes.py" |
| 16 | ) |
| 17 | |
| 18 | list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/tools/common") |
| 19 | include(GitUtils) |
| 20 | |
| 21 | # List of directories and files to exclude from checking for target |
| 22 | list(APPEND glob_excludes "^.git") |
| 23 | list(APPEND glob_excludes "^out") |
| 24 | list(APPEND glob_excludes "^build") |
| 25 | list(APPEND glob_excludes "^ext") |
| 26 | list(APPEND glob_excludes "^tools") |
| 27 | |
| 28 | # checkincludes_get_stats: Parse and returns number of errors and warnings |
| 29 | function(checkincludes_get_stats stats_arg errors_ret) |
| 30 | string(FIND "${stats_arg}" "total:" idx REVERSE) |
| 31 | if(NOT ${idx} EQUAL -1) |
| 32 | string(LENGTH "${stats_arg}" len) |
| 33 | string(SUBSTRING "${stats_arg}" ${idx} ${len} last_line) |
| 34 | |
| 35 | string(REPLACE " " ";" last_line_list ${last_line}) |
| 36 | list(GET last_line_list 1 errors) |
| 37 | else() |
| 38 | set(errors 1) |
| 39 | endif() |
| 40 | |
| 41 | set(${errors_ret} ${errors} PARENT_SCOPE) |
| 42 | endfunction() |
| 43 | |
| 44 | # |
| 45 | # print_stats_and_exit: Print summary of all errors and warnings. |
| 46 | # If there are errors call message(FATAL_ERROR) |
| 47 | # |
| 48 | function(print_stats_and_exit check_type total_errors) |
| 49 | message(STATUS "${check_type}: total errors: ${total_errors}") |
| 50 | |
| 51 | if(${total_errors} GREATER 0) |
| 52 | message(FATAL_ERROR "${check_type}: FAILED") |
| 53 | endif() |
| 54 | |
| 55 | message(STATUS "${check_type}: PASSED") |
| 56 | endfunction() |
| 57 | |
| 58 | # filter all files except *.c/*.h/*.S files |
| 59 | function(filter_source_files all_files source_files_ret) |
| 60 | foreach(exclude IN LISTS glob_excludes) |
| 61 | list(FILTER all_files EXCLUDE REGEX "${exclude}") |
| 62 | endforeach() |
| 63 | |
| 64 | foreach(source_file ${all_files}) |
| 65 | if(NOT source_file MATCHES ".c$" AND |
| 66 | NOT source_file MATCHES ".S$" AND |
| 67 | NOT source_file MATCHES ".h$") |
| 68 | list(REMOVE_ITEM all_files ${source_file}) |
| 69 | endif() |
| 70 | endforeach() |
| 71 | |
| 72 | set(${source_files_ret} ${all_files} PARENT_SCOPE) |
| 73 | endfunction() |
| 74 | |
| 75 | # Run checkincludes.py on the list of files. |
| 76 | function(run_checkincludes source_files errors_ret) |
| 77 | set(errors 0) |
| 78 | |
| 79 | string(REPLACE ";" " " source_files "${source_files}") |
| 80 | separate_arguments(source_files NATIVE_COMMAND "${source_files}") |
| 81 | |
| 82 | execute_process( |
| 83 | WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} |
| 84 | COMMAND ${CHECKINCLUDES_EXECUTABLE} ${source_files} |
| 85 | OUTPUT_VARIABLE checkincludes_output |
| 86 | RESULT_VARIABLE checkincludes_rc |
| 87 | ECHO_OUTPUT_VARIABLE |
| 88 | OUTPUT_STRIP_TRAILING_WHITESPACE |
| 89 | ) |
| 90 | |
| 91 | # checkincludes failed for this file. Collect no.of errors |
| 92 | if(${checkincludes_rc} GREATER 0) |
| 93 | checkincludes_get_stats("${checkincludes_output}" errors) |
| 94 | endif() |
| 95 | |
| 96 | set(${errors_ret} ${errors} PARENT_SCOPE) |
| 97 | endfunction() |
| 98 | |
| 99 | # |
| 100 | # Run checkincludes on entire codebase. This verifies all files in this |
| 101 | # repository in "GLOB_INCLUDES". |
| 102 | # |
| 103 | # Exits with FATAL_ERROR upon errors. Warnings are ignored (temporary) |
| 104 | # |
| 105 | if(CHECKINCLUDES_CODEBASE) |
| 106 | set(source_files "") |
| 107 | |
| 108 | if (GIT_FOUND AND IS_DIRECTORY .git) |
| 109 | Git_Get_All_Files(all_files) |
| 110 | else() |
| 111 | file(GLOB_RECURSE all_files RELATIVE ${CMAKE_SOURCE_DIR} "*") |
| 112 | endif() |
| 113 | |
| 114 | filter_source_files("${all_files}" source_files) |
| 115 | |
| 116 | if(NOT source_files) |
| 117 | message(STATUS "checkincludes-codebase: No files to check") |
| 118 | return() |
| 119 | endif() |
| 120 | |
| 121 | run_checkincludes("${source_files}" total_errors) |
| 122 | print_stats_and_exit("checkincludes-codebase" ${total_errors}) |
| 123 | endif() |
| 124 | |
| 125 | # |
| 126 | # Check header files include order on pending commits. |
| 127 | # |
| 128 | # Exits with FATAL_ERROR upon errors. |
| 129 | # |
| 130 | if(CHECKINCLUDES_PATCH) |
| 131 | if(GIT_NOT_FOUND OR NOT IS_DIRECTORY .git) |
| 132 | message(FATAL_ERROR "Required dependencies Git not found") |
| 133 | endif() |
| 134 | |
| 135 | # Get list of commits to check |
| 136 | Git_Get_Pending_Commits(pending_commits) |
| 137 | |
| 138 | # Iterate throuth list of commit ids |
| 139 | set(total_errors 0) |
| 140 | foreach(commit IN LISTS pending_commits) |
| 141 | message(STATUS "Checking commit: ${commit}") |
| 142 | |
| 143 | Git_Get_Files_In_Commit("${commit}" files_in_commit) |
| 144 | |
| 145 | set(source_files "") |
| 146 | filter_source_files("${files_in_commit}" source_files) |
| 147 | |
| 148 | run_checkincludes("${source_files}" errors) |
| 149 | MATH(EXPR total_errors "${total_errors}+${errors}") |
| 150 | endforeach() |
| 151 | |
| 152 | print_stats_and_exit("checkincludes-patch" ${total_errors}) |
| 153 | endif() |