xref: /spdk/scripts/check_format.sh (revision b37db06935181fd0e8f5592a96d860040abaa201)
170044923SDaniel Verkamp#!/usr/bin/env bash
2eb53c232Spaul luse#  SPDX-License-Identifier: BSD-3-Clause
3eb53c232Spaul luse#  Copyright (C) 2015 Intel Corporation
4b557c9c8SAlexey Marchuk#  Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES.
5eb53c232Spaul luse#  All rights reserved.
6eb53c232Spaul luse#
746875520SJim Harrisif [[ $(uname -s) == Darwin ]]; then
846875520SJim Harris	# SPDK is not supported on MacOS, but as a developer
946875520SJim Harris	# convenience we support running the check_format.sh
1046875520SJim Harris	# script on MacOS.
118d27a5f7SAlexey Marchuk	# Running "brew install bash coreutils grep" should be
1246875520SJim Harris	# sufficient to get the correct versions of these utilities.
1346875520SJim Harris	if [[ $(type -t mapfile) != builtin ]]; then
1446875520SJim Harris		# We need bash version >= 4.0 for mapfile builtin
1546875520SJim Harris		echo "Please install bash version >= 4.0"
1646875520SJim Harris		exit 1
1746875520SJim Harris	fi
1846875520SJim Harris	if ! hash greadlink 2> /dev/null; then
1946875520SJim Harris		# We need GNU readlink for -f option
2046875520SJim Harris		echo "Please install GNU readlink"
2146875520SJim Harris		exit 1
2246875520SJim Harris	fi
2346875520SJim Harris	if ! hash ggrep 2> /dev/null; then
2446875520SJim Harris		# We need GNU grep for -P option
2546875520SJim Harris		echo "Please install GNU grep"
2646875520SJim Harris		exit 1
2746875520SJim Harris	fi
2846875520SJim Harris	GNU_READLINK="greadlink"
2946875520SJim Harris	GNU_GREP="ggrep"
3046875520SJim Harriselse
3146875520SJim Harris	GNU_READLINK="readlink"
3246875520SJim Harris	GNU_GREP="grep"
3346875520SJim Harrisfi
3446875520SJim Harris
3546875520SJim Harrisrootdir=$($GNU_READLINK -f "$(dirname "$0")")/..
3636f5f0dfSMichal Bergersource "$rootdir/scripts/common.sh"
37ec632be2SMichal Berger
3836f5f0dfSMichal Bergercd "$rootdir"
3970044923SDaniel Verkamp
4070044923SDaniel Verkamp# exit on errors
4170044923SDaniel Verkampset -e
4270044923SDaniel Verkamp
4338103f1fSPawel Wodkowskiif ! hash nproc 2> /dev/null; then
4438103f1fSPawel Wodkowski
4538103f1fSPawel Wodkowski	function nproc() {
4638103f1fSPawel Wodkowski		echo 8
4738103f1fSPawel Wodkowski	}
4838103f1fSPawel Wodkowski
4938103f1fSPawel Wodkowskifi
5038103f1fSPawel Wodkowski
514171d1f5STomasz Kulasekfunction version_lt() {
524171d1f5STomasz Kulasek	[ $(echo -e "$1\n$2" | sort -V | head -1) != "$1" ]
534171d1f5STomasz Kulasek}
544171d1f5STomasz Kulasek
550506fd21SSeth Howellfunction array_contains_string() {
560506fd21SSeth Howell	name="$1[@]"
570506fd21SSeth Howell	array=("${!name}")
580506fd21SSeth Howell
590506fd21SSeth Howell	for element in "${array[@]}"; do
600506fd21SSeth Howell		if [ "$element" = "$2" ]; then
610506fd21SSeth Howell			return $(true)
620506fd21SSeth Howell		fi
630506fd21SSeth Howell	done
640506fd21SSeth Howell
650506fd21SSeth Howell	return $(false)
660506fd21SSeth Howell}
670506fd21SSeth Howell
6838d41acdSDaniel Verkamprc=0
6938d41acdSDaniel Verkamp
70d61da0ccSBen Walkerfunction check_permissions() {
718721d5daSMichal Berger	local rc=0 files=()
72d705ab93SBen Walker
738721d5daSMichal Berger	mapfile -t files < <(git ls-files)
748721d5daSMichal Berger	mapfile -t files < <(get_diffed_dups "${files[@]}")
758721d5daSMichal Berger
768721d5daSMichal Berger	((${#files[@]} > 0)) || return 0
778721d5daSMichal Berger
788721d5daSMichal Berger	echo -n "Checking file permissions..."
79d61da0ccSBen Walker
80d705ab93SBen Walker	while read -r perm _res0 _res1 path; do
81d705ab93SBen Walker		if [ ! -f "$path" ]; then
82d705ab93SBen Walker			continue
83d705ab93SBen Walker		fi
84d705ab93SBen Walker
853425fc1fSMichal Berger		# Skip symlinks
863425fc1fSMichal Berger		if [[ -L $path ]]; then
873425fc1fSMichal Berger			continue
883425fc1fSMichal Berger		fi
89d705ab93SBen Walker		fname=$(basename -- "$path")
90d705ab93SBen Walker
91d705ab93SBen Walker		case ${fname##*.} in
92d705ab93SBen Walker			c | h | cpp | cc | cxx | hh | hpp | md | html | js | json | svg | Doxyfile | yml | LICENSE | README | conf | in | Makefile | mk | gitignore | go | txt)
93d705ab93SBen Walker				# These file types should never be executable
94d705ab93SBen Walker				if [ "$perm" -eq 100755 ]; then
95d705ab93SBen Walker					echo "ERROR: $path is marked executable but is a code file."
96d705ab93SBen Walker					rc=1
97d705ab93SBen Walker				fi
98d705ab93SBen Walker				;;
99d705ab93SBen Walker			*)
100d705ab93SBen Walker				shebang=$(head -n 1 $path | cut -c1-3)
101d705ab93SBen Walker
102d705ab93SBen Walker				# git only tracks the execute bit, so will only ever return 755 or 644 as the permission.
103d705ab93SBen Walker				if [ "$perm" -eq 100755 ]; then
104d705ab93SBen Walker					# If the file has execute permission, it should start with a shebang.
105d705ab93SBen Walker					if [ "$shebang" != "#!/" ]; then
106d705ab93SBen Walker						echo "ERROR: $path is marked executable but does not start with a shebang."
107d705ab93SBen Walker						rc=1
108d705ab93SBen Walker					fi
109d705ab93SBen Walker				else
110d705ab93SBen Walker					# If the file does not have execute permissions, it should not start with a shebang.
111d705ab93SBen Walker					if [ "$shebang" = "#!/" ]; then
112d705ab93SBen Walker						echo "ERROR: $path is not marked executable but starts with a shebang."
113d705ab93SBen Walker						rc=1
114d705ab93SBen Walker					fi
115d705ab93SBen Walker				fi
116d705ab93SBen Walker				;;
117d705ab93SBen Walker		esac
118d705ab93SBen Walker
1198721d5daSMichal Berger	done < <(git ls-files -s "${files[@]}")
120d705ab93SBen Walker
121d705ab93SBen Walker	if [ $rc -eq 0 ]; then
122d705ab93SBen Walker		echo " OK"
123d705ab93SBen Walker	fi
124d705ab93SBen Walker
125d61da0ccSBen Walker	return $rc
126d61da0ccSBen Walker}
127d61da0ccSBen Walker
128d61da0ccSBen Walkerfunction check_c_style() {
129d61da0ccSBen Walker	local rc=0
130d61da0ccSBen Walker
13170044923SDaniel Verkamp	if hash astyle; then
13270044923SDaniel Verkamp		echo -n "Checking coding style..."
133c08f917fSSebastian Brzezinka		version=$(astyle --version | awk '{print $NF}')
1340616beb5SArtur Paszkiewicz		if lt "$version" 3.0.1 || gt "$version" 3.1; then
1350616beb5SArtur Paszkiewicz			echo " Your astyle version is not compatible so skipping coding style checks. Please use astyle version between 3.0.1 and 3.1"
136c08f917fSSebastian Brzezinka			rc=1
137760bb7e8SJim Harris		else
13870044923SDaniel Verkamp			rm -f astyle.log
13970044923SDaniel Verkamp			touch astyle.log
1401eb35ac7SJim Harris			# Exclude DPDK header files copied into our tree
141a9eea50dSAnton Nayshtut			git ls-files '*.[ch]' ':!:*/env_dpdk/*/*.h' ':!:include/linux/fuse_kernel.h' \
1428dd1cd21SBen Walker				| xargs -P$(nproc) -n10 astyle --break-return-type --attach-return-type-decl \
14308477ef8SBen Walker					--options=.astylerc >> astyle.log
14408477ef8SBen Walker			git ls-files '*.cpp' '*.cc' '*.cxx' '*.hh' '*.hpp' \
145844c8ec3SMichal Berger				| xargs -P$(nproc) -n10 astyle --options=.astylerc >> astyle.log
14670044923SDaniel Verkamp			if grep -q "^Formatted" astyle.log; then
14770044923SDaniel Verkamp				echo " errors detected"
148131e6b02SMichal Berger				git diff --ignore-submodules=all
14970044923SDaniel Verkamp				sed -i -e 's/  / /g' astyle.log
15070044923SDaniel Verkamp				grep --color=auto "^Formatted.*" astyle.log
15170044923SDaniel Verkamp				echo "Incorrect code style detected in one or more files."
15270044923SDaniel Verkamp				echo "The files have been automatically formatted."
15370044923SDaniel Verkamp				echo "Remember to add the files to your commit."
15438d41acdSDaniel Verkamp				rc=1
15538d41acdSDaniel Verkamp			else
15670044923SDaniel Verkamp				echo " OK"
15738d41acdSDaniel Verkamp			fi
15870044923SDaniel Verkamp			rm -f astyle.log
159760bb7e8SJim Harris		fi
16070044923SDaniel Verkamp	else
16170044923SDaniel Verkamp		echo "You do not have astyle installed so your code style is not being checked!"
16270044923SDaniel Verkamp	fi
163d61da0ccSBen Walker	return $rc
164d61da0ccSBen Walker}
16570044923SDaniel Verkamp
166d61da0ccSBen Walkerfunction check_comment_style() {
167d61da0ccSBen Walker	local rc=0
1684171d1f5STomasz Kulasek
1696b919432SDaniel Verkamp	echo -n "Checking comment style..."
1706b919432SDaniel Verkamp
171415abe87SDayu Liu	git grep --line-number -e '\/[*][^ *-]' -- '*.[ch]' > comment.log || true
172415abe87SDayu Liu	git grep --line-number -e '[^ ][*]\/' -- '*.[ch]' ':!lib/rte_vhost*/*' >> comment.log || true
173a9eea50dSAnton Nayshtut	git grep --line-number -e '^[*]' -- '*.[ch]' ':!include/linux/fuse_kernel.h' >> comment.log || true
174415abe87SDayu Liu	git grep --line-number -e '\s\/\/' -- '*.[ch]' >> comment.log || true
175415abe87SDayu Liu	git grep --line-number -e '^\/\/' -- '*.[ch]' >> comment.log || true
1766b919432SDaniel Verkamp
1776b919432SDaniel Verkamp	if [ -s comment.log ]; then
1786b919432SDaniel Verkamp		echo " Incorrect comment formatting detected"
1796b919432SDaniel Verkamp		cat comment.log
1806b919432SDaniel Verkamp		rc=1
1816b919432SDaniel Verkamp	else
1826b919432SDaniel Verkamp		echo " OK"
1836b919432SDaniel Verkamp	fi
1846b919432SDaniel Verkamp	rm -f comment.log
1856b919432SDaniel Verkamp
186d61da0ccSBen Walker	return $rc
187d61da0ccSBen Walker}
188d61da0ccSBen Walker
189d61da0ccSBen Walkerfunction check_spaces_before_tabs() {
190d61da0ccSBen Walker	local rc=0
191d61da0ccSBen Walker
1928a6ba58cSDaniel Verkamp	echo -n "Checking for spaces before tabs..."
193c39e1e96STomasz Kulasek	git grep --line-number $' \t' -- './*' ':!*.patch' > whitespace.log || true
1948a6ba58cSDaniel Verkamp	if [ -s whitespace.log ]; then
1958a6ba58cSDaniel Verkamp		echo " Spaces before tabs detected"
1968a6ba58cSDaniel Verkamp		cat whitespace.log
1978a6ba58cSDaniel Verkamp		rc=1
1988a6ba58cSDaniel Verkamp	else
1998a6ba58cSDaniel Verkamp		echo " OK"
2008a6ba58cSDaniel Verkamp	fi
2018a6ba58cSDaniel Verkamp	rm -f whitespace.log
2028a6ba58cSDaniel Verkamp
203d61da0ccSBen Walker	return $rc
204d61da0ccSBen Walker}
205d61da0ccSBen Walker
206d61da0ccSBen Walkerfunction check_trailing_whitespace() {
207d61da0ccSBen Walker	local rc=0
208d61da0ccSBen Walker
20964465d0dSDaniel Verkamp	echo -n "Checking trailing whitespace in output strings..."
21064465d0dSDaniel Verkamp
21164465d0dSDaniel Verkamp	git grep --line-number -e ' \\n"' -- '*.[ch]' > whitespace.log || true
21264465d0dSDaniel Verkamp
21364465d0dSDaniel Verkamp	if [ -s whitespace.log ]; then
21464465d0dSDaniel Verkamp		echo " Incorrect trailing whitespace detected"
21564465d0dSDaniel Verkamp		cat whitespace.log
21664465d0dSDaniel Verkamp		rc=1
21764465d0dSDaniel Verkamp	else
21864465d0dSDaniel Verkamp		echo " OK"
21964465d0dSDaniel Verkamp	fi
22064465d0dSDaniel Verkamp	rm -f whitespace.log
22164465d0dSDaniel Verkamp
222d61da0ccSBen Walker	return $rc
223d61da0ccSBen Walker}
224d61da0ccSBen Walker
225d61da0ccSBen Walkerfunction check_forbidden_functions() {
226d61da0ccSBen Walker	local rc=0
227d61da0ccSBen Walker
228474fcf64SJim Harris	echo -n "Checking for use of forbidden library functions..."
229474fcf64SJim Harris
230*b37db069SXuQi	git grep --line-number -w '\(atoi\|atol\|atoll\|strncpy\|strcpy\|strcat\|sprintf\|vsprintf\|strtok\)' -- './*.c' ':!lib/rte_vhost*/**' > badfunc.log || true
231474fcf64SJim Harris	if [ -s badfunc.log ]; then
232474fcf64SJim Harris		echo " Forbidden library functions detected"
233474fcf64SJim Harris		cat badfunc.log
234474fcf64SJim Harris		rc=1
235474fcf64SJim Harris	else
236474fcf64SJim Harris		echo " OK"
237474fcf64SJim Harris	fi
238474fcf64SJim Harris	rm -f badfunc.log
239474fcf64SJim Harris
240d61da0ccSBen Walker	return $rc
241d61da0ccSBen Walker}
242d61da0ccSBen Walker
243d61da0ccSBen Walkerfunction check_cunit_style() {
244d61da0ccSBen Walker	local rc=0
245d61da0ccSBen Walker
24656d35c5dSJim Harris	echo -n "Checking for use of forbidden CUnit macros..."
24756d35c5dSJim Harris
248ae431e31SKonrad Sztyber	git grep --line-number -w 'CU_ASSERT_FATAL' -- 'test/*' ':!include/spdk_internal/cunit.h' > badcunit.log || true
24956d35c5dSJim Harris	if [ -s badcunit.log ]; then
25056d35c5dSJim Harris		echo " Forbidden CU_ASSERT_FATAL usage detected - use SPDK_CU_ASSERT_FATAL instead"
25156d35c5dSJim Harris		cat badcunit.log
25256d35c5dSJim Harris		rc=1
25356d35c5dSJim Harris	else
25456d35c5dSJim Harris		echo " OK"
25556d35c5dSJim Harris	fi
25656d35c5dSJim Harris	rm -f badcunit.log
25756d35c5dSJim Harris
258d61da0ccSBen Walker	return $rc
259d61da0ccSBen Walker}
260d61da0ccSBen Walker
261d61da0ccSBen Walkerfunction check_eof() {
262d61da0ccSBen Walker	local rc=0
263d61da0ccSBen Walker
26438d41acdSDaniel Verkamp	echo -n "Checking blank lines at end of file..."
26557a1e893SDaniel Verkamp
266844c8ec3SMichal Berger	if ! git grep -I -l -e . -z './*' ':!*.patch' \
267844c8ec3SMichal Berger		| xargs -0 -P$(nproc) -n1 scripts/eofnl > eofnl.log; then
26838d41acdSDaniel Verkamp		echo " Incorrect end-of-file formatting detected"
26938d41acdSDaniel Verkamp		cat eofnl.log
27038d41acdSDaniel Verkamp		rc=1
27138d41acdSDaniel Verkamp	else
27238d41acdSDaniel Verkamp		echo " OK"
27338d41acdSDaniel Verkamp	fi
27438d41acdSDaniel Verkamp	rm -f eofnl.log
27538d41acdSDaniel Verkamp
276d61da0ccSBen Walker	return $rc
277d61da0ccSBen Walker}
278d61da0ccSBen Walker
279d61da0ccSBen Walkerfunction check_posix_includes() {
280d61da0ccSBen Walker	local rc=0
281d61da0ccSBen Walker
2821b276442SBen Walker	echo -n "Checking for POSIX includes..."
2831eb35ac7SJim Harris	git grep -I -i -f scripts/posix.txt -- './*' ':!include/spdk/stdinc.h' \
2841eb35ac7SJim Harris		':!include/linux/**' ':!scripts/posix.txt' ':!lib/env_dpdk/*/*.h' \
2851eb35ac7SJim Harris		':!*.patch' ':!configure' > scripts/posix.log || true
2861b276442SBen Walker	if [ -s scripts/posix.log ]; then
2871b276442SBen Walker		echo "POSIX includes detected. Please include spdk/stdinc.h instead."
2881b276442SBen Walker		cat scripts/posix.log
2891b276442SBen Walker		rc=1
2901b276442SBen Walker	else
2911b276442SBen Walker		echo " OK"
2921b276442SBen Walker	fi
2931b276442SBen Walker	rm -f scripts/posix.log
2941b276442SBen Walker
295d61da0ccSBen Walker	return $rc
296d61da0ccSBen Walker}
297d61da0ccSBen Walker
298dac8b27bSKonrad Sztybercheck_function_conventions() {
299d61da0ccSBen Walker	local rc=0
300d61da0ccSBen Walker
3010506fd21SSeth Howell	echo -n "Checking for proper function naming conventions..."
302a3549037SSeth Howell	# commit_to_compare = HEAD - 1.
303a3549037SSeth Howell	commit_to_compare="$(git log --pretty=oneline --skip=1 -n 1 | awk '{print $1}')"
3040506fd21SSeth Howell	failed_naming_conventions=false
3050506fd21SSeth Howell	changed_c_libs=()
3060506fd21SSeth Howell	declared_symbols=()
3070506fd21SSeth Howell
30863ff27f9SJim Harris	# Build an array of all the modified C libraries.
30963ff27f9SJim Harris	mapfile -t changed_c_libs < <(git diff --name-only HEAD $commit_to_compare -- lib/**/*.c module/**/*.c | xargs -r dirname | sort | uniq)
3105e911c0cSKonrad Sztyber	# Capture the names of all function declarations
3115e911c0cSKonrad Sztyber	mapfile -t declared_symbols < <(grep -Eh 'spdk_[a-zA-Z0-9_]*\(' include/spdk*/*.h \
3125e911c0cSKonrad Sztyber		| sed -En 's/.*(spdk[a-z,A-Z,0-9,_]*)\(.*/\1/p')
3130506fd21SSeth Howell
31463ff27f9SJim Harris	for c_lib in "${changed_c_libs[@]}"; do
3150506fd21SSeth Howell		lib_map_file="mk/spdk_blank.map"
3160506fd21SSeth Howell		defined_symbols=()
317c2312f6cSSeth Howell		removed_symbols=()
3180506fd21SSeth Howell		exported_symbols=()
31963ff27f9SJim Harris		if ls "$c_lib"/*.map &> /dev/null; then
32063ff27f9SJim Harris			lib_map_file="$(ls "$c_lib"/*.map)"
3210506fd21SSeth Howell		fi
3220506fd21SSeth Howell		# Matching groups are 1. leading +sign. 2, function name 3. argument list / anything after that.
3230506fd21SSeth Howell		# Capture just the names of newly added (or modified) functions that start with "spdk_"
32463ff27f9SJim Harris		mapfile -t defined_symbols < <(git diff -U0 $commit_to_compare HEAD -- $c_lib | sed -En 's/(^[+])(spdk[a-z,A-Z,0-9,_]*)(\(.*)/\2/p')
325c2312f6cSSeth Howell		# Capture the names of removed symbols to catch edge cases where we just move definitions around.
32663ff27f9SJim Harris		mapfile -t removed_symbols < <(git diff -U0 $commit_to_compare HEAD -- $c_lib | sed -En 's/(^[-])(spdk[a-z,A-Z,0-9,_]*)(\(.*)/\2/p')
327c2312f6cSSeth Howell		for symbol in "${removed_symbols[@]}"; do
328de016f27STomasz Zawadzki			for i in "${!defined_symbols[@]}"; do
329de016f27STomasz Zawadzki				if [[ ${defined_symbols[i]} = "$symbol" ]]; then
330de016f27STomasz Zawadzki					unset -v 'defined_symbols[i]'
331de016f27STomasz Zawadzki				fi
332de016f27STomasz Zawadzki			done
333c2312f6cSSeth Howell		done
3340506fd21SSeth Howell		# It's possible that we just modified a functions arguments so unfortunately we can't just look at changed lines in this function.
3350506fd21SSeth Howell		# matching groups are 1. All leading whitespace 2. function name. Capture just the symbol name.
3360506fd21SSeth Howell		mapfile -t exported_symbols < <(sed -En 's/(^[[:space:]]*)(spdk[a-z,A-Z,0-9,_]*);/\2/p' < $lib_map_file)
3370506fd21SSeth Howell		for defined_symbol in "${defined_symbols[@]}"; do
338c2312f6cSSeth Howell			# if the list of defined symbols is equal to the list of removed symbols, then we are left with a single empty element. skip it.
339c2312f6cSSeth Howell			if [ "$defined_symbol" = '' ]; then
340c2312f6cSSeth Howell				continue
341c2312f6cSSeth Howell			fi
3420506fd21SSeth Howell			not_exported=true
3430506fd21SSeth Howell			not_declared=true
3440506fd21SSeth Howell			if array_contains_string exported_symbols $defined_symbol; then
3450506fd21SSeth Howell				not_exported=false
3460506fd21SSeth Howell			fi
3470506fd21SSeth Howell
3480506fd21SSeth Howell			if array_contains_string declared_symbols $defined_symbol; then
3490506fd21SSeth Howell				not_declared=false
3500506fd21SSeth Howell			fi
3510506fd21SSeth Howell
3520506fd21SSeth Howell			if $not_exported || $not_declared; then
3530506fd21SSeth Howell				if ! $failed_naming_conventions; then
3540506fd21SSeth Howell					echo " found naming convention errors."
3550506fd21SSeth Howell				fi
3560506fd21SSeth Howell				echo "function $defined_symbol starts with spdk_ which is reserved for public API functions."
3570506fd21SSeth Howell				echo "Please add this function to its corresponding map file and a public header or remove the spdk_ prefix."
3580506fd21SSeth Howell				failed_naming_conventions=true
3590506fd21SSeth Howell				rc=1
3600506fd21SSeth Howell			fi
3610506fd21SSeth Howell		done
3620506fd21SSeth Howell	done
3630506fd21SSeth Howell
3640506fd21SSeth Howell	if ! $failed_naming_conventions; then
3650506fd21SSeth Howell		echo " OK"
3660506fd21SSeth Howell	fi
3670506fd21SSeth Howell
368d61da0ccSBen Walker	return $rc
369d61da0ccSBen Walker}
370d61da0ccSBen Walker
371dac8b27bSKonrad Sztybercheck_conventions_generic() {
372dac8b27bSKonrad Sztyber	local out decltype=$1 excludes=(${@:2})
373dac8b27bSKonrad Sztyber
374dac8b27bSKonrad Sztyber	# We only care about the types defined at the beginning of a line to allow stuff like nested
375dac8b27bSKonrad Sztyber	# structs.  Also, we need to drop any __attribute__ declarations.
376dac8b27bSKonrad Sztyber	out=$(git grep -E "^$decltype\s+\w+.*\{$" -- "include/spdk" "${excludes[@]}" \
377dac8b27bSKonrad Sztyber		| sed 's/__attribute__\s*(([[:alnum:]_, ()]*))\s*//g' \
378dac8b27bSKonrad Sztyber		| awk "!/$decltype\s+spdk_/ { \$(NF--)=\"\"; print }")
379dac8b27bSKonrad Sztyber
380dac8b27bSKonrad Sztyber	if [[ -n "$out" ]]; then
381dac8b27bSKonrad Sztyber		cat <<- WARN
382dac8b27bSKonrad Sztyber			Found $decltype declarations without the spdk_ prefix:
383dac8b27bSKonrad Sztyber
384dac8b27bSKonrad Sztyber			$out
385dac8b27bSKonrad Sztyber		WARN
386dac8b27bSKonrad Sztyber		return 1
387dac8b27bSKonrad Sztyber	fi
388dac8b27bSKonrad Sztyber}
389dac8b27bSKonrad Sztyber
390dac8b27bSKonrad Sztybercheck_naming_conventions() {
391dac8b27bSKonrad Sztyber	check_function_conventions
392dac8b27bSKonrad Sztyber	# There are still some enums without the spdk_ prefix.  Once they're renamed, we can remove
393dac8b27bSKonrad Sztyber	# these excludes
394dac8b27bSKonrad Sztyber	check_conventions_generic 'enum' \
395dac8b27bSKonrad Sztyber		':!include/spdk/blob.h' \
396dac8b27bSKonrad Sztyber		':!include/spdk/ftl.h' \
397dac8b27bSKonrad Sztyber		':!include/spdk/idxd_spec.h' \
398dac8b27bSKonrad Sztyber		':!include/spdk/iscsi_spec.h' \
399dac8b27bSKonrad Sztyber		':!include/spdk/lvol.h' \
400dac8b27bSKonrad Sztyber		':!include/spdk/nvmf_fc_spec.h' \
401dac8b27bSKonrad Sztyber		':!include/spdk/vfio_user_spec.h'
402dac8b27bSKonrad Sztyber	# Same with structs
403dac8b27bSKonrad Sztyber	check_conventions_generic 'struct' \
404dac8b27bSKonrad Sztyber		':!include/spdk/ftl.h' \
405dac8b27bSKonrad Sztyber		':!include/spdk/idxd_spec.h' \
406dac8b27bSKonrad Sztyber		':!include/spdk/iscsi_spec.h' \
407dac8b27bSKonrad Sztyber		':!include/spdk/vfio_user_spec.h'
408dac8b27bSKonrad Sztyber}
409dac8b27bSKonrad Sztyber
410d61da0ccSBen Walkerfunction check_include_style() {
411d61da0ccSBen Walker	local rc=0
412d61da0ccSBen Walker
4139fd924cbSBen Walker	echo -n "Checking #include style..."
4149fd924cbSBen Walker	git grep -I -i --line-number "#include <spdk/" -- '*.[ch]' > scripts/includes.log || true
4159fd924cbSBen Walker	if [ -s scripts/includes.log ]; then
4169fd924cbSBen Walker		echo "Incorrect #include syntax. #includes of spdk/ files should use quotes."
4179fd924cbSBen Walker		cat scripts/includes.log
4189fd924cbSBen Walker		rc=1
4199fd924cbSBen Walker	else
4209fd924cbSBen Walker		echo " OK"
4219fd924cbSBen Walker	fi
4229fd924cbSBen Walker	rm -f scripts/includes.log
4239fd924cbSBen Walker
424d61da0ccSBen Walker	return $rc
425d61da0ccSBen Walker}
426d61da0ccSBen Walker
427dff98e73SKonrad Sztyberfunction check_opts_structs() {
428dff98e73SKonrad Sztyber	local IFS="|" out types=(
429dff98e73SKonrad Sztyber		spdk_nvme_ns_cmd_ext_io_opts
430dff98e73SKonrad Sztyber		spdk_dif_ctx_init_ext_opts
431dff98e73SKonrad Sztyber	)
432dff98e73SKonrad Sztyber
433dff98e73SKonrad Sztyber	if out=$(git grep -InE "(\.|->)size[[:space:]]*=[[:space:]]*sizeof\(struct (${types[*]})\)"); then
434dff98e73SKonrad Sztyber		cat <<- WARN
435dff98e73SKonrad Sztyber			Found incorrect *ext_opts struct usage.  Use SPDK_SIZEOF() to calculate its size.
436dff98e73SKonrad Sztyber
437dff98e73SKonrad Sztyber			$out
438dff98e73SKonrad Sztyber		WARN
439dff98e73SKonrad Sztyber		return 1
440dff98e73SKonrad Sztyber	fi
441dff98e73SKonrad Sztyber}
442dff98e73SKonrad Sztyber
4431334e3e4SKonrad Sztyberfunction check_attr_packed() {
4441334e3e4SKonrad Sztyber	local out
4451334e3e4SKonrad Sztyber
4461334e3e4SKonrad Sztyber	# For now, we only care about the packed attribute in selected files.  We only check those
4471334e3e4SKonrad Sztyber	# used by Timberland (see https://github.com/timberland-sig), as they're using msvc, which
4481334e3e4SKonrad Sztyber	# doesn't support the __attribute__ keyword.
4491334e3e4SKonrad Sztyber	if out=$(git grep -In '__attribute__((packed))' \
4501334e3e4SKonrad Sztyber		'include/spdk/nvme*.h' \
4511334e3e4SKonrad Sztyber		'include/spdk/sock.h' \
4521334e3e4SKonrad Sztyber		'include/spdk_internal/nvme*.h' \
4531334e3e4SKonrad Sztyber		'lib/nvme' 'lib/sock'); then
4541334e3e4SKonrad Sztyber		cat <<- WARN
4551334e3e4SKonrad Sztyber			Found forbidden __attribute__((packed)).  Try to pack the structures manually or
4561334e3e4SKonrad Sztyber			use #pragma pack instead.
4571334e3e4SKonrad Sztyber
4581334e3e4SKonrad Sztyber			$out
4591334e3e4SKonrad Sztyber		WARN
4601334e3e4SKonrad Sztyber		return 1
4611334e3e4SKonrad Sztyber	fi
4621334e3e4SKonrad Sztyber}
4631334e3e4SKonrad Sztyber
464d61da0ccSBen Walkerfunction check_python_style() {
465d61da0ccSBen Walker	local rc=0
466d61da0ccSBen Walker
467b7322649SJohn Meneghini	if hash pycodestyle 2> /dev/null; then
468b7322649SJohn Meneghini		PEP8=pycodestyle
46905da4f3bSDaniel Verkamp	elif hash pep8 2> /dev/null; then
47005da4f3bSDaniel Verkamp		PEP8=pep8
471b7322649SJohn Meneghini	fi
472b7322649SJohn Meneghini
4731ccc878eSKarol Latecki	if [ -n "${PEP8}" ]; then
47438d41acdSDaniel Verkamp		echo -n "Checking Python style..."
47538d41acdSDaniel Verkamp
476b3487cb7SPanfil, Wojciech		PEP8_ARGS=" --max-line-length=140"
47738d41acdSDaniel Verkamp
47838d41acdSDaniel Verkamp		error=0
47938103f1fSPawel Wodkowski		git ls-files '*.py' | xargs -P$(nproc) -n1 $PEP8 $PEP8_ARGS > pep8.log || error=1
48038d41acdSDaniel Verkamp		if [ $error -ne 0 ]; then
48138d41acdSDaniel Verkamp			echo " Python formatting errors detected"
48238d41acdSDaniel Verkamp			cat pep8.log
48338d41acdSDaniel Verkamp			rc=1
48438d41acdSDaniel Verkamp		else
48538d41acdSDaniel Verkamp			echo " OK"
48638d41acdSDaniel Verkamp		fi
48738d41acdSDaniel Verkamp		rm -f pep8.log
4887fe37b86SDaniel Verkamp	else
4897fe37b86SDaniel Verkamp		echo "You do not have pycodestyle or pep8 installed so your Python style is not being checked!"
49038d41acdSDaniel Verkamp	fi
49138d41acdSDaniel Verkamp
492d61da0ccSBen Walker	return $rc
493d61da0ccSBen Walker}
494d61da0ccSBen Walker
495c1c889b5Sehaligowfunction check_golang_style() {
496c1c889b5Sehaligow	local rc=0 out
497c1c889b5Sehaligow
498c1c889b5Sehaligow	if hash golangci-lint 2> /dev/null; then
499c1c889b5Sehaligow		echo -n "Checking Golang style..."
500150b302cSEmilia Haligowska		gofmtOut=$(gofmt -d . 2>&1)
501150b302cSEmilia Haligowska		golintOut=$(find "$rootdir/go" -name go.mod -execdir golangci-lint run \; 2>&1)
502150b302cSEmilia Haligowska		if [[ -n "$gofmtOut" ]] && [[ $gofmtOut == *"diff"* ]]; then
503c1c889b5Sehaligow			cat <<- WARN
504c1c889b5Sehaligow				Golang formatting errors detected:
505150b302cSEmilia Haligowska				echo "$gofmtOut"
506150b302cSEmilia Haligowska			WARN
507150b302cSEmilia Haligowska
508150b302cSEmilia Haligowska			return 1
509150b302cSEmilia Haligowska		elif [[ -n "$golintOut" ]]; then
510150b302cSEmilia Haligowska			cat <<- WARN
511150b302cSEmilia Haligowska				Golangci lint failed:
512150b302cSEmilia Haligowska				echo "$golintOut"
513c1c889b5Sehaligow			WARN
514c1c889b5Sehaligow
515c1c889b5Sehaligow			return 1
516c1c889b5Sehaligow		else
517c1c889b5Sehaligow			echo "OK"
518c1c889b5Sehaligow		fi
519c1c889b5Sehaligow	else
520c1c889b5Sehaligow		echo "You do not have golangci-lint installed, so Golang style will not be checked!"
521c1c889b5Sehaligow	fi
522c1c889b5Sehaligow	return 0
523c1c889b5Sehaligow}
524c1c889b5Sehaligow
525e7ab45baSMichal Bergerfunction get_bash_files() {
526e7ab45baSMichal Berger	local sh shebang
527e7ab45baSMichal Berger
528e7ab45baSMichal Berger	mapfile -t sh < <(git ls-files '*.sh')
529e7ab45baSMichal Berger	mapfile -t shebang < <(git grep -l '^#!.*bash')
530e7ab45baSMichal Berger
5318721d5daSMichal Berger	get_diffed_dups "${sh[@]}" "${shebang[@]}"
532e7ab45baSMichal Berger}
533e7ab45baSMichal Berger
534d61da0ccSBen Walkerfunction check_bash_style() {
535dfb2950fSMichal Berger	local rc=0 supported_shfmt_version=v3.8.0
536d61da0ccSBen Walker
537e5f021c1SDarek Stojaczyk	# find compatible shfmt binary
5382e3c69d7SKonrad Sztyber	shfmt_bins=($(compgen -c | grep '^shfmt' | uniq || true))
5392e3c69d7SKonrad Sztyber	for bin in "${shfmt_bins[@]}"; do
5406e2ba81eSJim Harris		shfmt_version=$("$bin" --version)
5412e3c69d7SKonrad Sztyber		if [[ $shfmt_version == "$supported_shfmt_version" ]]; then
542e5f021c1SDarek Stojaczyk			shfmt=$bin
543e5f021c1SDarek Stojaczyk			break
544e5f021c1SDarek Stojaczyk		fi
545e5f021c1SDarek Stojaczyk	done
546e5f021c1SDarek Stojaczyk
547e5f021c1SDarek Stojaczyk	if [ -n "$shfmt" ]; then
5480968ee2fSMichal Berger		shfmt_cmdline=() sh_files=()
549d596ba87SMichal Berger
5500968ee2fSMichal Berger		mapfile -t sh_files < <(get_bash_files)
551d596ba87SMichal Berger
552d596ba87SMichal Berger		if ((${#sh_files[@]})); then
5534bd48a3dSSeth Howell			printf 'Checking .sh formatting style...'
554d596ba87SMichal Berger
555d596ba87SMichal Berger			shfmt_cmdline+=(-i 0)     # indent_style = tab|indent_size = 0
556d596ba87SMichal Berger			shfmt_cmdline+=(-bn)      # binary_next_line = true
557d596ba87SMichal Berger			shfmt_cmdline+=(-ci)      # switch_case_indent = true
558d596ba87SMichal Berger			shfmt_cmdline+=(-ln bash) # shell_variant = bash (default)
559d596ba87SMichal Berger			shfmt_cmdline+=(-d)       # diffOut - print diff of the changes and exit with != 0
560d596ba87SMichal Berger			shfmt_cmdline+=(-sr)      # redirect operators will be followed by a space
561d596ba87SMichal Berger
562c9883483SMichal Berger			diff=${output_dir:-$PWD}/$shfmt.patch
563d596ba87SMichal Berger
564dfb2950fSMichal Berger			if ! "$shfmt" "${shfmt_cmdline[@]}" "${sh_files[@]}" > "$diff"; then
565d596ba87SMichal Berger				# In case shfmt detects an actual syntax error it will write out a proper message on
566d596ba87SMichal Berger				# its stderr, hence the diff file should remain empty.
567b870a4cfSMichal Berger				rc=1
568d596ba87SMichal Berger				if [[ -s $diff ]]; then
569b870a4cfSMichal Berger					if patch --merge -p0 < "$diff"; then
570b870a4cfSMichal Berger						diff_out=$(git diff)
571d596ba87SMichal Berger
572b870a4cfSMichal Berger						if [[ -n $diff_out ]]; then
573d596ba87SMichal Berger							cat <<- ERROR_SHFMT
574d596ba87SMichal Berger
575d596ba87SMichal Berger								* Errors in style formatting have been detected.
576b870a4cfSMichal Berger								  Please, review the generated patch at $diff
577d596ba87SMichal Berger
578d596ba87SMichal Berger								# _START_OF_THE_DIFF
579d596ba87SMichal Berger
580b870a4cfSMichal Berger								$diff_out
581d596ba87SMichal Berger
582d596ba87SMichal Berger								# _END_OF_THE_DIFF
583d596ba87SMichal Berger
584d596ba87SMichal Berger							ERROR_SHFMT
585b870a4cfSMichal Berger						else
586b870a4cfSMichal Berger							# Empty diff? This likely means that we reverted to a clean state
587b870a4cfSMichal Berger							printf '* Patch reverted, please review your changes and %s\n' "$diff"
588b870a4cfSMichal Berger						fi
589b870a4cfSMichal Berger					else
590b870a4cfSMichal Berger						printf '* Failed to apply %s\n' "$diff"
591b870a4cfSMichal Berger
592b870a4cfSMichal Berger					fi
593b870a4cfSMichal Berger				fi
594d596ba87SMichal Berger			else
595d596ba87SMichal Berger				rm -f "$diff"
596d596ba87SMichal Berger				printf ' OK\n'
597d596ba87SMichal Berger			fi
598d596ba87SMichal Berger		fi
599d596ba87SMichal Berger	else
6002e3c69d7SKonrad Sztyber		cat <<- MSG
6012e3c69d7SKonrad Sztyber			Supported version of shfmt not detected${shfmt_bins[*]:+ (only ${shfmt_bins[*]} is available)}.  Bash style
6022e3c69d7SKonrad Sztyber			formatting check is skipped.  shfmt-$supported_shfmt_version can be installed using 'scripts/pkgdep.sh -d'.
6032e3c69d7SKonrad Sztyber		MSG
604d596ba87SMichal Berger	fi
605d596ba87SMichal Berger
606b870a4cfSMichal Berger	# Cleanup potential .orig files that shfmt creates
607b870a4cfSMichal Berger	local orig_f
608b870a4cfSMichal Berger
609b870a4cfSMichal Berger	mapfile -t orig_f < <(git diff --name-only)
610b870a4cfSMichal Berger	orig_f=("${orig_f[@]/%/.orig}")
611b870a4cfSMichal Berger
612b870a4cfSMichal Berger	if ((${#orig_f[@]} > 0)); then
613b870a4cfSMichal Berger		git clean -f "${orig_f[@]}"
614b870a4cfSMichal Berger	fi
615b870a4cfSMichal Berger
616d61da0ccSBen Walker	return $rc
617d61da0ccSBen Walker}
618d61da0ccSBen Walker
619d61da0ccSBen Walkerfunction check_bash_static_analysis() {
6208721d5daSMichal Berger	local rc=0 files=()
6218721d5daSMichal Berger
6228721d5daSMichal Berger	mapfile -t files < <(get_bash_files)
6238721d5daSMichal Berger	((${#files[@]} > 0)) || return 0
624d61da0ccSBen Walker
625a12229dbSKarol Latecki	if hash shellcheck 2> /dev/null; then
6268dbf28acSJim Harris		echo -n "Checking Bash static analysis with shellcheck..."
627a12229dbSKarol Latecki
628a12229dbSKarol Latecki		shellcheck_v=$(shellcheck --version | grep -P "version: [0-9\.]+" | cut -d " " -f2)
629a12229dbSKarol Latecki
63050e5d883SPawel Kaminski		# SHCK_EXCLUDE contains a list of all of the spellcheck errors found in SPDK scripts
63150e5d883SPawel Kaminski		# currently. New errors should only be added to this list if the cost of fixing them
63250e5d883SPawel Kaminski		# is deemed too high. For more information about the errors, go to:
63350e5d883SPawel Kaminski		# https://github.com/koalaman/shellcheck/wiki/Checks
634a12229dbSKarol Latecki		# Error descriptions can also be found at: https://github.com/koalaman/shellcheck/wiki
6350eb4e6d5SJames Bergsten		# SPDK fails some error checks which have been deprecated in later versions of shellcheck.
6360eb4e6d5SJames Bergsten		# We will not try to fix these error checks, but instead just leave the error types here
6370eb4e6d5SJames Bergsten		# so that we can still run with older versions of shellcheck.
63850e5d883SPawel Kaminski		SHCK_EXCLUDE="SC1117"
6390ac493f5SMaciej Wawryk		# SPDK has decided to not fix violations of these errors.
6400ac493f5SMaciej Wawryk		# We are aware about below exclude list and we want this errors to be excluded.
64191dc22baSPawel Kaminski		# SC1083: This {/} is literal. Check expression (missing ;/\n?) or quote it.
6420ac493f5SMaciej Wawryk		# SC1090: Can't follow non-constant source. Use a directive to specify location.
6430ac493f5SMaciej Wawryk		# SC1091: Not following: (error message here)
64428021d2aSMaciej Wawryk		# SC2001: See if you can use ${variable//search/replace} instead.
64550e5d883SPawel Kaminski		# SC2010: Don't use ls | grep. Use a glob or a for loop with a condition to allow non-alphanumeric filenames.
646fd73cb78SSeth Howell		# SC2015: Note that A && B || C is not if-then-else. C may run when A is true.
6470951fc4fSMaciej Wawryk		# SC2016: Expressions don't expand in single quotes, use double quotes for that.
6486c274749SMaciej Wawryk		# SC2034: foo appears unused. Verify it or export it.
6497200ad81SPawel Kaminski		# SC2046: Quote this to prevent word splitting.
6509c47dd8eSPawel Kaminski		# SC2086: Double quote to prevent globbing and word splitting.
6514aa03903SMaciej Wawryk		# SC2119: Use foo "$@" if function's $1 should mean script's $1.
6521f08daebSMaciej Wawryk		# SC2120: foo references arguments, but none are ever passed.
6539d486621SMichal Berger		# SC2128: Expanding an array without an index only gives the first element.
654e3d18b50SPawel Kaminski		# SC2148: Add shebang to the top of your script.
655c2f59d6cSPawel Kaminski		# SC2153: Possible Misspelling: MYVARIABLE may not be assigned, but MY_VARIABLE is.
6564d0c5091SPawel Kaminski		# SC2154: var is referenced but not assigned.
65727ef3373SMaciej Wawryk		# SC2164: Use cd ... || exit in case cd fails.
658e0f2a6e3SPawel Kaminski		# SC2174: When used with -p, -m only applies to the deepest directory.
6599d486621SMichal Berger		# SC2178: Variable was used as an array but is now assigned a string.
6601c2291baSPawel Kaminski		# SC2206: Quote to prevent word splitting/globbing,
6611c2291baSPawel Kaminski		#         or split robustly with mapfile or read -a.
662f62c4d23SPawel Kaminski		# SC2207: Prefer mapfile or read -a to split command output (or quote to avoid splitting).
663117710eeSPawel Kaminski		# SC2223: This default assignment may cause DoS due to globbing. Quote it.
66450e5d883SPawel Kaminski		SHCK_EXCLUDE="$SHCK_EXCLUDE,SC1083,SC1090,SC1091,SC2010,SC2015,SC2016,SC2034,SC2046,SC2086,\
6659d486621SMichal BergerSC2119,SC2120,SC2128,SC2148,SC2153,SC2154,SC2164,SC2174,SC2178,SC2001,SC2206,SC2207,SC2223"
666a12229dbSKarol Latecki
667a12229dbSKarol Latecki		SHCK_FORMAT="tty"
668a12229dbSKarol Latecki		SHCK_APPLY=false
669ec632be2SMichal Berger		SHCH_ARGS="-e $SHCK_EXCLUDE -f $SHCK_FORMAT"
670ec632be2SMichal Berger
671ec632be2SMichal Berger		if ge "$shellcheck_v" 0.4.0; then
672ec632be2SMichal Berger			SHCH_ARGS+=" -x"
673ec632be2SMichal Berger		else
674ec632be2SMichal Berger			echo "shellcheck $shellcheck_v detected, recommended >= 0.4.0."
675ec632be2SMichal Berger		fi
676a12229dbSKarol Latecki
6778721d5daSMichal Berger		printf '%s\n' "${files[@]}" | xargs -P$(nproc) -n1 shellcheck $SHCH_ARGS &> shellcheck.log
6789f6f02f2SBen Walker		if [[ -s shellcheck.log ]]; then
6798dbf28acSJim Harris			echo " Bash shellcheck errors detected!"
6801a3e41f2SKarol Latecki
681a12229dbSKarol Latecki			cat shellcheck.log
682a12229dbSKarol Latecki			if $SHCK_APPLY; then
683a12229dbSKarol Latecki				git apply shellcheck.log
684a12229dbSKarol Latecki				echo "Bash errors were automatically corrected."
685a12229dbSKarol Latecki				echo "Please remember to add the changes to your commit."
686a12229dbSKarol Latecki			fi
687a12229dbSKarol Latecki			rc=1
688a12229dbSKarol Latecki		else
689a12229dbSKarol Latecki			echo " OK"
690a12229dbSKarol Latecki		fi
691a12229dbSKarol Latecki		rm -f shellcheck.log
692a12229dbSKarol Latecki	else
6938dbf28acSJim Harris		echo "You do not have shellcheck installed so your Bash static analysis is not being performed!"
694a12229dbSKarol Latecki	fi
695a12229dbSKarol Latecki
696d61da0ccSBen Walker	return $rc
697d61da0ccSBen Walker}
698d61da0ccSBen Walker
699d61da0ccSBen Walkerfunction check_changelog() {
700d61da0ccSBen Walker	local rc=0
701d61da0ccSBen Walker
702f5a4b049SBen Walker	# Check if any of the public interfaces were modified by this patch.
703f5a4b049SBen Walker	# Warn the user to consider updating the changelog any changes
704f5a4b049SBen Walker	# are detected.
705f5a4b049SBen Walker	echo -n "Checking whether CHANGELOG.md should be updated..."
706f5a4b049SBen Walker	staged=$(git diff --name-only --cached .)
707da992ebcSJim Harris	working=$(git status -s --porcelain --ignore-submodules | grep -iv "??" | awk '{print $2}')
708f5a4b049SBen Walker	files="$staged $working"
709f5a4b049SBen Walker	if [[ "$files" = " " ]]; then
710f5a4b049SBen Walker		files=$(git diff-tree --no-commit-id --name-only -r HEAD)
711f5a4b049SBen Walker	fi
712f5a4b049SBen Walker
713f5a4b049SBen Walker	has_changelog=0
714f5a4b049SBen Walker	for f in $files; do
715f5a4b049SBen Walker		if [[ $f == CHANGELOG.md ]]; then
716f5a4b049SBen Walker			# The user has a changelog entry, so exit.
717f5a4b049SBen Walker			has_changelog=1
718f5a4b049SBen Walker			break
719f5a4b049SBen Walker		fi
720f5a4b049SBen Walker	done
721f5a4b049SBen Walker
722f5a4b049SBen Walker	needs_changelog=0
723f5a4b049SBen Walker	if [ $has_changelog -eq 0 ]; then
724f5a4b049SBen Walker		for f in $files; do
725f5a4b049SBen Walker			if [[ $f == include/spdk/* ]] || [[ $f == scripts/rpc.py ]] || [[ $f == etc/* ]]; then
726f5a4b049SBen Walker				echo ""
727f5a4b049SBen Walker				echo -n "$f was modified. Consider updating CHANGELOG.md."
728f5a4b049SBen Walker				needs_changelog=1
729f5a4b049SBen Walker			fi
730f5a4b049SBen Walker		done
731f5a4b049SBen Walker	fi
732f5a4b049SBen Walker
733f5a4b049SBen Walker	if [ $needs_changelog -eq 0 ]; then
734f5a4b049SBen Walker		echo " OK"
735f5a4b049SBen Walker	else
736f5a4b049SBen Walker		echo ""
737f5a4b049SBen Walker	fi
738f5a4b049SBen Walker
739d61da0ccSBen Walker	return $rc
740d61da0ccSBen Walker}
741d61da0ccSBen Walker
74203a3a5c0SBen Walkerfunction check_json_rpc() {
7434aff2dfdSJim Harris	local rc=0
74403a3a5c0SBen Walker
7454aff2dfdSJim Harris	echo -n "Checking that all RPCs are documented..."
74603a3a5c0SBen Walker	while IFS='"' read -r _ rpc _; do
7471e1fd9acSwawryk		if ! grep -q "^### $rpc" doc/jsonrpc.md; then
74803a3a5c0SBen Walker			echo "Missing JSON-RPC documentation for ${rpc}"
74903a3a5c0SBen Walker			rc=1
75003a3a5c0SBen Walker		fi
75129784f35SKrzysztof Karas	done < <(git grep -h -E "^SPDK_RPC_REGISTER\(" ':!test/*' ':!examples/*')
75203a3a5c0SBen Walker
7534aff2dfdSJim Harris	if [ $rc -eq 0 ]; then
7544aff2dfdSJim Harris		echo " OK"
7554aff2dfdSJim Harris	fi
75603a3a5c0SBen Walker	return $rc
75703a3a5c0SBen Walker}
75803a3a5c0SBen Walker
759e4d23dc7Swawrykfunction check_markdown_format() {
760e4d23dc7Swawryk	local rc=0
761e4d23dc7Swawryk
7622f5c6025Swawryk	if hash mdl 2> /dev/null; then
763e4d23dc7Swawryk		echo -n "Checking markdown files format..."
7642f5c6025Swawryk		mdl -g -s $rootdir/mdl_rules.rb . > mdl.log || true
765e4d23dc7Swawryk		if [ -s mdl.log ]; then
766e4d23dc7Swawryk			echo " Errors in .md files detected:"
767e4d23dc7Swawryk			cat mdl.log
768e4d23dc7Swawryk			rc=1
769e4d23dc7Swawryk		else
770e4d23dc7Swawryk			echo " OK"
771e4d23dc7Swawryk		fi
772e4d23dc7Swawryk		rm -f mdl.log
7732f5c6025Swawryk	else
7742f5c6025Swawryk		echo "You do not have markdownlint installed so .md files not being checked!"
7752f5c6025Swawryk	fi
7762f5c6025Swawryk
777e4d23dc7Swawryk	return $rc
778e4d23dc7Swawryk}
779e4d23dc7Swawryk
7807a372bbeSJim Harrisfunction check_rpc_args() {
7817a372bbeSJim Harris	local rc=0
7827a372bbeSJim Harris
7837a372bbeSJim Harris	echo -n "Checking rpc.py argument option names..."
78446875520SJim Harris	grep add_argument scripts/rpc.py | $GNU_GREP -oP "(?<=--)[a-z0-9\-\_]*(?=\')" | grep "_" > badargs.log
7857a372bbeSJim Harris
7867a372bbeSJim Harris	if [[ -s badargs.log ]]; then
7877a372bbeSJim Harris		echo "rpc.py arguments with underscores detected!"
7887a372bbeSJim Harris		cat badargs.log
7897a372bbeSJim Harris		echo "Please convert the underscores to dashes."
7907a372bbeSJim Harris		rc=1
7917a372bbeSJim Harris	else
7927a372bbeSJim Harris		echo " OK"
7937a372bbeSJim Harris	fi
7947a372bbeSJim Harris	rm -f badargs.log
7957a372bbeSJim Harris	return $rc
7967a372bbeSJim Harris}
7977a372bbeSJim Harris
798c2713811SMichal Bergerfunction get_files_for_lic() {
799c2713811SMichal Berger	local f_shebang="" f_suffix=() f_type=() f_all=() exceptions=""
800c2713811SMichal Berger
801c2713811SMichal Berger	f_shebang+="bash|"
802c2713811SMichal Berger	f_shebang+="make|"
803c2713811SMichal Berger	f_shebang+="perl|"
804c2713811SMichal Berger	f_shebang+="python|"
805c2713811SMichal Berger	f_shebang+="sh"
806c2713811SMichal Berger
807c2713811SMichal Berger	f_suffix+=("*.c")
808c2713811SMichal Berger	f_suffix+=("*.cpp")
809c2713811SMichal Berger	f_suffix+=("*.h")
810c2713811SMichal Berger	f_suffix+=("*.go")
811c2713811SMichal Berger	f_suffix+=("*.mk")
812c2713811SMichal Berger	f_suffix+=("*.pl")
813c2713811SMichal Berger	f_suffix+=("*.py")
814c2713811SMichal Berger	f_suffix+=("*.sh")
815c2713811SMichal Berger	f_suffix+=("*.yaml")
816c2713811SMichal Berger
817c2713811SMichal Berger	f_type+=("*Dockerfile")
818c2713811SMichal Berger	f_type+=("*Makefile")
819c2713811SMichal Berger
820c2713811SMichal Berger	# Exclude files that may match the above types but should not
821c2713811SMichal Berger	# fall under SPDX check.
822c2713811SMichal Berger	exceptions+="include/linux|"
823b557c9c8SAlexey Marchuk	exceptions+="include/spdk/queue_extras.h|"
824b557c9c8SAlexey Marchuk	exceptions+="lib/mlx5/mlx5_ifc.h"
825c2713811SMichal Berger
826c2713811SMichal Berger	mapfile -t f_all < <(
827c2713811SMichal Berger		git ls-files "${f_suffix[@]}" "${f_type[@]}"
828c2713811SMichal Berger		git grep -lE "^#!.*($f_shebang)"
829c2713811SMichal Berger	)
830c2713811SMichal Berger
831c2713811SMichal Berger	printf '%s\n' "${f_all[@]}" | sort -u | grep -vE "$exceptions"
832c2713811SMichal Berger}
833c2713811SMichal Berger
834c2713811SMichal Bergerfunction check_spdx_lic() {
835c2713811SMichal Berger	local files_missing_license_header=() hint=()
836c2713811SMichal Berger	local rc=0
837c2713811SMichal Berger
838c2713811SMichal Berger	hint+=("SPDX-License-Identifier: BSD-3-Clause")
839c2713811SMichal Berger	hint+=("All rights reserved.")
840c2713811SMichal Berger
841c2713811SMichal Berger	printf 'Checking SPDX-license...'
842c2713811SMichal Berger
843c2713811SMichal Berger	mapfile -t files_missing_license_header < <(
844c2713811SMichal Berger		grep -LE "SPDX-License-Identifier:.+" $(get_files_for_lic)
845c2713811SMichal Berger	)
846c2713811SMichal Berger
847c2713811SMichal Berger	if ((${#files_missing_license_header[@]} > 0)); then
848c2713811SMichal Berger		printf '\nFollowing files are missing SPDX-license header:\n'
849c2713811SMichal Berger		printf '  @%s\n' "${files_missing_license_header[@]}"
850c2713811SMichal Berger		printf '\nExample:\n'
851c2713811SMichal Berger		printf '  #  %s\n' "${hint[@]}"
852c2713811SMichal Berger		return 1
853c2713811SMichal Berger	fi
854c2713811SMichal Berger
855c2713811SMichal Berger	printf 'OK\n'
856c2713811SMichal Berger}
857c2713811SMichal Berger
8588721d5daSMichal Bergerfunction get_diffed_files() {
8598721d5daSMichal Berger	# Get files where changes are meant to be committed
8608721d5daSMichal Berger	git diff --name-only HEAD HEAD~1
8618721d5daSMichal Berger	# Get files from staging
8628721d5daSMichal Berger	git diff --name-only --cached HEAD
8638721d5daSMichal Berger	git diff --name-only HEAD
8648721d5daSMichal Berger}
8658721d5daSMichal Berger
8668721d5daSMichal Bergerfunction get_diffed_dups() {
8678721d5daSMichal Berger	local files=("$@") diff=() _diff=()
8688721d5daSMichal Berger
8698721d5daSMichal Berger	# Sort and get rid of duplicates from the main list
8708721d5daSMichal Berger	mapfile -t files < <(printf '%s\n' "${files[@]}" | sort -u)
8718721d5daSMichal Berger	# Get staged|committed files
8728721d5daSMichal Berger	mapfile -t diff < <(get_diffed_files | sort -u)
8738721d5daSMichal Berger
8748721d5daSMichal Berger	if [[ ! -v CHECK_FORMAT_ONLY_DIFF ]]; then
8758721d5daSMichal Berger		# Just return the main list
8768721d5daSMichal Berger		printf '%s\n' "${files[@]}"
8778721d5daSMichal Berger		return 0
8788721d5daSMichal Berger	fi
8798721d5daSMichal Berger
8808721d5daSMichal Berger	if ((${#diff[@]} > 0)); then
8818721d5daSMichal Berger		# Check diff'ed files against the main list to see if they are a subset
8828721d5daSMichal Berger		# of it. If yes, then we return the duplicates which are the files that
8838721d5daSMichal Berger		# should be committed, modified.
8848721d5daSMichal Berger		mapfile -t _diff < <(
8858721d5daSMichal Berger			printf '%s\n' "${diff[@]}" "${files[@]}" | sort | uniq -d
8868721d5daSMichal Berger		)
8878721d5daSMichal Berger		if ((${#_diff[@]} > 0)); then
8888721d5daSMichal Berger			printf '%s\n' "${_diff[@]}"
8898721d5daSMichal Berger			return 0
8908721d5daSMichal Berger		fi
8918721d5daSMichal Berger	fi
8928721d5daSMichal Berger}
8938721d5daSMichal Berger
8948f15778fSSebastian Brzezinkafunction check_extern_c() {
8958f15778fSSebastian Brzezinka	local files_missing_extern_c=()
8968f15778fSSebastian Brzezinka
8978f15778fSSebastian Brzezinka	printf 'Checking extern "C"... '
8988f15778fSSebastian Brzezinka
8998f15778fSSebastian Brzezinka	mapfile -t files_missing_extern_c < <(
9008f15778fSSebastian Brzezinka		grep -LE "extern \"C\"" $(git ls-files -- ./include/spdk/*.h)
9018f15778fSSebastian Brzezinka	)
9028f15778fSSebastian Brzezinka
9038f15778fSSebastian Brzezinka	if ((${#files_missing_extern_c[@]} > 0)); then
9048f15778fSSebastian Brzezinka		printf '\nFollowing header files are missing extern C:\n'
9058f15778fSSebastian Brzezinka		printf '  %s\n' "${files_missing_extern_c[@]}"
9068f15778fSSebastian Brzezinka		return 1
9078f15778fSSebastian Brzezinka	fi
9088f15778fSSebastian Brzezinka
9098f15778fSSebastian Brzezinka	printf 'OK\n'
9108f15778fSSebastian Brzezinka}
9118f15778fSSebastian Brzezinka
912d61da0ccSBen Walkerrc=0
913d61da0ccSBen Walker
914d61da0ccSBen Walkercheck_permissions || rc=1
915d61da0ccSBen Walkercheck_c_style || rc=1
916d61da0ccSBen Walker
917d61da0ccSBen WalkerGIT_VERSION=$(git --version | cut -d' ' -f3)
918d61da0ccSBen Walker
919d61da0ccSBen Walkerif version_lt "1.9.5" "${GIT_VERSION}"; then
920d61da0ccSBen Walker	# git <1.9.5 doesn't support pathspec magic exclude
921d61da0ccSBen Walker	echo " Your git version is too old to perform all tests. Please update git to at least 1.9.5 version..."
922d61da0ccSBen Walker	exit $rc
923d61da0ccSBen Walkerfi
924d61da0ccSBen Walker
925d61da0ccSBen Walkercheck_comment_style || rc=1
926e4d23dc7Swawrykcheck_markdown_format || rc=1
927d61da0ccSBen Walkercheck_spaces_before_tabs || rc=1
928d61da0ccSBen Walkercheck_trailing_whitespace || rc=1
929d61da0ccSBen Walkercheck_forbidden_functions || rc=1
930d61da0ccSBen Walkercheck_cunit_style || rc=1
931d61da0ccSBen Walkercheck_eof || rc=1
932d61da0ccSBen Walkercheck_posix_includes || rc=1
93396d4bb6aSBen Walkercheck_naming_conventions || rc=1
934d61da0ccSBen Walkercheck_include_style || rc=1
935dff98e73SKonrad Sztybercheck_opts_structs || rc=1
9361334e3e4SKonrad Sztybercheck_attr_packed || rc=1
937d61da0ccSBen Walkercheck_python_style || rc=1
938d61da0ccSBen Walkercheck_bash_style || rc=1
939d61da0ccSBen Walkercheck_bash_static_analysis || rc=1
940d61da0ccSBen Walkercheck_changelog || rc=1
94103a3a5c0SBen Walkercheck_json_rpc || rc=1
9427a372bbeSJim Harrischeck_rpc_args || rc=1
943c2713811SMichal Bergercheck_spdx_lic || rc=1
944c1c889b5Sehaligowcheck_golang_style || rc=1
9458f15778fSSebastian Brzezinkacheck_extern_c || rc=1
946d61da0ccSBen Walker
94738d41acdSDaniel Verkampexit $rc
948