xref: /spdk/scripts/check_format.sh (revision bad2c8e86cc9de65297431f7b6aa715850e6c67a)
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	# SHCK_EXCLUDE contains a list of all of the spellcheck errors found in SPDK scripts
237	# currently. New errors should only be added to this list if the cost of fixing them
238	# is deemed too high. For more information about the errors, go to:
239	# https://github.com/koalaman/shellcheck/wiki/Checks
240	# Error descriptions can also be found at: https://github.com/koalaman/shellcheck/wiki
241	# SPDK fails some error checks which have been deprecated in later versions of shellcheck.
242	# We will not try to fix these error checks, but instead just leave the error types here
243	# so that we can still run with older versions of shellcheck.
244	SHCK_EXCLUDE="SC1117"
245	# SPDK has decided to not fix violations of these errors.
246	# We are aware about below exclude list and we want this errors to be excluded.
247	# SC1083: This {/} is literal. Check expression (missing ;/\n?) or quote it.
248	# SC1090: Can't follow non-constant source. Use a directive to specify location.
249	# SC1091: Not following: (error message here)
250	# SC2001: See if you can use ${variable//search/replace} instead.
251	# SC2010: Don't use ls | grep. Use a glob or a for loop with a condition to allow non-alphanumeric filenames.
252	# SC2015: Note that A && B || C is not if-then-else. C may run when A is true.
253	# SC2016: Expressions don't expand in single quotes, use double quotes for that.
254	# SC2034: foo appears unused. Verify it or export it.
255	# SC2046: Quote this to prevent word splitting.
256	# SC2086: Double quote to prevent globbing and word splitting.
257	# SC2119: Use foo "$@" if function's $1 should mean script's $1.
258	# SC2120: foo references arguments, but none are ever passed.
259	# SC2148: Add shebang to the top of your script.
260	# SC2153: Possible Misspelling: MYVARIABLE may not be assigned, but MY_VARIABLE is.
261	# SC2154: var is referenced but not assigned.
262	# SC2164: Use cd ... || exit in case cd fails.
263	# SC2174: When used with -p, -m only applies to the deepest directory.
264	# SC2206: Quote to prevent word splitting/globbing,
265	#         or split robustly with mapfile or read -a.
266	# SC2207: Prefer mapfile or read -a to split command output (or quote to avoid splitting).
267	# SC2223: This default assignment may cause DoS due to globbing. Quote it.
268	SHCK_EXCLUDE="$SHCK_EXCLUDE,SC1083,SC1090,SC1091,SC2010,SC2015,SC2016,SC2034,SC2046,SC2086,\
269SC2119,SC2120,SC2148,SC2153,SC2154,SC2164,SC2174,SC2001,SC2206,SC2207,SC2223"
270
271	SHCK_FORMAT="diff"
272	SHCK_APPLY=true
273	if [ "$shellcheck_v" \< "0.7.0" ]; then
274		SHCK_FORMAT="tty"
275		SHCK_APPLY=false
276	fi
277	SHCH_ARGS=" -x -e $SHCK_EXCLUDE -f $SHCK_FORMAT"
278
279	error=0
280	git ls-files '*.sh' | xargs -P$(nproc) -n1 shellcheck $SHCH_ARGS &> shellcheck.log || error=1
281	if [ $error -ne 0 ]; then
282		echo " Bash formatting errors detected!"
283
284		# Some errors are not auto-fixable. Fall back to tty output.
285		if grep -q "Use another format to see them." shellcheck.log; then
286			SHCK_FORMAT="tty"
287			SHCK_APPLY=false
288			SHCH_ARGS=" -e $SHCK_EXCLUDE -f $SHCK_FORMAT"
289			git ls-files '*.sh' | xargs -P$(nproc) -n1 shellcheck $SHCH_ARGS > shellcheck.log || error=1
290		fi
291
292		cat shellcheck.log
293		if $SHCK_APPLY; then
294			git apply shellcheck.log
295			echo "Bash errors were automatically corrected."
296			echo "Please remember to add the changes to your commit."
297		fi
298		rc=1
299	else
300		echo " OK"
301	fi
302	rm -f shellcheck.log
303else
304	echo "You do not have shellcheck installed so your Bash style is not being checked!"
305fi
306
307# Check if any of the public interfaces were modified by this patch.
308# Warn the user to consider updating the changelog any changes
309# are detected.
310echo -n "Checking whether CHANGELOG.md should be updated..."
311staged=$(git diff --name-only --cached .)
312working=$(git status -s --porcelain --ignore-submodules | grep -iv "??" | awk '{print $2}')
313files="$staged $working"
314if [[ "$files" = " " ]]; then
315	files=$(git diff-tree --no-commit-id --name-only -r HEAD)
316fi
317
318has_changelog=0
319for f in $files; do
320	if [[ $f == CHANGELOG.md ]]; then
321		# The user has a changelog entry, so exit.
322		has_changelog=1
323		break
324	fi
325done
326
327needs_changelog=0
328if [ $has_changelog -eq 0 ]; then
329	for f in $files; do
330		if [[ $f == include/spdk/* ]] || [[ $f == scripts/rpc.py ]] || [[ $f == etc/* ]]; then
331			echo ""
332			echo -n "$f was modified. Consider updating CHANGELOG.md."
333			needs_changelog=1
334		fi
335	done
336fi
337
338if [ $needs_changelog -eq 0 ]; then
339	echo " OK"
340else
341	echo ""
342fi
343
344exit $rc
345