xref: /spdk/scripts/check_format.sh (revision 913f780e1077efada6674c7a14b687ba5ac1b6d0)
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,SC2004,\
244SC2010,SC2012,SC2016,SC2034,SC2045,SC2046,SC2068,SC2086,SC2089,SC2090,\
245SC2097,SC2098,SC2119,SC2120,SC2121,SC2124,SC2126,SC2128,\
246SC2129,SC2140,SC2142,SC2143,SC2148,SC2152,SC2153,SC2154,SC2155,\
247SC2162"
248	# SPDK fails some error checks which have been deprecated in later versions of shellcheck.
249	# We will not try to fix these error checks, but instead just leave the error types here
250	# so that we can still run with older versions of shellcheck.
251	SHCK_EXCLUDE="$SHCK_EXCLUDE,SC1117"
252	# SPDK has decided to not fix violations of these errors.
253	# We are aware about below exclude list and we want this errors to be excluded.
254	# SC1090: Can't follow non-constant source. Use a directive to specify location.
255	# SC1091: Not following: (error message here)
256	# SC2001: See if you can use ${variable//search/replace} instead.
257	# SC2164: Use cd ... || exit in case cd fails.
258	# SC2174: When used with -p, -m only applies to the deepest directory.
259	# SC2206: Quote to prevent word splitting/globbing,
260	#         or split robustly with mapfile or read -a.
261	# SC2207: Prefer mapfile or read -a to split command output (or quote to avoid splitting).
262	# SC2223: This default assignment may cause DoS due to globbing. Quote it.
263	SHCK_EXCLUDE="$SHCK_EXCLUDE,SC1090,SC1091,SC2164,SC2174,SC2001,SC2206,SC2207,SC2223"
264
265	SHCK_FORMAT="diff"
266	SHCK_APPLY=true
267	if [ "$shellcheck_v" \< "0.7.0" ]; then
268		SHCK_FORMAT="tty"
269		SHCK_APPLY=false
270	fi
271	SHCH_ARGS=" -e $SHCK_EXCLUDE -f $SHCK_FORMAT"
272
273	error=0
274	git ls-files '*.sh' | xargs -P$(nproc) -n1 shellcheck $SHCH_ARGS &> shellcheck.log || error=1
275	if [ $error -ne 0 ]; then
276		echo " Bash formatting errors detected!"
277
278		# Some errors are not auto-fixable. Fall back to tty output.
279		if grep -q "Use another format to see them." shellcheck.log; then
280			SHCK_FORMAT="tty"
281			SHCK_APPLY=false
282			SHCH_ARGS=" -e $SHCK_EXCLUDE -f $SHCK_FORMAT"
283			git ls-files '*.sh' | xargs -P$(nproc) -n1 shellcheck $SHCH_ARGS > shellcheck.log || error=1
284		fi
285
286		cat shellcheck.log
287		if $SHCK_APPLY; then
288			git apply shellcheck.log
289			echo "Bash errors were automatically corrected."
290			echo "Please remember to add the changes to your commit."
291		fi
292		rc=1
293	else
294		echo " OK"
295	fi
296	rm -f shellcheck.log
297else
298	echo "You do not have shellcheck installed so your Bash style is not being checked!"
299fi
300
301# Check if any of the public interfaces were modified by this patch.
302# Warn the user to consider updating the changelog any changes
303# are detected.
304echo -n "Checking whether CHANGELOG.md should be updated..."
305staged=$(git diff --name-only --cached .)
306working=$(git status -s --porcelain --ignore-submodules | grep -iv "??" | awk '{print $2}')
307files="$staged $working"
308if [[ "$files" = " " ]]; then
309	files=$(git diff-tree --no-commit-id --name-only -r HEAD)
310fi
311
312has_changelog=0
313for f in $files; do
314	if [[ $f == CHANGELOG.md ]]; then
315		# The user has a changelog entry, so exit.
316		has_changelog=1
317		break
318	fi
319done
320
321needs_changelog=0
322if [ $has_changelog -eq 0 ]; then
323	for f in $files; do
324		if [[ $f == include/spdk/* ]] || [[ $f == scripts/rpc.py ]] || [[ $f == etc/* ]]; then
325			echo ""
326			echo -n "$f was modified. Consider updating CHANGELOG.md."
327			needs_changelog=1
328		fi
329	done
330fi
331
332if [ $needs_changelog -eq 0 ]; then
333	echo " OK"
334else
335	echo ""
336fi
337
338exit $rc
339