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,AVOID_BUG" 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 # refrain from new additions of 16/32/64 bits rte_atomicNN_xxx() 82 awk -v FOLDERS="lib drivers app examples" \ 83 -v EXPRESSIONS="rte_atomic[0-9][0-9]_.*\\\(" \ 84 -v RET_ON_FAIL=1 \ 85 -v MESSAGE='Using rte_atomicNN_xxx' \ 86 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 87 "$1" || res=1 88 89 # refrain from new additions of rte_smp_[r/w]mb() 90 awk -v FOLDERS="lib drivers app examples" \ 91 -v EXPRESSIONS="rte_smp_(r|w)?mb\\\(" \ 92 -v RET_ON_FAIL=1 \ 93 -v MESSAGE='Using rte_smp_[r/w]mb' \ 94 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 95 "$1" || res=1 96 97 # refrain from using compiler __sync_xxx builtins 98 awk -v FOLDERS="lib drivers app examples" \ 99 -v EXPRESSIONS="__sync_.*\\\(" \ 100 -v RET_ON_FAIL=1 \ 101 -v MESSAGE='Using __sync_xxx builtins' \ 102 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 103 "$1" || res=1 104 105 # refrain from using compiler __rte_atomic_thread_fence() 106 # It should be avoided on x86 for SMP case. 107 awk -v FOLDERS="lib drivers app examples" \ 108 -v EXPRESSIONS="__rte_atomic_thread_fence\\\(" \ 109 -v RET_ON_FAIL=1 \ 110 -v MESSAGE='Using __rte_atomic_thread_fence, prefer rte_atomic_thread_fence' \ 111 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 112 "$1" || res=1 113 114 # refrain from using compiler __atomic_xxx builtins 115 awk -v FOLDERS="lib drivers app examples" \ 116 -v EXPRESSIONS="__atomic_.*\\\( __ATOMIC_(RELAXED|CONSUME|ACQUIRE|RELEASE|ACQ_REL|SEQ_CST)" \ 117 -v RET_ON_FAIL=1 \ 118 -v MESSAGE='Using __atomic_xxx/__ATOMIC_XXX built-ins, prefer rte_atomic_xxx/rte_memory_order_xxx' \ 119 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 120 "$1" || res=1 121 122 # refrain from using some pthread functions 123 awk -v FOLDERS="lib drivers app examples" \ 124 -v EXPRESSIONS="pthread_(create|join|detach|set(_?name_np|affinity_np)|attr_set(inheritsched|schedpolicy))\\\(" \ 125 -v RET_ON_FAIL=1 \ 126 -v MESSAGE='Using pthread functions, prefer rte_thread' \ 127 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 128 "$1" || res=1 129 130 # forbid use of __reserved which is a reserved keyword in Windows system headers 131 awk -v FOLDERS="lib drivers app examples" \ 132 -v EXPRESSIONS='\\<__reserved\\>' \ 133 -v RET_ON_FAIL=1 \ 134 -v MESSAGE='Using __reserved' \ 135 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 136 "$1" || res=1 137 138 # forbid use of non abstracted bit count operations 139 awk -v FOLDERS="lib drivers app examples" \ 140 -v EXPRESSIONS='\\<__builtin_(clz|clzll|ctz|ctzll|popcount|popcountll)\\>' \ 141 -v RET_ON_FAIL=1 \ 142 -v MESSAGE='Using __builtin helpers for bit count operations' \ 143 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 144 "$1" || res=1 145 146 # forbid inclusion of Linux header for PCI constants 147 awk -v FOLDERS="lib drivers app examples" \ 148 -v EXPRESSIONS='include.*linux/pci_regs\\.h' \ 149 -v RET_ON_FAIL=1 \ 150 -v MESSAGE='Using linux/pci_regs.h, prefer rte_pci.h' \ 151 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 152 "$1" || res=1 153 154 # forbid use of experimental build flag except in examples 155 awk -v FOLDERS='lib drivers app' \ 156 -v EXPRESSIONS='-DALLOW_EXPERIMENTAL_API allow_experimental_apis' \ 157 -v RET_ON_FAIL=1 \ 158 -v MESSAGE='Using experimental build flag for in-tree compilation' \ 159 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 160 "$1" || res=1 161 162 # refrain from using RTE_LOG_REGISTER for drivers and libs 163 awk -v FOLDERS='lib drivers' \ 164 -v EXPRESSIONS='\\<RTE_LOG_REGISTER\\>' \ 165 -v RET_ON_FAIL=1 \ 166 -v MESSAGE='Using RTE_LOG_REGISTER, prefer RTE_LOG_REGISTER_(DEFAULT|SUFFIX)' \ 167 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 168 "$1" || res=1 169 170 # forbid non-internal thread in drivers and libs 171 awk -v FOLDERS='lib drivers' \ 172 -v EXPRESSIONS="rte_thread_(set_name|create_control)\\\(" \ 173 -v RET_ON_FAIL=1 \ 174 -v MESSAGE='Prefer rte_thread_(set_prefixed_name|create_internal_control)' \ 175 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 176 "$1" || res=1 177 178 # forbid inclusion of driver specific headers in apps and examples 179 awk -v FOLDERS='app examples' \ 180 -v EXPRESSIONS='include.*_driver\\.h include.*_pmd\\.h' \ 181 -v RET_ON_FAIL=1 \ 182 -v MESSAGE='Using driver specific headers in applications' \ 183 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 184 "$1" || res=1 185 186 # prevent addition of tests not in one of our test suites 187 awk -v FOLDERS='app/test' \ 188 -v EXPRESSIONS='REGISTER_TEST_COMMAND' \ 189 -v RET_ON_FAIL=1 \ 190 -v MESSAGE='Using REGISTER_TEST_COMMAND instead of REGISTER_<suite_name>_TEST' \ 191 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 192 "$1" || res=1 193 194 # SVG must be included with wildcard extension to allow conversion 195 awk -v FOLDERS='doc' \ 196 -v EXPRESSIONS='::[[:space:]]*[^[:space:]]*\\.svg' \ 197 -v RET_ON_FAIL=1 \ 198 -v MESSAGE='Using explicit .svg extension instead of .*' \ 199 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 200 "$1" || res=1 201 202 # links must prefer https over http 203 awk -v FOLDERS='doc' \ 204 -v EXPRESSIONS='http://.*dpdk.org' \ 205 -v RET_ON_FAIL=1 \ 206 -v MESSAGE='Using non https link to dpdk.org' \ 207 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 208 "$1" || res=1 209 210 # prefer Sphinx references for internal documentation 211 awk -v FOLDERS='doc' \ 212 -v EXPRESSIONS='//doc.dpdk.org/guides/' \ 213 -v RET_ON_FAIL=1 \ 214 -v MESSAGE='Using explicit URL to doc.dpdk.org, prefer :ref: or :doc:' \ 215 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 216 "$1" || res=1 217 218 # '// XXX is not set' must be preferred over '#undef XXX' 219 awk -v FOLDERS='config/rte_config.h' \ 220 -v EXPRESSIONS='#undef' \ 221 -v RET_ON_FAIL=1 \ 222 -v MESSAGE='Using "#undef XXX", prefer "// XXX is not set"' \ 223 -f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \ 224 "$1" || res=1 225 226 return $res 227} 228 229check_experimental_tags() { # <patch> 230 res=0 231 232 cat "$1" |awk ' 233 BEGIN { 234 current_file = ""; 235 ret = 0; 236 } 237 /^+++ b\// { 238 current_file = $2; 239 } 240 /^+.*__rte_experimental/ { 241 if (current_file ~ ".c$" ) { 242 print "Please only put __rte_experimental tags in " \ 243 "headers ("current_file")"; 244 ret = 1; 245 } 246 if ($1 != "+__rte_experimental" || $2 != "") { 247 print "__rte_experimental must appear alone on the line" \ 248 " immediately preceding the return type of a function." 249 ret = 1; 250 } 251 } 252 END { 253 exit ret; 254 }' || res=1 255 256 return $res 257} 258 259check_internal_tags() { # <patch> 260 res=0 261 262 cat "$1" |awk ' 263 BEGIN { 264 current_file = ""; 265 ret = 0; 266 } 267 /^+++ b\// { 268 current_file = $2; 269 } 270 /^+.*__rte_internal/ { 271 if (current_file ~ ".c$" ) { 272 print "Please only put __rte_internal tags in " \ 273 "headers ("current_file")"; 274 ret = 1; 275 } 276 if ($1 != "+__rte_internal" || $2 != "") { 277 print "__rte_internal must appear alone on the line" \ 278 " immediately preceding the return type of" \ 279 " a function." 280 ret = 1; 281 } 282 } 283 END { 284 exit ret; 285 }' || res=1 286 287 return $res 288} 289 290check_release_notes() { # <patch> 291 rel_notes_prefix=doc/guides/rel_notes/release_ 292 IFS=. read year month release < VERSION 293 current_rel_notes=${rel_notes_prefix}${year}_${month}.rst 294 295 ! grep -e '^--- a/'$rel_notes_prefix -e '^+++ b/'$rel_notes_prefix "$1" | 296 grep -v $current_rel_notes 297} 298 299number=0 300range='origin/main..' 301quiet=false 302verbose=false 303while getopts hn:qr:v ARG ; do 304 case $ARG in 305 n ) number=$OPTARG ;; 306 q ) quiet=true ;; 307 r ) range=$OPTARG ;; 308 v ) verbose=true ;; 309 h ) print_usage ; exit 0 ;; 310 ? ) print_usage ; exit 1 ;; 311 esac 312done 313shift $(($OPTIND - 1)) 314 315if [ ! -f "$DPDK_CHECKPATCH_PATH" ] || [ ! -x "$DPDK_CHECKPATCH_PATH" ] ; then 316 default_path="/lib/modules/$(uname -r)/source/scripts/checkpatch.pl" 317 if [ -f "$default_path" ] && [ -x "$default_path" ] ; then 318 DPDK_CHECKPATCH_PATH="$default_path" 319 else 320 print_usage >&2 321 echo 322 echo 'Cannot execute DPDK_CHECKPATCH_PATH' >&2 323 exit 1 324 fi 325fi 326 327print_headline() { # <title> 328 printf '\n### %s\n\n' "$1" 329 headline_printed=true 330} 331 332total=0 333status=0 334 335check () { # <patch-file> <commit> 336 local ret=0 337 local subject='' 338 headline_printed=false 339 340 total=$(($total + 1)) 341 if [ -n "$1" ] ; then 342 tmpinput=$1 343 else 344 tmpinput=$(mktemp -t dpdk.checkpatches.XXXXXX) 345 trap "rm -f '$tmpinput'" INT 346 347 if [ -n "$2" ] ; then 348 git format-patch --find-renames \ 349 --no-stat --stdout -1 $commit > "$tmpinput" 350 else 351 cat > "$tmpinput" 352 fi 353 fi 354 355 # Subject can be on 2 lines 356 subject=$(sed '/^Subject: */!d;s///;N;s,\n[[:space:]]\+, ,;s,\n.*,,;q' "$tmpinput") 357 ! $verbose || print_headline "$subject" 358 359 ! $verbose || printf 'Running checkpatch.pl:\n' 360 report=$($DPDK_CHECKPATCH_PATH $options "$tmpinput" 2>/dev/null) 361 if [ $? -ne 0 ] ; then 362 $headline_printed || print_headline "$subject" 363 printf '%s\n' "$report" | sed -n '1,/^total:.*lines checked$/p' 364 ret=1 365 fi 366 367 ! $verbose || printf '\nChecking API additions/removals:\n' 368 report=$($VALIDATE_NEW_API "$tmpinput") 369 if [ $? -ne 0 ] ; then 370 $headline_printed || print_headline "$subject" 371 printf '%s\n' "$report" 372 ret=1 373 fi 374 375 ! $verbose || printf '\nChecking forbidden tokens additions:\n' 376 report=$(check_forbidden_additions "$tmpinput") 377 if [ $? -ne 0 ] ; then 378 $headline_printed || print_headline "$subject" 379 printf '%s\n' "$report" 380 ret=1 381 fi 382 383 ! $verbose || printf '\nChecking __rte_experimental tags:\n' 384 report=$(check_experimental_tags "$tmpinput") 385 if [ $? -ne 0 ] ; then 386 $headline_printed || print_headline "$subject" 387 printf '%s\n' "$report" 388 ret=1 389 fi 390 391 ! $verbose || printf '\nChecking __rte_internal tags:\n' 392 report=$(check_internal_tags "$tmpinput") 393 if [ $? -ne 0 ] ; then 394 $headline_printed || print_headline "$subject" 395 printf '%s\n' "$report" 396 ret=1 397 fi 398 399 ! $verbose || printf '\nChecking release notes updates:\n' 400 report=$(check_release_notes "$tmpinput") 401 if [ $? -ne 0 ] ; then 402 $headline_printed || print_headline "$subject" 403 printf '%s\n' "$report" 404 ret=1 405 fi 406 407 if [ "$tmpinput" != "$1" ]; then 408 rm -f "$tmpinput" 409 trap - INT 410 fi 411 [ $ret -eq 0 ] && return 0 412 413 status=$(($status + 1)) 414} 415 416if [ -n "$1" ] ; then 417 for patch in "$@" ; do 418 check "$patch" '' 419 done 420elif [ ! -t 0 ] ; then # stdin 421 check '' '' 422else 423 if [ $number -eq 0 ] ; then 424 commits=$(git rev-list --reverse $range) 425 else 426 commits=$(git rev-list --reverse --max-count=$number HEAD) 427 fi 428 for commit in $commits ; do 429 check '' $commit 430 done 431fi 432pass=$(($total - $status)) 433$quiet || printf '\n%d/%d valid patch' $pass $total 434$quiet || [ $pass -le 1 ] || printf 'es' 435$quiet || printf '\n' 436exit $status 437