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 11 function 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" ]; then 69 echo -n " Your astyle version is too old so skipping coding style checks. Please update astyle to at least 3.0.1 version..." 70 else 71 rm -f astyle.log 72 touch astyle.log 73 # Exclude rte_vhost code imported from DPDK - we want to keep the original code 74 # as-is to enable ongoing work to synch with a generic upstream DPDK vhost library, 75 # rather than making diffs more complicated by a lot of changes to follow SPDK 76 # coding standards. 77 git ls-files '*.[ch]' '*.cpp' '*.cc' '*.cxx' '*.hh' '*.hpp' \ 78 | grep -v rte_vhost | grep -v cpp_headers \ 79 | xargs -P$(nproc) -n10 astyle --options=.astylerc >> astyle.log 80 if grep -q "^Formatted" astyle.log; then 81 echo " errors detected" 82 git diff 83 sed -i -e 's/ / /g' astyle.log 84 grep --color=auto "^Formatted.*" astyle.log 85 echo "Incorrect code style detected in one or more files." 86 echo "The files have been automatically formatted." 87 echo "Remember to add the files to your commit." 88 rc=1 89 else 90 echo " OK" 91 fi 92 rm -f astyle.log 93 fi 94else 95 echo "You do not have astyle installed so your code style is not being checked!" 96fi 97 98GIT_VERSION=$(git --version | cut -d' ' -f3) 99 100if version_lt "1.9.5" "${GIT_VERSION}"; then 101 # git <1.9.5 doesn't support pathspec magic exclude 102 echo " Your git version is too old to perform all tests. Please update git to at least 1.9.5 version..." 103 exit 0 104fi 105 106echo -n "Checking comment style..." 107 108git grep --line-number -e '/[*][^ *-]' -- '*.[ch]' > comment.log || true 109git grep --line-number -e '[^ ][*]/' -- '*.[ch]' ':!lib/rte_vhost*/*' >> comment.log || true 110git grep --line-number -e '^[*]' -- '*.[ch]' >> comment.log || true 111git grep --line-number -e '\s//' -- '*.[ch]' >> comment.log || true 112git grep --line-number -e '^//' -- '*.[ch]' >> comment.log || true 113 114if [ -s comment.log ]; then 115 echo " Incorrect comment formatting detected" 116 cat comment.log 117 rc=1 118else 119 echo " OK" 120fi 121rm -f comment.log 122 123echo -n "Checking for spaces before tabs..." 124git grep --line-number $' \t' -- './*' ':!*.patch' > whitespace.log || true 125if [ -s whitespace.log ]; then 126 echo " Spaces before tabs detected" 127 cat whitespace.log 128 rc=1 129else 130 echo " OK" 131fi 132rm -f whitespace.log 133 134echo -n "Checking trailing whitespace in output strings..." 135 136git grep --line-number -e ' \\n"' -- '*.[ch]' > whitespace.log || true 137 138if [ -s whitespace.log ]; then 139 echo " Incorrect trailing whitespace detected" 140 cat whitespace.log 141 rc=1 142else 143 echo " OK" 144fi 145rm -f whitespace.log 146 147echo -n "Checking for use of forbidden library functions..." 148 149git grep --line-number -w '\(atoi\|atol\|atoll\|strncpy\|strcpy\|strcat\|sprintf\|vsprintf\)' -- './*.c' ':!lib/rte_vhost*/**' > badfunc.log || true 150if [ -s badfunc.log ]; then 151 echo " Forbidden library functions detected" 152 cat badfunc.log 153 rc=1 154else 155 echo " OK" 156fi 157rm -f badfunc.log 158 159echo -n "Checking for use of forbidden CUnit macros..." 160 161git grep --line-number -w 'CU_ASSERT_FATAL' -- 'test/*' ':!test/spdk_cunit.h' > badcunit.log || true 162if [ -s badcunit.log ]; then 163 echo " Forbidden CU_ASSERT_FATAL usage detected - use SPDK_CU_ASSERT_FATAL instead" 164 cat badcunit.log 165 rc=1 166else 167 echo " OK" 168fi 169rm -f badcunit.log 170 171echo -n "Checking blank lines at end of file..." 172 173if ! git grep -I -l -e . -z './*' ':!*.patch' \ 174 | xargs -0 -P$(nproc) -n1 scripts/eofnl > eofnl.log; then 175 echo " Incorrect end-of-file formatting detected" 176 cat eofnl.log 177 rc=1 178else 179 echo " OK" 180fi 181rm -f eofnl.log 182 183echo -n "Checking for POSIX includes..." 184git grep -I -i -f scripts/posix.txt -- './*' ':!include/spdk/stdinc.h' ':!include/linux/**' ':!lib/rte_vhost*/**' ':!scripts/posix.txt' ':!*.patch' > scripts/posix.log || true 185if [ -s scripts/posix.log ]; then 186 echo "POSIX includes detected. Please include spdk/stdinc.h instead." 187 cat scripts/posix.log 188 rc=1 189else 190 echo " OK" 191fi 192rm -f scripts/posix.log 193 194echo -n "Checking #include style..." 195git grep -I -i --line-number "#include <spdk/" -- '*.[ch]' > scripts/includes.log || true 196if [ -s scripts/includes.log ]; then 197 echo "Incorrect #include syntax. #includes of spdk/ files should use quotes." 198 cat scripts/includes.log 199 rc=1 200else 201 echo " OK" 202fi 203rm -f scripts/includes.log 204 205if hash pycodestyle 2> /dev/null; then 206 PEP8=pycodestyle 207elif hash pep8 2> /dev/null; then 208 PEP8=pep8 209fi 210 211if [ -n "${PEP8}" ]; then 212 echo -n "Checking Python style..." 213 214 PEP8_ARGS+=" --max-line-length=140" 215 216 error=0 217 git ls-files '*.py' | xargs -P$(nproc) -n1 $PEP8 $PEP8_ARGS > pep8.log || error=1 218 if [ $error -ne 0 ]; then 219 echo " Python formatting errors detected" 220 cat pep8.log 221 rc=1 222 else 223 echo " OK" 224 fi 225 rm -f pep8.log 226else 227 echo "You do not have pycodestyle or pep8 installed so your Python style is not being checked!" 228fi 229 230shfmt="shfmt-3.1.0" 231if hash "$shfmt" 2> /dev/null; then 232 shfmt_cmdline=() silly_plural=() 233 234 silly_plural[1]="s" 235 236 commits=() sh_files=() sh_files_repo=() sh_files_staged=() 237 238 mapfile -t sh_files_repo < <(git ls-files '*.sh') 239 # Fetch .sh files only from the commits that are targeted for merge 240 while read -r _ commit; do 241 commits+=("$commit") 242 done < <(git cherry -v origin/master) 243 244 mapfile -t sh_files < <(git diff --name-only HEAD origin/master "${sh_files_repo[@]}") 245 # In case of a call from a pre-commit git hook 246 mapfile -t sh_files_staged < <( 247 IFS="|" 248 git diff --cached --name-only "${sh_files_repo[@]}" | grep -v "${sh_files[*]}" 249 ) 250 251 if ((${#sh_files[@]})); then 252 printf 'Checking .sh formatting style...\n\n' 253 printf ' Found %u updated|new .sh file%s from the following commits:\n' \ 254 "${#sh_files[@]}" "${silly_plural[${#sh_files[@]} > 1 ? 1 : 0]}" 255 printf ' * %s\n' "${commits[@]}" 256 257 if ((${#sh_files_staged[@]})); then 258 printf ' Found %u .sh file%s in staging area:\n' \ 259 "${#sh_files_staged[@]}" "${silly_plural[${#sh_files_staged[@]} > 1 ? 1 : 0]}" 260 printf ' * %s\n' "${sh_files_staged[@]}" 261 sh_files+=("${sh_files_staged[@]}") 262 fi 263 264 printf ' Running %s against the following file%s:\n' "$shfmt" "${silly_plural[${#sh_files[@]} > 1 ? 1 : 0]}" 265 printf ' * %s\n' "${sh_files[@]}" 266 267 shfmt_cmdline+=(-i 0) # indent_style = tab|indent_size = 0 268 shfmt_cmdline+=(-bn) # binary_next_line = true 269 shfmt_cmdline+=(-ci) # switch_case_indent = true 270 shfmt_cmdline+=(-ln bash) # shell_variant = bash (default) 271 shfmt_cmdline+=(-d) # diffOut - print diff of the changes and exit with != 0 272 shfmt_cmdline+=(-sr) # redirect operators will be followed by a space 273 274 diff=$PWD/$shfmt.patch 275 276 # Explicitly tell shfmt to not look for .editorconfig. .editorconfig is also not looked up 277 # in case any formatting arguments has been passed on its cmdline. 278 if ! SHFMT_NO_EDITORCONFIG=true "$shfmt" "${shfmt_cmdline[@]}" "${sh_files[@]}" > "$diff"; then 279 # In case shfmt detects an actual syntax error it will write out a proper message on 280 # its stderr, hence the diff file should remain empty. 281 if [[ -s $diff ]]; then 282 diff_out=$(< "$diff") 283 fi 284 285 cat <<- ERROR_SHFMT 286 287 * Errors in style formatting have been detected. 288 ${diff_out:+* Please, review the generated patch at $diff 289 290 # _START_OF_THE_DIFF 291 292 ${diff_out:-ERROR} 293 294 # _END_OF_THE_DIFF 295 } 296 297 ERROR_SHFMT 298 rc=1 299 else 300 rm -f "$diff" 301 printf 'OK\n' 302 fi 303 fi 304else 305 printf '%s not detected, Bash style formatting check is skipped\n' "$shfmt" 306fi 307 308if hash shellcheck 2> /dev/null; then 309 echo -n "Checking Bash style..." 310 311 shellcheck_v=$(shellcheck --version | grep -P "version: [0-9\.]+" | cut -d " " -f2) 312 313 # SHCK_EXCLUDE contains a list of all of the spellcheck errors found in SPDK scripts 314 # currently. New errors should only be added to this list if the cost of fixing them 315 # is deemed too high. For more information about the errors, go to: 316 # https://github.com/koalaman/shellcheck/wiki/Checks 317 # Error descriptions can also be found at: https://github.com/koalaman/shellcheck/wiki 318 # SPDK fails some error checks which have been deprecated in later versions of shellcheck. 319 # We will not try to fix these error checks, but instead just leave the error types here 320 # so that we can still run with older versions of shellcheck. 321 SHCK_EXCLUDE="SC1117" 322 # SPDK has decided to not fix violations of these errors. 323 # We are aware about below exclude list and we want this errors to be excluded. 324 # SC1083: This {/} is literal. Check expression (missing ;/\n?) or quote it. 325 # SC1090: Can't follow non-constant source. Use a directive to specify location. 326 # SC1091: Not following: (error message here) 327 # SC2001: See if you can use ${variable//search/replace} instead. 328 # SC2010: Don't use ls | grep. Use a glob or a for loop with a condition to allow non-alphanumeric filenames. 329 # SC2015: Note that A && B || C is not if-then-else. C may run when A is true. 330 # SC2016: Expressions don't expand in single quotes, use double quotes for that. 331 # SC2034: foo appears unused. Verify it or export it. 332 # SC2046: Quote this to prevent word splitting. 333 # SC2086: Double quote to prevent globbing and word splitting. 334 # SC2119: Use foo "$@" if function's $1 should mean script's $1. 335 # SC2120: foo references arguments, but none are ever passed. 336 # SC2148: Add shebang to the top of your script. 337 # SC2153: Possible Misspelling: MYVARIABLE may not be assigned, but MY_VARIABLE is. 338 # SC2154: var is referenced but not assigned. 339 # SC2164: Use cd ... || exit in case cd fails. 340 # SC2174: When used with -p, -m only applies to the deepest directory. 341 # SC2206: Quote to prevent word splitting/globbing, 342 # or split robustly with mapfile or read -a. 343 # SC2207: Prefer mapfile or read -a to split command output (or quote to avoid splitting). 344 # SC2223: This default assignment may cause DoS due to globbing. Quote it. 345 SHCK_EXCLUDE="$SHCK_EXCLUDE,SC1083,SC1090,SC1091,SC2010,SC2015,SC2016,SC2034,SC2046,SC2086,\ 346SC2119,SC2120,SC2148,SC2153,SC2154,SC2164,SC2174,SC2001,SC2206,SC2207,SC2223" 347 348 SHCK_FORMAT="diff" 349 SHCK_APPLY=true 350 if [ "$shellcheck_v" \< "0.7.0" ]; then 351 SHCK_FORMAT="tty" 352 SHCK_APPLY=false 353 fi 354 SHCH_ARGS=" -x -e $SHCK_EXCLUDE -f $SHCK_FORMAT" 355 356 error=0 357 git ls-files '*.sh' | xargs -P$(nproc) -n1 shellcheck $SHCH_ARGS &> shellcheck.log || error=1 358 if [ $error -ne 0 ]; then 359 echo " Bash formatting errors detected!" 360 361 # Some errors are not auto-fixable. Fall back to tty output. 362 if grep -q "Use another format to see them." shellcheck.log; then 363 SHCK_FORMAT="tty" 364 SHCK_APPLY=false 365 SHCH_ARGS=" -e $SHCK_EXCLUDE -f $SHCK_FORMAT" 366 git ls-files '*.sh' | xargs -P$(nproc) -n1 shellcheck $SHCH_ARGS > shellcheck.log || error=1 367 fi 368 369 cat shellcheck.log 370 if $SHCK_APPLY; then 371 git apply shellcheck.log 372 echo "Bash errors were automatically corrected." 373 echo "Please remember to add the changes to your commit." 374 fi 375 rc=1 376 else 377 echo " OK" 378 fi 379 rm -f shellcheck.log 380else 381 echo "You do not have shellcheck installed so your Bash style is not being checked!" 382fi 383 384# Check if any of the public interfaces were modified by this patch. 385# Warn the user to consider updating the changelog any changes 386# are detected. 387echo -n "Checking whether CHANGELOG.md should be updated..." 388staged=$(git diff --name-only --cached .) 389working=$(git status -s --porcelain --ignore-submodules | grep -iv "??" | awk '{print $2}') 390files="$staged $working" 391if [[ "$files" = " " ]]; then 392 files=$(git diff-tree --no-commit-id --name-only -r HEAD) 393fi 394 395has_changelog=0 396for f in $files; do 397 if [[ $f == CHANGELOG.md ]]; then 398 # The user has a changelog entry, so exit. 399 has_changelog=1 400 break 401 fi 402done 403 404needs_changelog=0 405if [ $has_changelog -eq 0 ]; then 406 for f in $files; do 407 if [[ $f == include/spdk/* ]] || [[ $f == scripts/rpc.py ]] || [[ $f == etc/* ]]; then 408 echo "" 409 echo -n "$f was modified. Consider updating CHANGELOG.md." 410 needs_changelog=1 411 fi 412 done 413fi 414 415if [ $needs_changelog -eq 0 ]; then 416 echo " OK" 417else 418 echo "" 419fi 420 421exit $rc 422