|
1 | 1 | #!/usr/bin/env bash |
2 | 2 |
|
3 | | -function do_cppcheck() |
| 3 | +set -euo pipefail |
| 4 | + |
| 5 | +ROOT_DIR=$(git rev-parse --show-toplevel) |
| 6 | +LOG_DIR=${STATUS_CHECK_LOG_DIR:-"${ROOT_DIR}/.status-check-logs"} |
| 7 | +TOOL_CACHE_DIR=${STATUS_CHECK_TOOL_CACHE_DIR:-"${ROOT_DIR}/.status-check-tools"} |
| 8 | +mkdir -p "${LOG_DIR}" "${TOOL_CACHE_DIR}" |
| 9 | + |
| 10 | +mapfile -t SOURCES < <(git -C "${ROOT_DIR}" ls-files '*.c' '*.cc' '*.cpp' '*.h') |
| 11 | + |
| 12 | +count_matches() |
4 | 13 | { |
5 | | - local SOURCES=$(find $(git rev-parse --show-toplevel) | grep -E "\.(cpp|cc|c|h)\$") |
| 14 | + local pattern=$1 |
| 15 | + local file=$2 |
6 | 16 |
|
7 | | - local CPPCHECK=$(which cppcheck) |
8 | | - if [ $? -ne 0 ]; then |
9 | | - echo "[!] cppcheck not installed. Failed to run static analysis the source code." >&2 |
10 | | - exit 1 |
| 17 | + grep -E -c "${pattern}" "${file}" 2>/dev/null || true |
| 18 | +} |
| 19 | + |
| 20 | +needs_rebuild() |
| 21 | +{ |
| 22 | + local repo_url=$1 |
| 23 | + local repo_dir=$2 |
| 24 | + local binary=$3 |
| 25 | + local stamp_file=$4 |
| 26 | + |
| 27 | + if [ ! -x "${binary}" ] || ! "${binary}" --version >/dev/null 2>&1; then |
| 28 | + return 0 |
| 29 | + fi |
| 30 | + |
| 31 | + if [ ! -d "${repo_dir}/.git" ]; then |
| 32 | + return 0 |
11 | 33 | fi |
12 | 34 |
|
13 | | - ## Suppression list ## |
14 | | - # This list will explain the detail of suppressed warnings. |
15 | | - # The prototype of the item should be like: |
16 | | - # "- [{file}] {spec}: {reason}" |
17 | | - # |
18 | | - # - [hello-1.c] unusedFunction: False positive of init_module and cleanup_module. |
19 | | - # - [*.c] missingIncludeSystem: Focus on the example code, not the kernel headers. |
| 35 | + local remote_head local_head |
| 36 | + remote_head=$(git ls-remote "${repo_url}" HEAD 2>/dev/null | cut -f1) |
| 37 | + local_head=$(git -C "${repo_dir}" rev-parse HEAD 2>/dev/null) |
20 | 38 |
|
21 | | - local OPTS=" |
22 | | - --enable=warning,performance,information |
23 | | - --suppress=unusedFunction:hello-1.c |
24 | | - --suppress=missingIncludeSystem |
25 | | - --std=c89 " |
| 39 | + if [ -z "${remote_head}" ] || [ -z "${local_head}" ]; then |
| 40 | + return 0 |
| 41 | + fi |
26 | 42 |
|
27 | | - $CPPCHECK $OPTS --xml ${SOURCES} 2> cppcheck.xml |
28 | | - local ERROR_COUNT=$(cat cppcheck.xml | grep -E -c "</error>" ) |
| 43 | + if [ ! -f "${stamp_file}" ] || [ "$(cat "${stamp_file}")" != "${local_head}" ]; then |
| 44 | + return 0 |
| 45 | + fi |
29 | 46 |
|
30 | | - if [ $ERROR_COUNT -gt 0 ]; then |
31 | | - echo "Cppcheck failed: $ERROR_COUNT error(s)" |
32 | | - cat cppcheck.xml |
33 | | - exit 1 |
| 47 | + [ "${remote_head}" != "${local_head}" ] |
| 48 | +} |
| 49 | + |
| 50 | +sync_repo() |
| 51 | +{ |
| 52 | + local repo_url=$1 |
| 53 | + local repo_dir=$2 |
| 54 | + |
| 55 | + if [ -d "${repo_dir}/.git" ]; then |
| 56 | + git -C "${repo_dir}" fetch --depth=1 origin |
| 57 | + git -C "${repo_dir}" reset --hard FETCH_HEAD |
| 58 | + return |
34 | 59 | fi |
| 60 | + |
| 61 | + rm -rf "${repo_dir}" |
| 62 | + git clone --depth=1 "${repo_url}" "${repo_dir}" |
35 | 63 | } |
36 | 64 |
|
37 | | -function do_sparse() |
| 65 | +do_cppcheck() |
38 | 66 | { |
39 | | - git clone git://git.kernel.org/pub/scm/devel/sparse/sparse.git --depth=1 |
40 | | - if [ $? -ne 0 ]; then |
41 | | - echo "Failed to download sparse." |
| 67 | + local cppcheck_log="${LOG_DIR}/cppcheck.xml" |
| 68 | + local cppcheck_bin |
| 69 | + |
| 70 | + cppcheck_bin=$(command -v cppcheck || true) |
| 71 | + if [ -z "${cppcheck_bin}" ]; then |
| 72 | + echo "[!] cppcheck not installed. Failed to run static analysis the source code." >&2 |
42 | 73 | exit 1 |
43 | 74 | fi |
44 | | - pushd sparse |
45 | | - make sparse || exit 1 |
46 | | - sudo make INST_PROGRAMS=sparse PREFIX=/usr install || exit 1 |
47 | | - popd |
48 | | - local SPARSE=$(which sparse) |
49 | 75 |
|
50 | | - make -C examples C=2 CHECK="$SPARSE" 2> sparse.log |
| 76 | + "${cppcheck_bin}" \ |
| 77 | + --enable=warning,performance,information \ |
| 78 | + --suppress=unusedFunction:hello-1.c \ |
| 79 | + --suppress=missingIncludeSystem \ |
| 80 | + --std=c89 \ |
| 81 | + --xml \ |
| 82 | + "${SOURCES[@]}" \ |
| 83 | + 2> "${cppcheck_log}" |
51 | 84 |
|
52 | | - local WARNING_COUNT=$(cat sparse.log | grep -E -c " warning:" ) |
53 | | - local ERROR_COUNT=$(cat sparse.log | grep -E -c " error:" ) |
54 | | - local COUNT=`expr $WARNING_COUNT + $ERROR_COUNT` |
55 | | - if [ $COUNT -gt 0 ]; then |
56 | | - echo "Sparse failed: $WARNING_COUNT warning(s), $ERROR_COUNT error(s)" |
57 | | - cat sparse.log |
| 85 | + local error_count |
| 86 | + error_count=$(count_matches "</error>" "${cppcheck_log}") |
| 87 | + if [ "${error_count}" -gt 0 ]; then |
| 88 | + echo "Cppcheck failed: ${error_count} error(s)" |
| 89 | + cat "${cppcheck_log}" |
58 | 90 | exit 1 |
59 | 91 | fi |
60 | | - make -C examples clean |
61 | 92 | } |
62 | 93 |
|
63 | | -function do_gcc() |
| 94 | +do_sparse() |
64 | 95 | { |
65 | | - local GCC=$(which gcc) |
66 | | - if [ $? -ne 0 ]; then |
| 96 | + local sparse_url="https://git.kernel.org/pub/scm/devel/sparse/sparse.git" |
| 97 | + local sparse_dir="${TOOL_CACHE_DIR}/sparse" |
| 98 | + local sparse_log="${LOG_DIR}/sparse.log" |
| 99 | + local sparse_bin="${sparse_dir}/sparse" |
| 100 | + local sparse_stamp="${sparse_dir}/.build-head" |
| 101 | + local warning_count |
| 102 | + local error_count |
| 103 | + local count |
| 104 | + |
| 105 | + if needs_rebuild "${sparse_url}" "${sparse_dir}" "${sparse_bin}" "${sparse_stamp}"; then |
| 106 | + sync_repo "${sparse_url}" "${sparse_dir}" |
| 107 | + make -C "${sparse_dir}" HAVE_LLVM=no sparse |
| 108 | + git -C "${sparse_dir}" rev-parse HEAD > "${sparse_stamp}" |
| 109 | + fi |
| 110 | + |
| 111 | + make -C examples clean >/dev/null 2>&1 || true |
| 112 | + if ! make -C examples C=2 CHECK="${sparse_bin}" 2> "${sparse_log}"; then |
| 113 | + cat "${sparse_log}" |
| 114 | + exit 1 |
| 115 | + fi |
| 116 | + |
| 117 | + warning_count=$(count_matches " warning:" "${sparse_log}") |
| 118 | + error_count=$(count_matches " error:" "${sparse_log}") |
| 119 | + count=$((warning_count + error_count)) |
| 120 | + if [ "${count}" -gt 0 ]; then |
| 121 | + echo "Sparse failed: ${warning_count} warning(s), ${error_count} error(s)" |
| 122 | + cat "${sparse_log}" |
| 123 | + exit 1 |
| 124 | + fi |
| 125 | + make -C examples clean >/dev/null 2>&1 || true |
| 126 | +} |
| 127 | + |
| 128 | +do_gcc() |
| 129 | +{ |
| 130 | + local gcc_log="${LOG_DIR}/gcc.log" |
| 131 | + local gcc_bin |
| 132 | + local warning_count |
| 133 | + local error_count |
| 134 | + local count |
| 135 | + |
| 136 | + gcc_bin=$(command -v gcc || true) |
| 137 | + if [ -z "${gcc_bin}" ]; then |
67 | 138 | echo "[!] gcc is not installed. Failed to run static analysis with GCC." >&2 |
68 | 139 | exit 1 |
69 | 140 | fi |
70 | 141 |
|
71 | | - make -C examples CONFIG_STATUS_CHECK_GCC=y STATUS_CHECK_GCC=$GCC 2> gcc.log |
| 142 | + make -C examples clean >/dev/null 2>&1 || true |
| 143 | + if ! make -C examples CONFIG_STATUS_CHECK_GCC=y STATUS_CHECK_GCC="${gcc_bin}" \ |
| 144 | + 2> "${gcc_log}"; then |
| 145 | + cat "${gcc_log}" |
| 146 | + exit 1 |
| 147 | + fi |
72 | 148 |
|
73 | | - local WARNING_COUNT=$(cat gcc.log | grep -E -c " warning:" ) |
74 | | - local ERROR_COUNT=$(cat gcc.log | grep -E -c " error:" ) |
75 | | - local COUNT=`expr $WARNING_COUNT + $ERROR_COUNT` |
76 | | - if [ $COUNT -gt 0 ]; then |
77 | | - echo "gcc failed: $WARNING_COUNT warning(s), $ERROR_COUNT error(s)" |
78 | | - cat gcc.log |
| 149 | + warning_count=$(count_matches " warning:" "${gcc_log}") |
| 150 | + error_count=$(count_matches " error:" "${gcc_log}") |
| 151 | + count=$((warning_count + error_count)) |
| 152 | + if [ "${count}" -gt 0 ]; then |
| 153 | + echo "gcc failed: ${warning_count} warning(s), ${error_count} error(s)" |
| 154 | + cat "${gcc_log}" |
79 | 155 | exit 1 |
80 | 156 | fi |
81 | | - make -C examples CONFIG_STATUS_CHECK_GCC=y STATUS_CHECK_GCC=$GCC clean |
| 157 | + make -C examples CONFIG_STATUS_CHECK_GCC=y STATUS_CHECK_GCC="${gcc_bin}" clean \ |
| 158 | + >/dev/null 2>&1 || true |
82 | 159 | } |
83 | 160 |
|
84 | | -function do_smatch() |
| 161 | +do_smatch() |
85 | 162 | { |
86 | | - git clone https://github.com/error27/smatch.git --depth=1 |
87 | | - if [ $? -ne 0 ]; then |
88 | | - echo "Failed to download smatch." |
| 163 | + local smatch_url="https://github.com/error27/smatch.git" |
| 164 | + local smatch_dir="${TOOL_CACHE_DIR}/smatch" |
| 165 | + local smatch_log="${LOG_DIR}/smatch.log" |
| 166 | + local smatch_bin="${smatch_dir}/smatch" |
| 167 | + local smatch_stamp="${smatch_dir}/.build-head" |
| 168 | + local warning_count |
| 169 | + local error_count |
| 170 | + local count |
| 171 | + |
| 172 | + if needs_rebuild "${smatch_url}" "${smatch_dir}" "${smatch_bin}" "${smatch_stamp}"; then |
| 173 | + sync_repo "${smatch_url}" "${smatch_dir}" |
| 174 | + make -C "${smatch_dir}" smatch |
| 175 | + git -C "${smatch_dir}" rev-parse HEAD > "${smatch_stamp}" |
| 176 | + fi |
| 177 | + |
| 178 | + make -C examples clean >/dev/null 2>&1 || true |
| 179 | + if ! make -C examples C=2 CHECK="${smatch_bin} -p=kernel" > "${smatch_log}" \ |
| 180 | + 2>&1; then |
| 181 | + cat "${smatch_log}" |
89 | 182 | exit 1 |
90 | 183 | fi |
91 | | - pushd smatch |
92 | | - make smatch || exit 1 |
93 | | - local SMATCH=$(pwd)/smatch |
94 | | - popd |
95 | | - |
96 | | - make -C examples C=2 CHECK="$SMATCH -p=kernel" > smatch.log |
97 | | - local WARNING_COUNT=$(cat smatch.log | egrep -c " warn:" ) |
98 | | - local ERROR_COUNT=$(cat smatch.log | egrep -c " error:" ) |
99 | | - local COUNT=`expr $WARNING_COUNT + $ERROR_COUNT` |
100 | | - if [ $COUNT -gt 0 ]; then |
101 | | - echo "Smatch failed: $WARNING_COUNT warning(s), $ERROR_COUNT error(s)" |
102 | | - cat smatch.log | grep "warn:\|error:" |
| 184 | + |
| 185 | + warning_count=$(count_matches " warn:" "${smatch_log}") |
| 186 | + error_count=$(count_matches " error:" "${smatch_log}") |
| 187 | + count=$((warning_count + error_count)) |
| 188 | + if [ "${count}" -gt 0 ]; then |
| 189 | + echo "Smatch failed: ${warning_count} warning(s), ${error_count} error(s)" |
| 190 | + grep -E "warn:|error:" "${smatch_log}" || true |
103 | 191 | exit 1 |
104 | 192 | fi |
105 | | - make -C examples clean |
| 193 | + make -C examples clean >/dev/null 2>&1 || true |
106 | 194 | } |
107 | 195 |
|
108 | 196 | do_cppcheck |
109 | 197 | do_sparse |
110 | 198 | do_gcc |
111 | 199 | do_smatch |
112 | | -exit 0 |
|
0 commit comments