xref: /spdk/scripts/check_format.sh (revision be4a5602ce7d3e2d9cc7ff6cde0b0dcb99d647c8)
1#!/usr/bin/env bash
2
3readonly BASEDIR=$(readlink -f $(dirname $0))/..
4cd $BASEDIR
5
6# exit on errors
7set -e
8
9if ! hash nproc 2>/dev/null; then
10
11function nproc() {
12	echo 8
13}
14
15fi
16
17function version_lt() {
18	[ $( echo -e "$1\n$2" | sort -V | head -1 ) != "$1" ]
19}
20
21rc=0
22
23echo -n "Checking file permissions..."
24
25while read -r perm _res0 _res1 path; do
26	if [ ! -f "$path" ]; then
27		continue
28	fi
29
30	fname=$(basename -- "$path")
31
32	case ${fname##*.} in
33		c|h|cpp|cc|cxx|hh|hpp|md|html|js|json|svg|Doxyfile|yml|LICENSE|README|conf|in|Makefile|mk|gitignore|go|txt)
34			# These file types should never be executable
35			if [ "$perm" -eq 100755 ]; then
36				echo "ERROR: $path is marked executable but is a code file."
37				rc=1
38			fi
39		;;
40		*)
41			shebang=$(head -n 1 $path | cut -c1-3)
42
43			# git only tracks the execute bit, so will only ever return 755 or 644 as the permission.
44			if [ "$perm" -eq 100755 ]; then
45				# If the file has execute permission, it should start with a shebang.
46				if [ "$shebang" != "#!/" ]; then
47					echo "ERROR: $path is marked executable but does not start with a shebang."
48					rc=1
49				fi
50			else
51				# If the file doesnot have execute permissions, it should not start with a shebang.
52				if [ "$shebang" = "#!/" ]; then
53					echo "ERROR: $path is not marked executable but starts with a shebang."
54					rc=1
55				fi
56			fi
57		;;
58	esac
59
60done <<< "$(git grep -I --name-only --untracked -e . | git ls-files -s)"
61
62if [ $rc -eq 0 ]; then
63	echo " OK"
64fi
65
66if hash astyle; then
67	echo -n "Checking coding style..."
68	if [ "$(astyle -V)" \< "Artistic Style Version 3" ]
69	then
70		echo -n " Your astyle version is too old so skipping coding style checks. Please update astyle to at least 3.0.1 version..."
71	else
72		rm -f astyle.log
73		touch astyle.log
74		# Exclude rte_vhost code imported from DPDK - we want to keep the original code
75		#  as-is to enable ongoing work to synch with a generic upstream DPDK vhost library,
76		#  rather than making diffs more complicated by a lot of changes to follow SPDK
77		#  coding standards.
78		git ls-files '*.[ch]' '*.cpp' '*.cc' '*.cxx' '*.hh' '*.hpp' | \
79			grep -v rte_vhost | grep -v cpp_headers | \
80			xargs -P$(nproc) -n10 astyle --options=.astylerc >> astyle.log
81		if grep -q "^Formatted" astyle.log; then
82			echo " errors detected"
83			git diff
84			sed -i -e 's/  / /g' astyle.log
85			grep --color=auto "^Formatted.*" astyle.log
86			echo "Incorrect code style detected in one or more files."
87			echo "The files have been automatically formatted."
88			echo "Remember to add the files to your commit."
89			rc=1
90		else
91			echo " OK"
92		fi
93		rm -f astyle.log
94	fi
95else
96	echo "You do not have astyle installed so your code style is not being checked!"
97fi
98
99GIT_VERSION=$( git --version | cut -d' ' -f3 )
100
101if version_lt "1.9.5" "${GIT_VERSION}"; then
102	# git <1.9.5 doesn't support pathspec magic exclude
103	echo " Your git version is too old to perform all tests. Please update git to at least 1.9.5 version..."
104	exit 0
105fi
106
107echo -n "Checking comment style..."
108
109git grep --line-number -e '/[*][^ *-]' -- '*.[ch]' > comment.log || true
110git grep --line-number -e '[^ ][*]/' -- '*.[ch]' ':!lib/rte_vhost*/*' >> comment.log || true
111git grep --line-number -e '^[*]' -- '*.[ch]' >> comment.log || true
112git grep --line-number -e '\s//' -- '*.[ch]' >> comment.log || true
113git grep --line-number -e '^//' -- '*.[ch]' >> comment.log || true
114
115if [ -s comment.log ]; then
116	echo " Incorrect comment formatting detected"
117	cat comment.log
118	rc=1
119else
120	echo " OK"
121fi
122rm -f comment.log
123
124echo -n "Checking for spaces before tabs..."
125git grep --line-number $' \t' -- './*' ':!*.patch' > whitespace.log || true
126if [ -s whitespace.log ]; then
127	echo " Spaces before tabs detected"
128	cat whitespace.log
129	rc=1
130else
131	echo " OK"
132fi
133rm -f whitespace.log
134
135echo -n "Checking trailing whitespace in output strings..."
136
137git grep --line-number -e ' \\n"' -- '*.[ch]' > whitespace.log || true
138
139if [ -s whitespace.log ]; then
140	echo " Incorrect trailing whitespace detected"
141	cat whitespace.log
142	rc=1
143else
144	echo " OK"
145fi
146rm -f whitespace.log
147
148echo -n "Checking for use of forbidden library functions..."
149
150git grep --line-number -w '\(atoi\|atol\|atoll\|strncpy\|strcpy\|strcat\|sprintf\|vsprintf\)' -- './*.c' ':!lib/rte_vhost*/**' > badfunc.log || true
151if [ -s badfunc.log ]; then
152	echo " Forbidden library functions detected"
153	cat badfunc.log
154	rc=1
155else
156	echo " OK"
157fi
158rm -f badfunc.log
159
160echo -n "Checking for use of forbidden CUnit macros..."
161
162git grep --line-number -w 'CU_ASSERT_FATAL' -- 'test/*' ':!test/spdk_cunit.h' > badcunit.log || true
163if [ -s badcunit.log ]; then
164	echo " Forbidden CU_ASSERT_FATAL usage detected - use SPDK_CU_ASSERT_FATAL instead"
165	cat badcunit.log
166	rc=1
167else
168	echo " OK"
169fi
170rm -f badcunit.log
171
172echo -n "Checking blank lines at end of file..."
173
174if ! git grep -I -l -e . -z  './*' ':!*.patch' | \
175	xargs -0 -P$(nproc) -n1 scripts/eofnl > eofnl.log; then
176	echo " Incorrect end-of-file formatting detected"
177	cat eofnl.log
178	rc=1
179else
180	echo " OK"
181fi
182rm -f eofnl.log
183
184echo -n "Checking for POSIX includes..."
185git grep -I -i -f scripts/posix.txt -- './*' ':!include/spdk/stdinc.h' ':!include/linux/**' ':!lib/rte_vhost*/**' ':!scripts/posix.txt' ':!*.patch' > scripts/posix.log || true
186if [ -s scripts/posix.log ]; then
187	echo "POSIX includes detected. Please include spdk/stdinc.h instead."
188	cat scripts/posix.log
189	rc=1
190else
191	echo " OK"
192fi
193rm -f scripts/posix.log
194
195echo -n "Checking #include style..."
196git grep -I -i --line-number "#include <spdk/" -- '*.[ch]' > scripts/includes.log || true
197if [ -s scripts/includes.log ]; then
198	echo "Incorrect #include syntax. #includes of spdk/ files should use quotes."
199	cat scripts/includes.log
200	rc=1
201else
202	echo " OK"
203fi
204rm -f scripts/includes.log
205
206if hash pycodestyle 2>/dev/null; then
207	PEP8=pycodestyle
208elif hash pep8 2>/dev/null; then
209	PEP8=pep8
210fi
211
212if [ -n "${PEP8}" ]; then
213	echo -n "Checking Python style..."
214
215	PEP8_ARGS+=" --max-line-length=140"
216
217	error=0
218	git ls-files '*.py' | xargs -P$(nproc) -n1 $PEP8 $PEP8_ARGS > pep8.log || error=1
219	if [ $error -ne 0 ]; then
220		echo " Python formatting errors detected"
221		cat pep8.log
222		rc=1
223	else
224		echo " OK"
225	fi
226	rm -f pep8.log
227else
228	echo "You do not have pycodestyle or pep8 installed so your Python style is not being checked!"
229fi
230
231if hash shellcheck 2>/dev/null; then
232	echo -n "Checking Bash style..."
233
234	shellcheck_v=$(shellcheck --version | grep -P "version: [0-9\.]+" | cut -d " " -f2)
235
236	# Exclude list currently holds all of reported errors. Errors will be fixed and list
237	# reduced over time. If you find any new error which is not on the list and you think
238	# that it should be excluded - please create a GitHub issue.
239	# For more information about the topic and a list of human-friendly error descripions
240	# go to: https://trello.com/c/29Z90j1W
241	# Error descriptions can also be found at: https://github.com/koalaman/shellcheck/wiki
242	# This SHCK_EXCLUDE list is out "to do" and we work to fix all of this errors.
243	SHCK_EXCLUDE="SC1083,SC2002,SC2010,SC2034,SC2045,SC2046,SC2086,SC2097,SC2098"
244	# SPDK fails some error checks which have been deprecated in later versions of shellcheck.
245	# We will not try to fix these error checks, but instead just leave the error types here
246	# so that we can still run with older versions of shellcheck.
247	SHCK_EXCLUDE="$SHCK_EXCLUDE,SC1117"
248	# SPDK has decided to not fix violations of these errors.
249	# We are aware about below exclude list and we want this errors to be excluded.
250	# SC1090: Can't follow non-constant source. Use a directive to specify location.
251	# SC1091: Not following: (error message here)
252	# SC2001: See if you can use ${variable//search/replace} instead.
253	# SC2016: Expressions don't expand in single quotes, use double quotes for that.
254	# SC2119: Use foo "$@" if function's $1 should mean script's $1.
255	# SC2120: foo references arguments, but none are ever passed.
256	# SC2148: Add shebang to the top of your script.
257	# SC2153: Possible Misspelling: MYVARIABLE may not be assigned, but MY_VARIABLE is.
258	# SC2154: var is referenced but not assigned.
259	# SC2164: Use cd ... || exit in case cd fails.
260	# SC2174: When used with -p, -m only applies to the deepest directory.
261	# SC2206: Quote to prevent word splitting/globbing,
262	#         or split robustly with mapfile or read -a.
263	# SC2207: Prefer mapfile or read -a to split command output (or quote to avoid splitting).
264	# SC2223: This default assignment may cause DoS due to globbing. Quote it.
265	SHCK_EXCLUDE="$SHCK_EXCLUDE,SC1090,SC1091,SC2016,SC2119,SC2120,SC2148,SC2153,SC2154,SC2164,\
266SC2174,SC2001,SC2206,SC2207,SC2223"
267
268	SHCK_FORMAT="diff"
269	SHCK_APPLY=true
270	if [ "$shellcheck_v" \< "0.7.0" ]; then
271		SHCK_FORMAT="tty"
272		SHCK_APPLY=false
273	fi
274	SHCH_ARGS=" -x -e $SHCK_EXCLUDE -f $SHCK_FORMAT"
275
276	error=0
277	git ls-files '*.sh' | xargs -P$(nproc) -n1 shellcheck $SHCH_ARGS &> shellcheck.log || error=1
278	if [ $error -ne 0 ]; then
279		echo " Bash formatting errors detected!"
280
281		# Some errors are not auto-fixable. Fall back to tty output.
282		if grep -q "Use another format to see them." shellcheck.log; then
283			SHCK_FORMAT="tty"
284			SHCK_APPLY=false
285			SHCH_ARGS=" -e $SHCK_EXCLUDE -f $SHCK_FORMAT"
286			git ls-files '*.sh' | xargs -P$(nproc) -n1 shellcheck $SHCH_ARGS > shellcheck.log || error=1
287		fi
288
289		cat shellcheck.log
290		if $SHCK_APPLY; then
291			git apply shellcheck.log
292			echo "Bash errors were automatically corrected."
293			echo "Please remember to add the changes to your commit."
294		fi
295		rc=1
296	else
297		echo " OK"
298	fi
299	rm -f shellcheck.log
300else
301	echo "You do not have shellcheck installed so your Bash style is not being checked!"
302fi
303
304# Check if any of the public interfaces were modified by this patch.
305# Warn the user to consider updating the changelog any changes
306# are detected.
307echo -n "Checking whether CHANGELOG.md should be updated..."
308staged=$(git diff --name-only --cached .)
309working=$(git status -s --porcelain --ignore-submodules | grep -iv "??" | awk '{print $2}')
310files="$staged $working"
311if [[ "$files" = " " ]]; then
312	files=$(git diff-tree --no-commit-id --name-only -r HEAD)
313fi
314
315has_changelog=0
316for f in $files; do
317	if [[ $f == CHANGELOG.md ]]; then
318		# The user has a changelog entry, so exit.
319		has_changelog=1
320		break
321	fi
322done
323
324needs_changelog=0
325if [ $has_changelog -eq 0 ]; then
326	for f in $files; do
327		if [[ $f == include/spdk/* ]] || [[ $f == scripts/rpc.py ]] || [[ $f == etc/* ]]; then
328			echo ""
329			echo -n "$f was modified. Consider updating CHANGELOG.md."
330			needs_changelog=1
331		fi
332	done
333fi
334
335if [ $needs_changelog -eq 0 ]; then
336	echo " OK"
337else
338	echo ""
339fi
340
341exit $rc
342