1#!/usr/bin/env bash 2# SPDX-License-Identifier: BSD-3-Clause 3# Copyright (C) 2015 Intel Corporation 4# All rights reserved. 5# 6if [[ $(uname -s) == Darwin ]]; then 7 # SPDK is not supported on MacOS, but as a developer 8 # convenience we support running the check_format.sh 9 # script on MacOS. 10 # Running "brew install bash coreutils grep" should be 11 # sufficient to get the correct versions of these utilities. 12 if [[ $(type -t mapfile) != builtin ]]; then 13 # We need bash version >= 4.0 for mapfile builtin 14 echo "Please install bash version >= 4.0" 15 exit 1 16 fi 17 if ! hash greadlink 2> /dev/null; then 18 # We need GNU readlink for -f option 19 echo "Please install GNU readlink" 20 exit 1 21 fi 22 if ! hash ggrep 2> /dev/null; then 23 # We need GNU grep for -P option 24 echo "Please install GNU grep" 25 exit 1 26 fi 27 GNU_READLINK="greadlink" 28 GNU_GREP="ggrep" 29else 30 GNU_READLINK="readlink" 31 GNU_GREP="grep" 32fi 33 34rootdir=$($GNU_READLINK -f "$(dirname "$0")")/.. 35source "$rootdir/scripts/common.sh" 36 37cd "$rootdir" 38 39# exit on errors 40set -e 41 42if ! hash nproc 2> /dev/null; then 43 44 function nproc() { 45 echo 8 46 } 47 48fi 49 50function version_lt() { 51 [ $(echo -e "$1\n$2" | sort -V | head -1) != "$1" ] 52} 53 54function array_contains_string() { 55 name="$1[@]" 56 array=("${!name}") 57 58 for element in "${array[@]}"; do 59 if [ "$element" = "$2" ]; then 60 return $(true) 61 fi 62 done 63 64 return $(false) 65} 66 67rc=0 68 69function check_permissions() { 70 local rc=0 files=() 71 72 mapfile -t files < <(git ls-files) 73 mapfile -t files < <(get_diffed_dups "${files[@]}") 74 75 ((${#files[@]} > 0)) || return 0 76 77 echo -n "Checking file permissions..." 78 79 while read -r perm _res0 _res1 path; do 80 if [ ! -f "$path" ]; then 81 continue 82 fi 83 84 # Skip symlinks 85 if [[ -L $path ]]; then 86 continue 87 fi 88 fname=$(basename -- "$path") 89 90 case ${fname##*.} in 91 c | h | cpp | cc | cxx | hh | hpp | md | html | js | json | svg | Doxyfile | yml | LICENSE | README | conf | in | Makefile | mk | gitignore | go | txt) 92 # These file types should never be executable 93 if [ "$perm" -eq 100755 ]; then 94 echo "ERROR: $path is marked executable but is a code file." 95 rc=1 96 fi 97 ;; 98 *) 99 shebang=$(head -n 1 $path | cut -c1-3) 100 101 # git only tracks the execute bit, so will only ever return 755 or 644 as the permission. 102 if [ "$perm" -eq 100755 ]; then 103 # If the file has execute permission, it should start with a shebang. 104 if [ "$shebang" != "#!/" ]; then 105 echo "ERROR: $path is marked executable but does not start with a shebang." 106 rc=1 107 fi 108 else 109 # If the file does not have execute permissions, it should not start with a shebang. 110 if [ "$shebang" = "#!/" ]; then 111 echo "ERROR: $path is not marked executable but starts with a shebang." 112 rc=1 113 fi 114 fi 115 ;; 116 esac 117 118 done < <(git ls-files -s "${files[@]}") 119 120 if [ $rc -eq 0 ]; then 121 echo " OK" 122 fi 123 124 return $rc 125} 126 127function check_c_style() { 128 local rc=0 129 130 if hash astyle; then 131 echo -n "Checking coding style..." 132 version=$(astyle --version | awk '{print $NF}') 133 if lt "$version" 3.0.1; then 134 echo " Your astyle version is too old so skipping coding style checks. Please update astyle to at least 3.0.1 version..." 135 rc=1 136 elif ge "$version" 3.4; then 137 echo " Your astyle version is too new so skipping coding style checks. Please use astyle version < 3.4" 138 rc=1 139 else 140 rm -f astyle.log 141 touch astyle.log 142 # Exclude DPDK header files copied into our tree 143 git ls-files '*.[ch]' ':!:*/env_dpdk/*/*.h' \ 144 | xargs -P$(nproc) -n10 astyle --break-return-type --attach-return-type-decl \ 145 --options=.astylerc >> astyle.log 146 git ls-files '*.cpp' '*.cc' '*.cxx' '*.hh' '*.hpp' \ 147 | xargs -P$(nproc) -n10 astyle --options=.astylerc >> astyle.log 148 if grep -q "^Formatted" astyle.log; then 149 echo " errors detected" 150 git diff --ignore-submodules=all 151 sed -i -e 's/ / /g' astyle.log 152 grep --color=auto "^Formatted.*" astyle.log 153 echo "Incorrect code style detected in one or more files." 154 echo "The files have been automatically formatted." 155 echo "Remember to add the files to your commit." 156 rc=1 157 else 158 echo " OK" 159 fi 160 rm -f astyle.log 161 fi 162 else 163 echo "You do not have astyle installed so your code style is not being checked!" 164 fi 165 return $rc 166} 167 168function check_comment_style() { 169 local rc=0 170 171 echo -n "Checking comment style..." 172 173 git grep --line-number -e '\/[*][^ *-]' -- '*.[ch]' > comment.log || true 174 git grep --line-number -e '[^ ][*]\/' -- '*.[ch]' ':!lib/rte_vhost*/*' >> comment.log || true 175 git grep --line-number -e '^[*]' -- '*.[ch]' >> comment.log || true 176 git grep --line-number -e '\s\/\/' -- '*.[ch]' >> comment.log || true 177 git grep --line-number -e '^\/\/' -- '*.[ch]' >> comment.log || true 178 179 if [ -s comment.log ]; then 180 echo " Incorrect comment formatting detected" 181 cat comment.log 182 rc=1 183 else 184 echo " OK" 185 fi 186 rm -f comment.log 187 188 return $rc 189} 190 191function check_spaces_before_tabs() { 192 local rc=0 193 194 echo -n "Checking for spaces before tabs..." 195 git grep --line-number $' \t' -- './*' ':!*.patch' > whitespace.log || true 196 if [ -s whitespace.log ]; then 197 echo " Spaces before tabs detected" 198 cat whitespace.log 199 rc=1 200 else 201 echo " OK" 202 fi 203 rm -f whitespace.log 204 205 return $rc 206} 207 208function check_trailing_whitespace() { 209 local rc=0 210 211 echo -n "Checking trailing whitespace in output strings..." 212 213 git grep --line-number -e ' \\n"' -- '*.[ch]' > whitespace.log || true 214 215 if [ -s whitespace.log ]; then 216 echo " Incorrect trailing whitespace detected" 217 cat whitespace.log 218 rc=1 219 else 220 echo " OK" 221 fi 222 rm -f whitespace.log 223 224 return $rc 225} 226 227function check_forbidden_functions() { 228 local rc=0 229 230 echo -n "Checking for use of forbidden library functions..." 231 232 git grep --line-number -w '\(atoi\|atol\|atoll\|strncpy\|strcpy\|strcat\|sprintf\|vsprintf\)' -- './*.c' ':!lib/rte_vhost*/**' > badfunc.log || true 233 if [ -s badfunc.log ]; then 234 echo " Forbidden library functions detected" 235 cat badfunc.log 236 rc=1 237 else 238 echo " OK" 239 fi 240 rm -f badfunc.log 241 242 return $rc 243} 244 245function check_cunit_style() { 246 local rc=0 247 248 echo -n "Checking for use of forbidden CUnit macros..." 249 250 git grep --line-number -w 'CU_ASSERT_FATAL' -- 'test/*' ':!include/spdk_internal/cunit.h' > badcunit.log || true 251 if [ -s badcunit.log ]; then 252 echo " Forbidden CU_ASSERT_FATAL usage detected - use SPDK_CU_ASSERT_FATAL instead" 253 cat badcunit.log 254 rc=1 255 else 256 echo " OK" 257 fi 258 rm -f badcunit.log 259 260 return $rc 261} 262 263function check_eof() { 264 local rc=0 265 266 echo -n "Checking blank lines at end of file..." 267 268 if ! git grep -I -l -e . -z './*' ':!*.patch' \ 269 | xargs -0 -P$(nproc) -n1 scripts/eofnl > eofnl.log; then 270 echo " Incorrect end-of-file formatting detected" 271 cat eofnl.log 272 rc=1 273 else 274 echo " OK" 275 fi 276 rm -f eofnl.log 277 278 return $rc 279} 280 281function check_posix_includes() { 282 local rc=0 283 284 echo -n "Checking for POSIX includes..." 285 git grep -I -i -f scripts/posix.txt -- './*' ':!include/spdk/stdinc.h' \ 286 ':!include/linux/**' ':!scripts/posix.txt' ':!lib/env_dpdk/*/*.h' \ 287 ':!*.patch' ':!configure' > scripts/posix.log || true 288 if [ -s scripts/posix.log ]; then 289 echo "POSIX includes detected. Please include spdk/stdinc.h instead." 290 cat scripts/posix.log 291 rc=1 292 else 293 echo " OK" 294 fi 295 rm -f scripts/posix.log 296 297 return $rc 298} 299 300check_function_conventions() { 301 local rc=0 302 303 echo -n "Checking for proper function naming conventions..." 304 # commit_to_compare = HEAD - 1. 305 commit_to_compare="$(git log --pretty=oneline --skip=1 -n 1 | awk '{print $1}')" 306 failed_naming_conventions=false 307 changed_c_libs=() 308 declared_symbols=() 309 310 # Build an array of all the modified C libraries. 311 mapfile -t changed_c_libs < <(git diff --name-only HEAD $commit_to_compare -- lib/**/*.c module/**/*.c | xargs -r dirname | sort | uniq) 312 # Capture the names of all function declarations 313 mapfile -t declared_symbols < <(grep -Eh 'spdk_[a-zA-Z0-9_]*\(' include/spdk*/*.h \ 314 | sed -En 's/.*(spdk[a-z,A-Z,0-9,_]*)\(.*/\1/p') 315 316 for c_lib in "${changed_c_libs[@]}"; do 317 lib_map_file="mk/spdk_blank.map" 318 defined_symbols=() 319 removed_symbols=() 320 exported_symbols=() 321 if ls "$c_lib"/*.map &> /dev/null; then 322 lib_map_file="$(ls "$c_lib"/*.map)" 323 fi 324 # Matching groups are 1. leading +sign. 2, function name 3. argument list / anything after that. 325 # Capture just the names of newly added (or modified) functions that start with "spdk_" 326 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') 327 # Capture the names of removed symbols to catch edge cases where we just move definitions around. 328 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') 329 for symbol in "${removed_symbols[@]}"; do 330 for i in "${!defined_symbols[@]}"; do 331 if [[ ${defined_symbols[i]} = "$symbol" ]]; then 332 unset -v 'defined_symbols[i]' 333 fi 334 done 335 done 336 # It's possible that we just modified a functions arguments so unfortunately we can't just look at changed lines in this function. 337 # matching groups are 1. All leading whitespace 2. function name. Capture just the symbol name. 338 mapfile -t exported_symbols < <(sed -En 's/(^[[:space:]]*)(spdk[a-z,A-Z,0-9,_]*);/\2/p' < $lib_map_file) 339 for defined_symbol in "${defined_symbols[@]}"; do 340 # 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. 341 if [ "$defined_symbol" = '' ]; then 342 continue 343 fi 344 not_exported=true 345 not_declared=true 346 if array_contains_string exported_symbols $defined_symbol; then 347 not_exported=false 348 fi 349 350 if array_contains_string declared_symbols $defined_symbol; then 351 not_declared=false 352 fi 353 354 if $not_exported || $not_declared; then 355 if ! $failed_naming_conventions; then 356 echo " found naming convention errors." 357 fi 358 echo "function $defined_symbol starts with spdk_ which is reserved for public API functions." 359 echo "Please add this function to its corresponding map file and a public header or remove the spdk_ prefix." 360 failed_naming_conventions=true 361 rc=1 362 fi 363 done 364 done 365 366 if ! $failed_naming_conventions; then 367 echo " OK" 368 fi 369 370 return $rc 371} 372 373check_conventions_generic() { 374 local out decltype=$1 excludes=(${@:2}) 375 376 # We only care about the types defined at the beginning of a line to allow stuff like nested 377 # structs. Also, we need to drop any __attribute__ declarations. 378 out=$(git grep -E "^$decltype\s+\w+.*\{$" -- "include/spdk" "${excludes[@]}" \ 379 | sed 's/__attribute__\s*(([[:alnum:]_, ()]*))\s*//g' \ 380 | awk "!/$decltype\s+spdk_/ { \$(NF--)=\"\"; print }") 381 382 if [[ -n "$out" ]]; then 383 cat <<- WARN 384 Found $decltype declarations without the spdk_ prefix: 385 386 $out 387 WARN 388 return 1 389 fi 390} 391 392check_naming_conventions() { 393 check_function_conventions 394 # There are still some enums without the spdk_ prefix. Once they're renamed, we can remove 395 # these excludes 396 check_conventions_generic 'enum' \ 397 ':!include/spdk/blob.h' \ 398 ':!include/spdk/ftl.h' \ 399 ':!include/spdk/idxd_spec.h' \ 400 ':!include/spdk/iscsi_spec.h' \ 401 ':!include/spdk/lvol.h' \ 402 ':!include/spdk/nvmf_fc_spec.h' \ 403 ':!include/spdk/vfio_user_spec.h' 404 # Same with structs 405 check_conventions_generic 'struct' \ 406 ':!include/spdk/ftl.h' \ 407 ':!include/spdk/idxd_spec.h' \ 408 ':!include/spdk/iscsi_spec.h' \ 409 ':!include/spdk/vfio_user_spec.h' 410} 411 412function check_include_style() { 413 local rc=0 414 415 echo -n "Checking #include style..." 416 git grep -I -i --line-number "#include <spdk/" -- '*.[ch]' > scripts/includes.log || true 417 if [ -s scripts/includes.log ]; then 418 echo "Incorrect #include syntax. #includes of spdk/ files should use quotes." 419 cat scripts/includes.log 420 rc=1 421 else 422 echo " OK" 423 fi 424 rm -f scripts/includes.log 425 426 return $rc 427} 428 429function check_opts_structs() { 430 local IFS="|" out types=( 431 spdk_nvme_ns_cmd_ext_io_opts 432 spdk_dif_ctx_init_ext_opts 433 ) 434 435 if out=$(git grep -InE "(\.|->)size[[:space:]]*=[[:space:]]*sizeof\(struct (${types[*]})\)"); then 436 cat <<- WARN 437 Found incorrect *ext_opts struct usage. Use SPDK_SIZEOF() to calculate its size. 438 439 $out 440 WARN 441 return 1 442 fi 443} 444 445function check_attr_packed() { 446 local out 447 448 # For now, we only care about the packed attribute in selected files. We only check those 449 # used by Timberland (see https://github.com/timberland-sig), as they're using msvc, which 450 # doesn't support the __attribute__ keyword. 451 if out=$(git grep -In '__attribute__((packed))' \ 452 'include/spdk/nvme*.h' \ 453 'include/spdk/sock.h' \ 454 'include/spdk_internal/nvme*.h' \ 455 'lib/nvme' 'lib/sock'); then 456 cat <<- WARN 457 Found forbidden __attribute__((packed)). Try to pack the structures manually or 458 use #pragma pack instead. 459 460 $out 461 WARN 462 return 1 463 fi 464} 465 466function check_python_style() { 467 local rc=0 468 469 if hash pycodestyle 2> /dev/null; then 470 PEP8=pycodestyle 471 elif hash pep8 2> /dev/null; then 472 PEP8=pep8 473 fi 474 475 if [ -n "${PEP8}" ]; then 476 echo -n "Checking Python style..." 477 478 PEP8_ARGS=" --max-line-length=140" 479 480 error=0 481 git ls-files '*.py' | xargs -P$(nproc) -n1 $PEP8 $PEP8_ARGS > pep8.log || error=1 482 if [ $error -ne 0 ]; then 483 echo " Python formatting errors detected" 484 cat pep8.log 485 rc=1 486 else 487 echo " OK" 488 fi 489 rm -f pep8.log 490 else 491 echo "You do not have pycodestyle or pep8 installed so your Python style is not being checked!" 492 fi 493 494 return $rc 495} 496 497function check_golang_style() { 498 local rc=0 out 499 500 if hash golangci-lint 2> /dev/null; then 501 echo -n "Checking Golang style..." 502 gofmtOut=$(gofmt -d . 2>&1) 503 golintOut=$(find "$rootdir/go" -name go.mod -execdir golangci-lint run \; 2>&1) 504 if [[ -n "$gofmtOut" ]] && [[ $gofmtOut == *"diff"* ]]; then 505 cat <<- WARN 506 Golang formatting errors detected: 507 echo "$gofmtOut" 508 WARN 509 510 return 1 511 elif [[ -n "$golintOut" ]]; then 512 cat <<- WARN 513 Golangci lint failed: 514 echo "$golintOut" 515 WARN 516 517 return 1 518 else 519 echo "OK" 520 fi 521 else 522 echo "You do not have golangci-lint installed, so Golang style will not be checked!" 523 fi 524 return 0 525} 526 527function get_bash_files() { 528 local sh shebang 529 530 mapfile -t sh < <(git ls-files '*.sh') 531 mapfile -t shebang < <(git grep -l '^#!.*bash') 532 533 get_diffed_dups "${sh[@]}" "${shebang[@]}" 534} 535 536function check_bash_style() { 537 local rc=0 538 539 # find compatible shfmt binary 540 shfmt_bins=$(compgen -c | grep '^shfmt' | uniq || true) 541 for bin in $shfmt_bins; do 542 shfmt_version=$("$bin" --version) 543 if [ $shfmt_version != "v3.1.0" ]; then 544 echo "$bin version $shfmt_version not used (only v3.1.0 is supported)" 545 echo "v3.1.0 can be installed using 'scripts/pkgdep.sh -d'" 546 else 547 shfmt=$bin 548 break 549 fi 550 done 551 552 if [ -n "$shfmt" ]; then 553 shfmt_cmdline=() sh_files=() 554 555 mapfile -t sh_files < <(get_bash_files) 556 557 if ((${#sh_files[@]})); then 558 printf 'Checking .sh formatting style...' 559 560 shfmt_cmdline+=(-i 0) # indent_style = tab|indent_size = 0 561 shfmt_cmdline+=(-bn) # binary_next_line = true 562 shfmt_cmdline+=(-ci) # switch_case_indent = true 563 shfmt_cmdline+=(-ln bash) # shell_variant = bash (default) 564 shfmt_cmdline+=(-d) # diffOut - print diff of the changes and exit with != 0 565 shfmt_cmdline+=(-sr) # redirect operators will be followed by a space 566 567 diff=${output_dir:-$PWD}/$shfmt.patch 568 569 # Explicitly tell shfmt to not look for .editorconfig. .editorconfig is also not looked up 570 # in case any formatting arguments has been passed on its cmdline. 571 if ! SHFMT_NO_EDITORCONFIG=true "$shfmt" "${shfmt_cmdline[@]}" "${sh_files[@]}" > "$diff"; then 572 # In case shfmt detects an actual syntax error it will write out a proper message on 573 # its stderr, hence the diff file should remain empty. 574 rc=1 575 if [[ -s $diff ]]; then 576 if patch --merge -p0 < "$diff"; then 577 diff_out=$(git diff) 578 579 if [[ -n $diff_out ]]; then 580 cat <<- ERROR_SHFMT 581 582 * Errors in style formatting have been detected. 583 Please, review the generated patch at $diff 584 585 # _START_OF_THE_DIFF 586 587 $diff_out 588 589 # _END_OF_THE_DIFF 590 591 ERROR_SHFMT 592 else 593 # Empty diff? This likely means that we reverted to a clean state 594 printf '* Patch reverted, please review your changes and %s\n' "$diff" 595 fi 596 else 597 printf '* Failed to apply %s\n' "$diff" 598 599 fi 600 fi 601 else 602 rm -f "$diff" 603 printf ' OK\n' 604 fi 605 fi 606 else 607 echo "Supported version of shfmt not detected, Bash style formatting check is skipped" 608 fi 609 610 # Cleanup potential .orig files that shfmt creates 611 local orig_f 612 613 mapfile -t orig_f < <(git diff --name-only) 614 orig_f=("${orig_f[@]/%/.orig}") 615 616 if ((${#orig_f[@]} > 0)); then 617 git clean -f "${orig_f[@]}" 618 fi 619 620 return $rc 621} 622 623function check_bash_static_analysis() { 624 local rc=0 files=() 625 626 mapfile -t files < <(get_bash_files) 627 ((${#files[@]} > 0)) || return 0 628 629 if hash shellcheck 2> /dev/null; then 630 echo -n "Checking Bash static analysis with shellcheck..." 631 632 shellcheck_v=$(shellcheck --version | grep -P "version: [0-9\.]+" | cut -d " " -f2) 633 634 # SHCK_EXCLUDE contains a list of all of the spellcheck errors found in SPDK scripts 635 # currently. New errors should only be added to this list if the cost of fixing them 636 # is deemed too high. For more information about the errors, go to: 637 # https://github.com/koalaman/shellcheck/wiki/Checks 638 # Error descriptions can also be found at: https://github.com/koalaman/shellcheck/wiki 639 # SPDK fails some error checks which have been deprecated in later versions of shellcheck. 640 # We will not try to fix these error checks, but instead just leave the error types here 641 # so that we can still run with older versions of shellcheck. 642 SHCK_EXCLUDE="SC1117" 643 # SPDK has decided to not fix violations of these errors. 644 # We are aware about below exclude list and we want this errors to be excluded. 645 # SC1083: This {/} is literal. Check expression (missing ;/\n?) or quote it. 646 # SC1090: Can't follow non-constant source. Use a directive to specify location. 647 # SC1091: Not following: (error message here) 648 # SC2001: See if you can use ${variable//search/replace} instead. 649 # SC2010: Don't use ls | grep. Use a glob or a for loop with a condition to allow non-alphanumeric filenames. 650 # SC2015: Note that A && B || C is not if-then-else. C may run when A is true. 651 # SC2016: Expressions don't expand in single quotes, use double quotes for that. 652 # SC2034: foo appears unused. Verify it or export it. 653 # SC2046: Quote this to prevent word splitting. 654 # SC2086: Double quote to prevent globbing and word splitting. 655 # SC2119: Use foo "$@" if function's $1 should mean script's $1. 656 # SC2120: foo references arguments, but none are ever passed. 657 # SC2128: Expanding an array without an index only gives the first element. 658 # SC2148: Add shebang to the top of your script. 659 # SC2153: Possible Misspelling: MYVARIABLE may not be assigned, but MY_VARIABLE is. 660 # SC2154: var is referenced but not assigned. 661 # SC2164: Use cd ... || exit in case cd fails. 662 # SC2174: When used with -p, -m only applies to the deepest directory. 663 # SC2178: Variable was used as an array but is now assigned a string. 664 # SC2206: Quote to prevent word splitting/globbing, 665 # or split robustly with mapfile or read -a. 666 # SC2207: Prefer mapfile or read -a to split command output (or quote to avoid splitting). 667 # SC2223: This default assignment may cause DoS due to globbing. Quote it. 668 SHCK_EXCLUDE="$SHCK_EXCLUDE,SC1083,SC1090,SC1091,SC2010,SC2015,SC2016,SC2034,SC2046,SC2086,\ 669SC2119,SC2120,SC2128,SC2148,SC2153,SC2154,SC2164,SC2174,SC2178,SC2001,SC2206,SC2207,SC2223" 670 671 SHCK_FORMAT="tty" 672 SHCK_APPLY=false 673 SHCH_ARGS="-e $SHCK_EXCLUDE -f $SHCK_FORMAT" 674 675 if ge "$shellcheck_v" 0.4.0; then 676 SHCH_ARGS+=" -x" 677 else 678 echo "shellcheck $shellcheck_v detected, recommended >= 0.4.0." 679 fi 680 681 printf '%s\n' "${files[@]}" | xargs -P$(nproc) -n1 shellcheck $SHCH_ARGS &> shellcheck.log 682 if [[ -s shellcheck.log ]]; then 683 echo " Bash shellcheck errors detected!" 684 685 cat shellcheck.log 686 if $SHCK_APPLY; then 687 git apply shellcheck.log 688 echo "Bash errors were automatically corrected." 689 echo "Please remember to add the changes to your commit." 690 fi 691 rc=1 692 else 693 echo " OK" 694 fi 695 rm -f shellcheck.log 696 else 697 echo "You do not have shellcheck installed so your Bash static analysis is not being performed!" 698 fi 699 700 return $rc 701} 702 703function check_changelog() { 704 local rc=0 705 706 # Check if any of the public interfaces were modified by this patch. 707 # Warn the user to consider updating the changelog any changes 708 # are detected. 709 echo -n "Checking whether CHANGELOG.md should be updated..." 710 staged=$(git diff --name-only --cached .) 711 working=$(git status -s --porcelain --ignore-submodules | grep -iv "??" | awk '{print $2}') 712 files="$staged $working" 713 if [[ "$files" = " " ]]; then 714 files=$(git diff-tree --no-commit-id --name-only -r HEAD) 715 fi 716 717 has_changelog=0 718 for f in $files; do 719 if [[ $f == CHANGELOG.md ]]; then 720 # The user has a changelog entry, so exit. 721 has_changelog=1 722 break 723 fi 724 done 725 726 needs_changelog=0 727 if [ $has_changelog -eq 0 ]; then 728 for f in $files; do 729 if [[ $f == include/spdk/* ]] || [[ $f == scripts/rpc.py ]] || [[ $f == etc/* ]]; then 730 echo "" 731 echo -n "$f was modified. Consider updating CHANGELOG.md." 732 needs_changelog=1 733 fi 734 done 735 fi 736 737 if [ $needs_changelog -eq 0 ]; then 738 echo " OK" 739 else 740 echo "" 741 fi 742 743 return $rc 744} 745 746function check_json_rpc() { 747 local rc=0 748 749 echo -n "Checking that all RPCs are documented..." 750 while IFS='"' read -r _ rpc _; do 751 if ! grep -q "^### $rpc" doc/jsonrpc.md; then 752 echo "Missing JSON-RPC documentation for ${rpc}" 753 rc=1 754 fi 755 done < <(git grep -h -E "^SPDK_RPC_REGISTER\(" ':!test/*' ':!examples/*') 756 757 if [ $rc -eq 0 ]; then 758 echo " OK" 759 fi 760 return $rc 761} 762 763function check_markdown_format() { 764 local rc=0 765 766 if hash mdl 2> /dev/null; then 767 echo -n "Checking markdown files format..." 768 mdl -g -s $rootdir/mdl_rules.rb . > mdl.log || true 769 if [ -s mdl.log ]; then 770 echo " Errors in .md files detected:" 771 cat mdl.log 772 rc=1 773 else 774 echo " OK" 775 fi 776 rm -f mdl.log 777 else 778 echo "You do not have markdownlint installed so .md files not being checked!" 779 fi 780 781 return $rc 782} 783 784function check_rpc_args() { 785 local rc=0 786 787 echo -n "Checking rpc.py argument option names..." 788 grep add_argument scripts/rpc.py | $GNU_GREP -oP "(?<=--)[a-z0-9\-\_]*(?=\')" | grep "_" > badargs.log 789 790 if [[ -s badargs.log ]]; then 791 echo "rpc.py arguments with underscores detected!" 792 cat badargs.log 793 echo "Please convert the underscores to dashes." 794 rc=1 795 else 796 echo " OK" 797 fi 798 rm -f badargs.log 799 return $rc 800} 801 802function get_files_for_lic() { 803 local f_shebang="" f_suffix=() f_type=() f_all=() exceptions="" 804 805 f_shebang+="bash|" 806 f_shebang+="make|" 807 f_shebang+="perl|" 808 f_shebang+="python|" 809 f_shebang+="sh" 810 811 f_suffix+=("*.c") 812 f_suffix+=("*.cpp") 813 f_suffix+=("*.h") 814 f_suffix+=("*.go") 815 f_suffix+=("*.mk") 816 f_suffix+=("*.pl") 817 f_suffix+=("*.py") 818 f_suffix+=("*.sh") 819 f_suffix+=("*.yaml") 820 821 f_type+=("*Dockerfile") 822 f_type+=("*Makefile") 823 824 # Exclude files that may match the above types but should not 825 # fall under SPDX check. 826 exceptions+="include/linux|" 827 exceptions+="include/spdk/queue_extras.h" 828 829 mapfile -t f_all < <( 830 git ls-files "${f_suffix[@]}" "${f_type[@]}" 831 git grep -lE "^#!.*($f_shebang)" 832 ) 833 834 printf '%s\n' "${f_all[@]}" | sort -u | grep -vE "$exceptions" 835} 836 837function check_spdx_lic() { 838 local files_missing_license_header=() hint=() 839 local rc=0 840 841 hint+=("SPDX-License-Identifier: BSD-3-Clause") 842 hint+=("All rights reserved.") 843 844 printf 'Checking SPDX-license...' 845 846 mapfile -t files_missing_license_header < <( 847 grep -LE "SPDX-License-Identifier:.+" $(get_files_for_lic) 848 ) 849 850 if ((${#files_missing_license_header[@]} > 0)); then 851 printf '\nFollowing files are missing SPDX-license header:\n' 852 printf ' @%s\n' "${files_missing_license_header[@]}" 853 printf '\nExample:\n' 854 printf ' # %s\n' "${hint[@]}" 855 return 1 856 fi 857 858 printf 'OK\n' 859} 860 861function get_diffed_files() { 862 # Get files where changes are meant to be committed 863 git diff --name-only HEAD HEAD~1 864 # Get files from staging 865 git diff --name-only --cached HEAD 866 git diff --name-only HEAD 867} 868 869function get_diffed_dups() { 870 local files=("$@") diff=() _diff=() 871 872 # Sort and get rid of duplicates from the main list 873 mapfile -t files < <(printf '%s\n' "${files[@]}" | sort -u) 874 # Get staged|committed files 875 mapfile -t diff < <(get_diffed_files | sort -u) 876 877 if [[ ! -v CHECK_FORMAT_ONLY_DIFF ]]; then 878 # Just return the main list 879 printf '%s\n' "${files[@]}" 880 return 0 881 fi 882 883 if ((${#diff[@]} > 0)); then 884 # Check diff'ed files against the main list to see if they are a subset 885 # of it. If yes, then we return the duplicates which are the files that 886 # should be committed, modified. 887 mapfile -t _diff < <( 888 printf '%s\n' "${diff[@]}" "${files[@]}" | sort | uniq -d 889 ) 890 if ((${#_diff[@]} > 0)); then 891 printf '%s\n' "${_diff[@]}" 892 return 0 893 fi 894 fi 895} 896 897function check_extern_c() { 898 local files_missing_extern_c=() 899 900 printf 'Checking extern "C"... ' 901 902 mapfile -t files_missing_extern_c < <( 903 grep -LE "extern \"C\"" $(git ls-files -- ./include/spdk/*.h) 904 ) 905 906 if ((${#files_missing_extern_c[@]} > 0)); then 907 printf '\nFollowing header files are missing extern C:\n' 908 printf ' %s\n' "${files_missing_extern_c[@]}" 909 return 1 910 fi 911 912 printf 'OK\n' 913} 914 915rc=0 916 917check_permissions || rc=1 918check_c_style || rc=1 919 920GIT_VERSION=$(git --version | cut -d' ' -f3) 921 922if version_lt "1.9.5" "${GIT_VERSION}"; then 923 # git <1.9.5 doesn't support pathspec magic exclude 924 echo " Your git version is too old to perform all tests. Please update git to at least 1.9.5 version..." 925 exit $rc 926fi 927 928check_comment_style || rc=1 929check_markdown_format || rc=1 930check_spaces_before_tabs || rc=1 931check_trailing_whitespace || rc=1 932check_forbidden_functions || rc=1 933check_cunit_style || rc=1 934check_eof || rc=1 935check_posix_includes || rc=1 936check_naming_conventions || rc=1 937check_include_style || rc=1 938check_opts_structs || rc=1 939check_attr_packed || rc=1 940check_python_style || rc=1 941check_bash_style || rc=1 942check_bash_static_analysis || rc=1 943check_changelog || rc=1 944check_json_rpc || rc=1 945check_rpc_args || rc=1 946check_spdx_lic || rc=1 947check_golang_style || rc=1 948check_extern_c || rc=1 949 950exit $rc 951