xref: /f-stack/dpdk/devtools/checkpatches.sh (revision 2d9fd380)
12bfe3f2eSlogwang#! /bin/sh
2d30ea906Sjfb8856606# SPDX-License-Identifier: BSD-3-Clause
32bfe3f2eSlogwang# Copyright 2015 6WIND S.A.
42bfe3f2eSlogwang
52bfe3f2eSlogwang# Load config options:
62bfe3f2eSlogwang# - DPDK_CHECKPATCH_PATH
74418919fSjohnjiang# - DPDK_CHECKPATCH_CODESPELL
82bfe3f2eSlogwang# - DPDK_CHECKPATCH_LINE_LENGTH
94418919fSjohnjiang# - DPDK_CHECKPATCH_OPTIONS
104418919fSjohnjiang. $(dirname $(readlink -f $0))/load-devel-config
112bfe3f2eSlogwang
124418919fSjohnjiangVALIDATE_NEW_API=$(dirname $(readlink -f $0))/check-symbol-change.sh
13d30ea906Sjfb8856606
144418919fSjohnjiang# Enable codespell by default. This can be overwritten from a config file.
154418919fSjohnjiang# Codespell can also be enabled by setting DPDK_CHECKPATCH_CODESPELL to a valid path
164418919fSjohnjiang# to a dictionary.txt file if dictionary.txt is not in the default location.
174418919fSjohnjiangcodespell=${DPDK_CHECKPATCH_CODESPELL:-enable}
182bfe3f2eSlogwanglength=${DPDK_CHECKPATCH_LINE_LENGTH:-80}
192bfe3f2eSlogwang
202bfe3f2eSlogwang# override default Linux options
212bfe3f2eSlogwangoptions="--no-tree"
224418919fSjohnjiangif [ "$codespell" = "enable" ] ; then
234418919fSjohnjiang    options="$options --codespell"
244418919fSjohnjiangelif [ -f "$codespell" ] ; then
254418919fSjohnjiang    options="$options --codespell"
264418919fSjohnjiang    options="$options --codespellfile $codespell"
274418919fSjohnjiangfi
282bfe3f2eSlogwangoptions="$options --max-line-length=$length"
292bfe3f2eSlogwangoptions="$options --show-types"
30*2d9fd380Sjfb8856606options="$options --ignore=LINUX_VERSION_CODE,ENOSYS,\
31d30ea906Sjfb8856606FILE_PATH_CHANGES,MAINTAINERS_STYLE,SPDX_LICENSE_TAG,\
322bfe3f2eSlogwangVOLATILE,PREFER_PACKED,PREFER_ALIGNED,PREFER_PRINTF,\
33*2d9fd380Sjfb8856606PREFER_KERNEL_TYPES,PREFER_FALLTHROUGH,BIT_MACRO,CONST_STRUCT,\
34*2d9fd380Sjfb8856606SPLIT_STRING,LONG_LINE_STRING,C99_COMMENT_TOLERANCE,\
352bfe3f2eSlogwangLINE_SPACING,PARENTHESIS_ALIGNMENT,NETWORKING_BLOCK_COMMENT_STYLE,\
362bfe3f2eSlogwangNEW_TYPEDEFS,COMPARISON_TO_NULL"
374418919fSjohnjiangoptions="$options $DPDK_CHECKPATCH_OPTIONS"
38d30ea906Sjfb8856606
392bfe3f2eSlogwangprint_usage () {
402bfe3f2eSlogwang	cat <<- END_OF_HELP
41*2d9fd380Sjfb8856606	usage: $(basename $0) [-h] [-q] [-v] [-nX|-r range|patch1 [patch2] ...]
422bfe3f2eSlogwang
432bfe3f2eSlogwang	Run Linux kernel checkpatch.pl with DPDK options.
442bfe3f2eSlogwang	The environment variable DPDK_CHECKPATCH_PATH must be set.
452bfe3f2eSlogwang
462bfe3f2eSlogwang	The patches to check can be from stdin, files specified on the command line,
474418919fSjohnjiang	latest git commits limited with -n option, or commits in the git range
48*2d9fd380Sjfb8856606	specified with -r option (default: "origin/main..").
492bfe3f2eSlogwang	END_OF_HELP
502bfe3f2eSlogwang}
512bfe3f2eSlogwang
52d30ea906Sjfb8856606check_forbidden_additions() { # <patch>
531646932aSjfb8856606	res=0
541646932aSjfb8856606
55d30ea906Sjfb8856606	# refrain from new additions of rte_panic() and rte_exit()
56d30ea906Sjfb8856606	# multiple folders and expressions are separated by spaces
57d30ea906Sjfb8856606	awk -v FOLDERS="lib drivers" \
58d30ea906Sjfb8856606		-v EXPRESSIONS="rte_panic\\\( rte_exit\\\(" \
59d30ea906Sjfb8856606		-v RET_ON_FAIL=1 \
60d30ea906Sjfb8856606		-v MESSAGE='Using rte_panic/rte_exit' \
614418919fSjohnjiang		-f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \
621646932aSjfb8856606		"$1" || res=1
631646932aSjfb8856606
64*2d9fd380Sjfb8856606	# refrain from using compiler attribute without defining a common macro
65*2d9fd380Sjfb8856606	awk -v FOLDERS="lib drivers app examples" \
66*2d9fd380Sjfb8856606		-v EXPRESSIONS="__attribute__" \
67*2d9fd380Sjfb8856606		-v RET_ON_FAIL=1 \
68*2d9fd380Sjfb8856606		-v MESSAGE='Using compiler attribute directly' \
69*2d9fd380Sjfb8856606		-f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \
70*2d9fd380Sjfb8856606		"$1" || res=1
71*2d9fd380Sjfb8856606
72*2d9fd380Sjfb8856606	# forbid variable declaration inside "for" loop
73*2d9fd380Sjfb8856606	awk -v FOLDERS='.' \
74*2d9fd380Sjfb8856606		-v EXPRESSIONS='for[[:space:]]*\\((char|u?int|unsigned|s?size_t)' \
75*2d9fd380Sjfb8856606		-v RET_ON_FAIL=1 \
76*2d9fd380Sjfb8856606		-v MESSAGE='Declaring a variable inside for()' \
77*2d9fd380Sjfb8856606		-f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \
78*2d9fd380Sjfb8856606		"$1" || res=1
79*2d9fd380Sjfb8856606
80*2d9fd380Sjfb8856606	# refrain from new additions of 16/32/64 bits rte_atomicNN_xxx()
81*2d9fd380Sjfb8856606	awk -v FOLDERS="lib drivers app examples" \
82*2d9fd380Sjfb8856606		-v EXPRESSIONS="rte_atomic[0-9][0-9]_.*\\\(" \
83*2d9fd380Sjfb8856606		-v RET_ON_FAIL=1 \
84*2d9fd380Sjfb8856606		-v MESSAGE='Using rte_atomicNN_xxx' \
85*2d9fd380Sjfb8856606		-f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \
86*2d9fd380Sjfb8856606		"$1" || res=1
87*2d9fd380Sjfb8856606
88*2d9fd380Sjfb8856606	# refrain from new additions of rte_smp_[r/w]mb()
89*2d9fd380Sjfb8856606	awk -v FOLDERS="lib drivers app examples" \
90*2d9fd380Sjfb8856606		-v EXPRESSIONS="rte_smp_(r|w)?mb\\\(" \
91*2d9fd380Sjfb8856606		-v RET_ON_FAIL=1 \
92*2d9fd380Sjfb8856606		-v MESSAGE='Using rte_smp_[r/w]mb' \
93*2d9fd380Sjfb8856606		-f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \
94*2d9fd380Sjfb8856606		"$1" || res=1
95*2d9fd380Sjfb8856606
96*2d9fd380Sjfb8856606	# refrain from using compiler __sync_xxx builtins
97*2d9fd380Sjfb8856606	awk -v FOLDERS="lib drivers app examples" \
98*2d9fd380Sjfb8856606		-v EXPRESSIONS="__sync_.*\\\(" \
99*2d9fd380Sjfb8856606		-v RET_ON_FAIL=1 \
100*2d9fd380Sjfb8856606		-v MESSAGE='Using __sync_xxx builtins' \
101*2d9fd380Sjfb8856606		-f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \
102*2d9fd380Sjfb8856606		"$1" || res=1
103*2d9fd380Sjfb8856606
104*2d9fd380Sjfb8856606	# refrain from using compiler __atomic_thread_fence()
105*2d9fd380Sjfb8856606	# It should be avoided on x86 for SMP case.
106*2d9fd380Sjfb8856606	awk -v FOLDERS="lib drivers app examples" \
107*2d9fd380Sjfb8856606		-v EXPRESSIONS="__atomic_thread_fence\\\(" \
108*2d9fd380Sjfb8856606		-v RET_ON_FAIL=1 \
109*2d9fd380Sjfb8856606		-v MESSAGE='Using __atomic_thread_fence' \
110*2d9fd380Sjfb8856606		-f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \
111*2d9fd380Sjfb8856606		"$1" || res=1
112*2d9fd380Sjfb8856606
113*2d9fd380Sjfb8856606	# forbid use of experimental build flag except in examples
114*2d9fd380Sjfb8856606	awk -v FOLDERS='lib drivers app' \
115*2d9fd380Sjfb8856606		-v EXPRESSIONS='-DALLOW_EXPERIMENTAL_API allow_experimental_apis' \
116*2d9fd380Sjfb8856606		-v RET_ON_FAIL=1 \
117*2d9fd380Sjfb8856606		-v MESSAGE='Using experimental build flag for in-tree compilation' \
118*2d9fd380Sjfb8856606		-f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \
119*2d9fd380Sjfb8856606		"$1" || res=1
120*2d9fd380Sjfb8856606
121d30ea906Sjfb8856606	# svg figures must be included with wildcard extension
122d30ea906Sjfb8856606	# because of png conversion for pdf docs
123d30ea906Sjfb8856606	awk -v FOLDERS='doc' \
124d30ea906Sjfb8856606		-v EXPRESSIONS='::[[:space:]]*[^[:space:]]*\\.svg' \
125d30ea906Sjfb8856606		-v RET_ON_FAIL=1 \
126d30ea906Sjfb8856606		-v MESSAGE='Using explicit .svg extension instead of .*' \
1274418919fSjohnjiang		-f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \
1281646932aSjfb8856606		"$1" || res=1
1291646932aSjfb8856606
1300c6bd470Sfengbojiang	# links must prefer https over http
1310c6bd470Sfengbojiang	awk -v FOLDERS='doc' \
1320c6bd470Sfengbojiang		-v EXPRESSIONS='http://.*dpdk.org' \
1330c6bd470Sfengbojiang		-v RET_ON_FAIL=1 \
1340c6bd470Sfengbojiang		-v MESSAGE='Using non https link to dpdk.org' \
1350c6bd470Sfengbojiang		-f $(dirname $(readlink -f $0))/check-forbidden-tokens.awk \
1360c6bd470Sfengbojiang		"$1" || res=1
1370c6bd470Sfengbojiang
1381646932aSjfb8856606	return $res
139d30ea906Sjfb8856606}
140d30ea906Sjfb8856606
1414418919fSjohnjiangcheck_experimental_tags() { # <patch>
1424418919fSjohnjiang	res=0
1434418919fSjohnjiang
1444418919fSjohnjiang	cat "$1" |awk '
1454418919fSjohnjiang	BEGIN {
1464418919fSjohnjiang		current_file = "";
1474418919fSjohnjiang		ret = 0;
1484418919fSjohnjiang	}
1494418919fSjohnjiang	/^+++ b\// {
1504418919fSjohnjiang		current_file = $2;
1514418919fSjohnjiang	}
1524418919fSjohnjiang	/^+.*__rte_experimental/ {
1534418919fSjohnjiang		if (current_file ~ ".c$" ) {
1544418919fSjohnjiang			print "Please only put __rte_experimental tags in " \
1554418919fSjohnjiang				"headers ("current_file")";
1564418919fSjohnjiang			ret = 1;
1574418919fSjohnjiang		}
1584418919fSjohnjiang		if ($1 != "+__rte_experimental" || $2 != "") {
1594418919fSjohnjiang			print "__rte_experimental must appear alone on the line" \
1604418919fSjohnjiang				" immediately preceding the return type of a function."
1614418919fSjohnjiang			ret = 1;
1624418919fSjohnjiang		}
1634418919fSjohnjiang	}
1644418919fSjohnjiang	END {
1654418919fSjohnjiang		exit ret;
1664418919fSjohnjiang	}' || res=1
1674418919fSjohnjiang
1684418919fSjohnjiang	return $res
1694418919fSjohnjiang}
1704418919fSjohnjiang
171*2d9fd380Sjfb8856606check_internal_tags() { # <patch>
172*2d9fd380Sjfb8856606	res=0
173*2d9fd380Sjfb8856606
174*2d9fd380Sjfb8856606	cat "$1" |awk '
175*2d9fd380Sjfb8856606	BEGIN {
176*2d9fd380Sjfb8856606		current_file = "";
177*2d9fd380Sjfb8856606		ret = 0;
178*2d9fd380Sjfb8856606	}
179*2d9fd380Sjfb8856606	/^+++ b\// {
180*2d9fd380Sjfb8856606		current_file = $2;
181*2d9fd380Sjfb8856606	}
182*2d9fd380Sjfb8856606	/^+.*__rte_internal/ {
183*2d9fd380Sjfb8856606		if (current_file ~ ".c$" ) {
184*2d9fd380Sjfb8856606			print "Please only put __rte_internal tags in " \
185*2d9fd380Sjfb8856606				"headers ("current_file")";
186*2d9fd380Sjfb8856606			ret = 1;
187*2d9fd380Sjfb8856606		}
188*2d9fd380Sjfb8856606		if ($1 != "+__rte_internal" || $2 != "") {
189*2d9fd380Sjfb8856606			print "__rte_internal must appear alone on the line" \
190*2d9fd380Sjfb8856606				" immediately preceding the return type of" \
191*2d9fd380Sjfb8856606				" a function."
192*2d9fd380Sjfb8856606			ret = 1;
193*2d9fd380Sjfb8856606		}
194*2d9fd380Sjfb8856606	}
195*2d9fd380Sjfb8856606	END {
196*2d9fd380Sjfb8856606		exit ret;
197*2d9fd380Sjfb8856606	}' || res=1
198*2d9fd380Sjfb8856606
199*2d9fd380Sjfb8856606	return $res
200*2d9fd380Sjfb8856606}
201*2d9fd380Sjfb8856606
2022bfe3f2eSlogwangnumber=0
203*2d9fd380Sjfb8856606range='origin/main..'
2042bfe3f2eSlogwangquiet=false
2052bfe3f2eSlogwangverbose=false
2064418919fSjohnjiangwhile getopts hn:qr:v ARG ; do
2072bfe3f2eSlogwang	case $ARG in
2082bfe3f2eSlogwang		n ) number=$OPTARG ;;
2092bfe3f2eSlogwang		q ) quiet=true ;;
2104418919fSjohnjiang		r ) range=$OPTARG ;;
2112bfe3f2eSlogwang		v ) verbose=true ;;
2122bfe3f2eSlogwang		h ) print_usage ; exit 0 ;;
2132bfe3f2eSlogwang		? ) print_usage ; exit 1 ;;
2142bfe3f2eSlogwang	esac
2152bfe3f2eSlogwangdone
2162bfe3f2eSlogwangshift $(($OPTIND - 1))
2172bfe3f2eSlogwang
218d30ea906Sjfb8856606if [ ! -f "$DPDK_CHECKPATCH_PATH" ] || [ ! -x "$DPDK_CHECKPATCH_PATH" ] ; then
2192bfe3f2eSlogwang	print_usage >&2
2202bfe3f2eSlogwang	echo
2212bfe3f2eSlogwang	echo 'Cannot execute DPDK_CHECKPATCH_PATH' >&2
2222bfe3f2eSlogwang	exit 1
2232bfe3f2eSlogwangfi
2242bfe3f2eSlogwang
225d30ea906Sjfb8856606print_headline() { # <title>
226d30ea906Sjfb8856606	printf '\n### %s\n\n' "$1"
227d30ea906Sjfb8856606	headline_printed=true
228d30ea906Sjfb8856606}
229d30ea906Sjfb8856606
2302bfe3f2eSlogwangtotal=0
2312bfe3f2eSlogwangstatus=0
2322bfe3f2eSlogwang
2332bfe3f2eSlogwangcheck () { # <patch> <commit> <title>
234d30ea906Sjfb8856606	local ret=0
235d30ea906Sjfb8856606	headline_printed=false
236d30ea906Sjfb8856606
2372bfe3f2eSlogwang	total=$(($total + 1))
238d30ea906Sjfb8856606	! $verbose || print_headline "$3"
2392bfe3f2eSlogwang	if [ -n "$1" ] ; then
240d30ea906Sjfb8856606		tmpinput=$1
2414418919fSjohnjiang	else
242d30ea906Sjfb8856606		tmpinput=$(mktemp -t dpdk.checkpatches.XXXXXX)
2434418919fSjohnjiang		trap "rm -f '$tmpinput'" INT
2444418919fSjohnjiang
2454418919fSjohnjiang		if [ -n "$2" ] ; then
246d30ea906Sjfb8856606			git format-patch --find-renames \
247d30ea906Sjfb8856606			--no-stat --stdout -1 $commit > "$tmpinput"
2482bfe3f2eSlogwang		else
249d30ea906Sjfb8856606			cat > "$tmpinput"
2502bfe3f2eSlogwang		fi
2514418919fSjohnjiang	fi
252d30ea906Sjfb8856606
253d30ea906Sjfb8856606	! $verbose || printf 'Running checkpatch.pl:\n'
254d30ea906Sjfb8856606	report=$($DPDK_CHECKPATCH_PATH $options "$tmpinput" 2>/dev/null)
255d30ea906Sjfb8856606	if [ $? -ne 0 ] ; then
256d30ea906Sjfb8856606		$headline_printed || print_headline "$3"
2572bfe3f2eSlogwang		printf '%s\n' "$report" | sed -n '1,/^total:.*lines checked$/p'
258d30ea906Sjfb8856606		ret=1
259d30ea906Sjfb8856606	fi
260d30ea906Sjfb8856606
261d30ea906Sjfb8856606	! $verbose || printf '\nChecking API additions/removals:\n'
262d30ea906Sjfb8856606	report=$($VALIDATE_NEW_API "$tmpinput")
263d30ea906Sjfb8856606	if [ $? -ne 0 ] ; then
264d30ea906Sjfb8856606		$headline_printed || print_headline "$3"
265d30ea906Sjfb8856606		printf '%s\n' "$report"
266d30ea906Sjfb8856606		ret=1
267d30ea906Sjfb8856606	fi
268d30ea906Sjfb8856606
269d30ea906Sjfb8856606	! $verbose || printf '\nChecking forbidden tokens additions:\n'
270d30ea906Sjfb8856606	report=$(check_forbidden_additions "$tmpinput")
271d30ea906Sjfb8856606	if [ $? -ne 0 ] ; then
272d30ea906Sjfb8856606		$headline_printed || print_headline "$3"
273d30ea906Sjfb8856606		printf '%s\n' "$report"
274d30ea906Sjfb8856606		ret=1
275d30ea906Sjfb8856606	fi
276d30ea906Sjfb8856606
2774418919fSjohnjiang	! $verbose || printf '\nChecking __rte_experimental tags:\n'
2784418919fSjohnjiang	report=$(check_experimental_tags "$tmpinput")
2794418919fSjohnjiang	if [ $? -ne 0 ] ; then
2804418919fSjohnjiang		$headline_printed || print_headline "$3"
2814418919fSjohnjiang		printf '%s\n' "$report"
2824418919fSjohnjiang		ret=1
2834418919fSjohnjiang	fi
2844418919fSjohnjiang
285*2d9fd380Sjfb8856606	! $verbose || printf '\nChecking __rte_internal tags:\n'
286*2d9fd380Sjfb8856606	report=$(check_internal_tags "$tmpinput")
287*2d9fd380Sjfb8856606	if [ $? -ne 0 ] ; then
288*2d9fd380Sjfb8856606		$headline_printed || print_headline "$3"
289*2d9fd380Sjfb8856606		printf '%s\n' "$report"
290*2d9fd380Sjfb8856606		ret=1
291*2d9fd380Sjfb8856606	fi
292*2d9fd380Sjfb8856606
2934418919fSjohnjiang	if [ "$tmpinput" != "$1" ]; then
2944418919fSjohnjiang		rm -f "$tmpinput"
2954418919fSjohnjiang		trap - INT
2964418919fSjohnjiang	fi
297d30ea906Sjfb8856606	[ $ret -eq 0 ] && return 0
298d30ea906Sjfb8856606
2992bfe3f2eSlogwang	status=$(($status + 1))
3002bfe3f2eSlogwang}
3012bfe3f2eSlogwang
3022bfe3f2eSlogwangif [ -n "$1" ] ; then
3032bfe3f2eSlogwang	for patch in "$@" ; do
3042bfe3f2eSlogwang		# Subject can be on 2 lines
3052bfe3f2eSlogwang		subject=$(sed '/^Subject: */!d;s///;N;s,\n[[:space:]]\+, ,;s,\n.*,,;q' "$patch")
3062bfe3f2eSlogwang		check "$patch" '' "$subject"
3072bfe3f2eSlogwang	done
3082bfe3f2eSlogwangelif [ ! -t 0 ] ; then # stdin
3092bfe3f2eSlogwang	subject=$(while read header value ; do
3102bfe3f2eSlogwang		if [ "$header" = 'Subject:' ] ; then
3112bfe3f2eSlogwang			IFS= read next
3122bfe3f2eSlogwang			continuation=$(echo "$next" | sed -n 's,^[[:space:]]\+, ,p')
3132bfe3f2eSlogwang			echo $value$continuation
3142bfe3f2eSlogwang			break
3152bfe3f2eSlogwang		fi
3162bfe3f2eSlogwang	done)
3172bfe3f2eSlogwang	check '' '' "$subject"
3182bfe3f2eSlogwangelse
3192bfe3f2eSlogwang	if [ $number -eq 0 ] ; then
3204418919fSjohnjiang		commits=$(git rev-list --reverse $range)
3212bfe3f2eSlogwang	else
3222bfe3f2eSlogwang		commits=$(git rev-list --reverse --max-count=$number HEAD)
3232bfe3f2eSlogwang	fi
3242bfe3f2eSlogwang	for commit in $commits ; do
3252bfe3f2eSlogwang		subject=$(git log --format='%s' -1 $commit)
3262bfe3f2eSlogwang		check '' $commit "$subject"
3272bfe3f2eSlogwang	done
3282bfe3f2eSlogwangfi
3292bfe3f2eSlogwangpass=$(($total - $status))
3302bfe3f2eSlogwang$quiet || printf '\n%d/%d valid patch' $pass $total
3312bfe3f2eSlogwang$quiet || [ $pass -le 1 ] || printf 'es'
3322bfe3f2eSlogwang$quiet || printf '\n'
3332bfe3f2eSlogwangexit $status
334