1#! /bin/sh 2# SPDX-License-Identifier: BSD-3-Clause 3# Copyright 2015 6WIND S.A. 4 5# Load config options: 6# - DPDK_CHECKPATCH_PATH 7# - DPDK_CHECKPATCH_CODESPELL 8# - DPDK_CHECKPATCH_LINE_LENGTH 9# - DPDK_CHECKPATCH_OPTIONS 10. $(dirname $(readlink -f $0))/load-devel-config 11 12VALIDATE_NEW_API=$(dirname $(readlink -f $0))/check-symbol-change.sh 13 14# Enable codespell by default. This can be overwritten from a config file. 15# Codespell can also be enabled by setting DPDK_CHECKPATCH_CODESPELL to a valid path 16# to a dictionary.txt file if dictionary.txt is not in the default location. 17codespell=${DPDK_CHECKPATCH_CODESPELL:-enable} 18length=${DPDK_CHECKPATCH_LINE_LENGTH:-100} 19 20# override default Linux options 21options="--no-tree" 22if [ "$codespell" = "enable" ] ; then 23 options="$options --codespell" 24elif [ -f "$codespell" ] ; then 25 options="$options --codespell" 26 options="$options --codespellfile $codespell" 27fi 28options="$options --max-line-length=$length" 29options="$options --show-types" 30options="$options --ignore=LINUX_VERSION_CODE,ENOSYS,\ 31FILE_PATH_CHANGES,MAINTAINERS_STYLE,SPDX_LICENSE_TAG,\ 32VOLATILE,PREFER_PACKED,PREFER_ALIGNED,PREFER_PRINTF,STRLCPY,\ 33PREFER_KERNEL_TYPES,PREFER_FALLTHROUGH,BIT_MACRO,CONST_STRUCT,\ 34SPLIT_STRING,LONG_LINE_STRING,C99_COMMENT_TOLERANCE,\ 35LINE_SPACING,PARENTHESIS_ALIGNMENT,NETWORKING_BLOCK_COMMENT_STYLE,\ 36NEW_TYPEDEFS,COMPARISON_TO_NULL" 37options="$options $DPDK_CHECKPATCH_OPTIONS" 38 39print_usage () { 40 cat <<- END_OF_HELP 41 usage: $(basename $0) [-h] [-q] [-v] [-nX|-r range|patch1 [patch2] ...] 42 43 Run Linux kernel checkpatch.pl with DPDK options. 44 The environment variable DPDK_CHECKPATCH_PATH can be set, if not we will 45 try to find the script in the sources of the currently running kernel. 46 47 The patches to check can be from stdin, files specified on the command line, 48 latest git commits limited with -n option, or commits in the git range 49 specified with -r option (default: "origin/main.."). 50 END_OF_HELP 51} 52 53check_forbidden_additions() { # <patch> 54 res=0 55 56 # refrain from new additions of rte_panic() and rte_exit() 57 # multiple folders and expressions are separated by spaces 58 awk -v FOLDERS="lib drivers" \ 59 -v EXPRESSIONS="rte_panic\\\( rte_exit\\\(" \ 60 -v RET_ON_FAIL=1 \ 61 -v MESSAGE='Using rte_panic/rte_exit' \ 62 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 63 "$1" || res=1 64 65 # refrain from using compiler attribute without defining a common macro 66 awk -v FOLDERS="lib drivers app examples" \ 67 -v EXPRESSIONS="__attribute__" \ 68 -v RET_ON_FAIL=1 \ 69 -v MESSAGE='Using compiler attribute directly' \ 70 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 71 "$1" || res=1 72 73 # check %l or %ll format specifier 74 awk -v FOLDERS='lib drivers app examples' \ 75 -v EXPRESSIONS='%ll*[xud]' \ 76 -v RET_ON_FAIL=1 \ 77 -v MESSAGE='Using %l format, prefer %PRI*64 if type is [u]int64_t' \ 78 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 79 "$1" || res=1 80 81 # forbid variable declaration inside "for" loop 82 awk -v FOLDERS='.' \ 83 -v EXPRESSIONS='for[[:space:]]*\\((char|u?int|unsigned|s?size_t)' \ 84 -v RET_ON_FAIL=1 \ 85 -v MESSAGE='Declaring a variable inside for()' \ 86 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 87 "$1" || res=1 88 89 # refrain from new additions of 16/32/64 bits rte_atomicNN_xxx() 90 awk -v FOLDERS="lib drivers app examples" \ 91 -v EXPRESSIONS="rte_atomic[0-9][0-9]_.*\\\(" \ 92 -v RET_ON_FAIL=1 \ 93 -v MESSAGE='Using rte_atomicNN_xxx' \ 94 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 95 "$1" || res=1 96 97 # refrain from new additions of rte_smp_[r/w]mb() 98 awk -v FOLDERS="lib drivers app examples" \ 99 -v EXPRESSIONS="rte_smp_(r|w)?mb\\\(" \ 100 -v RET_ON_FAIL=1 \ 101 -v MESSAGE='Using rte_smp_[r/w]mb' \ 102 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 103 "$1" || res=1 104 105 # refrain from using compiler __sync_xxx builtins 106 awk -v FOLDERS="lib drivers app examples" \ 107 -v EXPRESSIONS="__sync_.*\\\(" \ 108 -v RET_ON_FAIL=1 \ 109 -v MESSAGE='Using __sync_xxx builtins' \ 110 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 111 "$1" || res=1 112 113 # refrain from using compiler __atomic_thread_fence() 114 # It should be avoided on x86 for SMP case. 115 awk -v FOLDERS="lib drivers app examples" \ 116 -v EXPRESSIONS="__atomic_thread_fence\\\(" \ 117 -v RET_ON_FAIL=1 \ 118 -v MESSAGE='Using __atomic_thread_fence' \ 119 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 120 "$1" || res=1 121 122 # forbid use of __reserved which is a reserved keyword in Windows system headers 123 awk -v FOLDERS="lib drivers app examples" \ 124 -v EXPRESSIONS='\\<__reserved\\>' \ 125 -v RET_ON_FAIL=1 \ 126 -v MESSAGE='Using __reserved' \ 127 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 128 "$1" || res=1 129 130 # forbid use of experimental build flag except in examples 131 awk -v FOLDERS='lib drivers app' \ 132 -v EXPRESSIONS='-DALLOW_EXPERIMENTAL_API allow_experimental_apis' \ 133 -v RET_ON_FAIL=1 \ 134 -v MESSAGE='Using experimental build flag for in-tree compilation' \ 135 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 136 "$1" || res=1 137 138 # refrain from using RTE_LOG_REGISTER for drivers and libs 139 awk -v FOLDERS='lib drivers' \ 140 -v EXPRESSIONS='\\<RTE_LOG_REGISTER\\>' \ 141 -v RET_ON_FAIL=1 \ 142 -v MESSAGE='Using RTE_LOG_REGISTER, prefer RTE_LOG_REGISTER_(DEFAULT|SUFFIX)' \ 143 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 144 "$1" || res=1 145 146 # forbid inclusion of driver specific headers in apps and examples 147 awk -v FOLDERS='app examples' \ 148 -v EXPRESSIONS='include.*_driver\\.h include.*_pmd\\.h' \ 149 -v RET_ON_FAIL=1 \ 150 -v MESSAGE='Using driver specific headers in applications' \ 151 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 152 "$1" || res=1 153 154 # SVG must be included with wildcard extension to allow conversion 155 awk -v FOLDERS='doc' \ 156 -v EXPRESSIONS='::[[:space:]]*[^[:space:]]*\\.svg' \ 157 -v RET_ON_FAIL=1 \ 158 -v MESSAGE='Using explicit .svg extension instead of .*' \ 159 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 160 "$1" || res=1 161 162 # links must prefer https over http 163 awk -v FOLDERS='doc' \ 164 -v EXPRESSIONS='http://.*dpdk.org' \ 165 -v RET_ON_FAIL=1 \ 166 -v MESSAGE='Using non https link to dpdk.org' \ 167 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 168 "$1" || res=1 169 170 # '// XXX is not set' must be preferred over '#undef XXX' 171 awk -v FOLDERS='config/rte_config.h' \ 172 -v EXPRESSIONS='#undef' \ 173 -v RET_ON_FAIL=1 \ 174 -v MESSAGE='Using "#undef XXX", prefer "// XXX is not set"' \ 175 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 176 "$1" || res=1 177 178 return $res 179} 180 181check_experimental_tags() { # <patch> 182 res=0 183 184 cat "$1" |awk ' 185 BEGIN { 186 current_file = ""; 187 ret = 0; 188 } 189 /^+++ b\// { 190 current_file = $2; 191 } 192 /^+.*__rte_experimental/ { 193 if (current_file ~ ".c$" ) { 194 print "Please only put __rte_experimental tags in " \ 195 "headers ("current_file")"; 196 ret = 1; 197 } 198 if ($1 != "+__rte_experimental" || $2 != "") { 199 print "__rte_experimental must appear alone on the line" \ 200 " immediately preceding the return type of a function." 201 ret = 1; 202 } 203 } 204 END { 205 exit ret; 206 }' || res=1 207 208 return $res 209} 210 211check_internal_tags() { # <patch> 212 res=0 213 214 cat "$1" |awk ' 215 BEGIN { 216 current_file = ""; 217 ret = 0; 218 } 219 /^+++ b\// { 220 current_file = $2; 221 } 222 /^+.*__rte_internal/ { 223 if (current_file ~ ".c$" ) { 224 print "Please only put __rte_internal tags in " \ 225 "headers ("current_file")"; 226 ret = 1; 227 } 228 if ($1 != "+__rte_internal" || $2 != "") { 229 print "__rte_internal must appear alone on the line" \ 230 " immediately preceding the return type of" \ 231 " a function." 232 ret = 1; 233 } 234 } 235 END { 236 exit ret; 237 }' || res=1 238 239 return $res 240} 241 242check_release_notes() { # <patch> 243 rel_notes_prefix=doc/guides/rel_notes/release_ 244 IFS=. read year month release < VERSION 245 current_rel_notes=${rel_notes_prefix}${year}_${month}.rst 246 247 ! grep -e '^--- a/'$rel_notes_prefix -e '^+++ b/'$rel_notes_prefix "$1" | 248 grep -v $current_rel_notes 249} 250 251number=0 252range='origin/main..' 253quiet=false 254verbose=false 255while getopts hn:qr:v ARG ; do 256 case $ARG in 257 n ) number=$OPTARG ;; 258 q ) quiet=true ;; 259 r ) range=$OPTARG ;; 260 v ) verbose=true ;; 261 h ) print_usage ; exit 0 ;; 262 ? ) print_usage ; exit 1 ;; 263 esac 264done 265shift $(($OPTIND - 1)) 266 267if [ ! -f "$DPDK_CHECKPATCH_PATH" ] || [ ! -x "$DPDK_CHECKPATCH_PATH" ] ; then 268 default_path="/lib/modules/$(uname -r)/source/scripts/checkpatch.pl" 269 if [ -f "$default_path" ] && [ -x "$default_path" ] ; then 270 DPDK_CHECKPATCH_PATH="$default_path" 271 else 272 print_usage >&2 273 echo 274 echo 'Cannot execute DPDK_CHECKPATCH_PATH' >&2 275 exit 1 276 fi 277fi 278 279print_headline() { # <title> 280 printf '\n### %s\n\n' "$1" 281 headline_printed=true 282} 283 284total=0 285status=0 286 287check () { # <patch-file> <commit> 288 local ret=0 289 local subject='' 290 headline_printed=false 291 292 total=$(($total + 1)) 293 if [ -n "$1" ] ; then 294 tmpinput=$1 295 else 296 tmpinput=$(mktemp -t dpdk.checkpatches.XXXXXX) 297 trap "rm -f '$tmpinput'" INT 298 299 if [ -n "$2" ] ; then 300 git format-patch --find-renames \ 301 --no-stat --stdout -1 $commit > "$tmpinput" 302 else 303 cat > "$tmpinput" 304 fi 305 fi 306 307 # Subject can be on 2 lines 308 subject=$(sed '/^Subject: */!d;s///;N;s,\n[[:space:]]\+, ,;s,\n.*,,;q' "$tmpinput") 309 ! $verbose || print_headline "$subject" 310 311 ! $verbose || printf 'Running checkpatch.pl:\n' 312 report=$($DPDK_CHECKPATCH_PATH $options "$tmpinput" 2>/dev/null) 313 if [ $? -ne 0 ] ; then 314 $headline_printed || print_headline "$subject" 315 printf '%s\n' "$report" | sed -n '1,/^total:.*lines checked$/p' 316 ret=1 317 fi 318 319 ! $verbose || printf '\nChecking API additions/removals:\n' 320 report=$($VALIDATE_NEW_API "$tmpinput") 321 if [ $? -ne 0 ] ; then 322 $headline_printed || print_headline "$subject" 323 printf '%s\n' "$report" 324 ret=1 325 fi 326 327 ! $verbose || printf '\nChecking forbidden tokens additions:\n' 328 report=$(check_forbidden_additions "$tmpinput") 329 if [ $? -ne 0 ] ; then 330 $headline_printed || print_headline "$subject" 331 printf '%s\n' "$report" 332 ret=1 333 fi 334 335 ! $verbose || printf '\nChecking __rte_experimental tags:\n' 336 report=$(check_experimental_tags "$tmpinput") 337 if [ $? -ne 0 ] ; then 338 $headline_printed || print_headline "$subject" 339 printf '%s\n' "$report" 340 ret=1 341 fi 342 343 ! $verbose || printf '\nChecking __rte_internal tags:\n' 344 report=$(check_internal_tags "$tmpinput") 345 if [ $? -ne 0 ] ; then 346 $headline_printed || print_headline "$subject" 347 printf '%s\n' "$report" 348 ret=1 349 fi 350 351 ! $verbose || printf '\nChecking release notes updates:\n' 352 report=$(check_release_notes "$tmpinput") 353 if [ $? -ne 0 ] ; then 354 $headline_printed || print_headline "$subject" 355 printf '%s\n' "$report" 356 ret=1 357 fi 358 359 if [ "$tmpinput" != "$1" ]; then 360 rm -f "$tmpinput" 361 trap - INT 362 fi 363 [ $ret -eq 0 ] && return 0 364 365 status=$(($status + 1)) 366} 367 368if [ -n "$1" ] ; then 369 for patch in "$@" ; do 370 check "$patch" '' 371 done 372elif [ ! -t 0 ] ; then # stdin 373 check '' '' 374else 375 if [ $number -eq 0 ] ; then 376 commits=$(git rev-list --reverse $range) 377 else 378 commits=$(git rev-list --reverse --max-count=$number HEAD) 379 fi 380 for commit in $commits ; do 381 check '' $commit 382 done 383fi 384pass=$(($total - $status)) 385$quiet || printf '\n%d/%d valid patch' $pass $total 386$quiet || [ $pass -le 1 ] || printf 'es' 387$quiet || printf '\n' 388exit $status 389