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 supported_shfmt_version=v3.8.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 != "$supported_shfmt_version" ]]; then 544 echo "$bin version $shfmt_version not used (only $supported_shfmt_version is supported)" 545 echo "$supported_shfmt_version 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 if ! "$shfmt" "${shfmt_cmdline[@]}" "${sh_files[@]}" > "$diff"; then 570 # In case shfmt detects an actual syntax error it will write out a proper message on 571 # its stderr, hence the diff file should remain empty. 572 rc=1 573 if [[ -s $diff ]]; then 574 if patch --merge -p0 < "$diff"; then 575 diff_out=$(git diff) 576 577 if [[ -n $diff_out ]]; then 578 cat <<- ERROR_SHFMT 579 580 * Errors in style formatting have been detected. 581 Please, review the generated patch at $diff 582 583 # _START_OF_THE_DIFF 584 585 $diff_out 586 587 # _END_OF_THE_DIFF 588 589 ERROR_SHFMT 590 else 591 # Empty diff? This likely means that we reverted to a clean state 592 printf '* Patch reverted, please review your changes and %s\n' "$diff" 593 fi 594 else 595 printf '* Failed to apply %s\n' "$diff" 596 597 fi 598 fi 599 else 600 rm -f "$diff" 601 printf ' OK\n' 602 fi 603 fi 604 else 605 echo "Supported version of shfmt not detected, Bash style formatting check is skipped" 606 fi 607 608 # Cleanup potential .orig files that shfmt creates 609 local orig_f 610 611 mapfile -t orig_f < <(git diff --name-only) 612 orig_f=("${orig_f[@]/%/.orig}") 613 614 if ((${#orig_f[@]} > 0)); then 615 git clean -f "${orig_f[@]}" 616 fi 617 618 return $rc 619} 620 621function check_bash_static_analysis() { 622 local rc=0 files=() 623 624 mapfile -t files < <(get_bash_files) 625 ((${#files[@]} > 0)) || return 0 626 627 if hash shellcheck 2> /dev/null; then 628 echo -n "Checking Bash static analysis with shellcheck..." 629 630 shellcheck_v=$(shellcheck --version | grep -P "version: [0-9\.]+" | cut -d " " -f2) 631 632 # SHCK_EXCLUDE contains a list of all of the spellcheck errors found in SPDK scripts 633 # currently. New errors should only be added to this list if the cost of fixing them 634 # is deemed too high. For more information about the errors, go to: 635 # https://github.com/koalaman/shellcheck/wiki/Checks 636 # Error descriptions can also be found at: https://github.com/koalaman/shellcheck/wiki 637 # SPDK fails some error checks which have been deprecated in later versions of shellcheck. 638 # We will not try to fix these error checks, but instead just leave the error types here 639 # so that we can still run with older versions of shellcheck. 640 SHCK_EXCLUDE="SC1117" 641 # SPDK has decided to not fix violations of these errors. 642 # We are aware about below exclude list and we want this errors to be excluded. 643 # SC1083: This {/} is literal. Check expression (missing ;/\n?) or quote it. 644 # SC1090: Can't follow non-constant source. Use a directive to specify location. 645 # SC1091: Not following: (error message here) 646 # SC2001: See if you can use ${variable//search/replace} instead. 647 # SC2010: Don't use ls | grep. Use a glob or a for loop with a condition to allow non-alphanumeric filenames. 648 # SC2015: Note that A && B || C is not if-then-else. C may run when A is true. 649 # SC2016: Expressions don't expand in single quotes, use double quotes for that. 650 # SC2034: foo appears unused. Verify it or export it. 651 # SC2046: Quote this to prevent word splitting. 652 # SC2086: Double quote to prevent globbing and word splitting. 653 # SC2119: Use foo "$@" if function's $1 should mean script's $1. 654 # SC2120: foo references arguments, but none are ever passed. 655 # SC2128: Expanding an array without an index only gives the first element. 656 # SC2148: Add shebang to the top of your script. 657 # SC2153: Possible Misspelling: MYVARIABLE may not be assigned, but MY_VARIABLE is. 658 # SC2154: var is referenced but not assigned. 659 # SC2164: Use cd ... || exit in case cd fails. 660 # SC2174: When used with -p, -m only applies to the deepest directory. 661 # SC2178: Variable was used as an array but is now assigned a string. 662 # SC2206: Quote to prevent word splitting/globbing, 663 # or split robustly with mapfile or read -a. 664 # SC2207: Prefer mapfile or read -a to split command output (or quote to avoid splitting). 665 # SC2223: This default assignment may cause DoS due to globbing. Quote it. 666 SHCK_EXCLUDE="$SHCK_EXCLUDE,SC1083,SC1090,SC1091,SC2010,SC2015,SC2016,SC2034,SC2046,SC2086,\ 667SC2119,SC2120,SC2128,SC2148,SC2153,SC2154,SC2164,SC2174,SC2178,SC2001,SC2206,SC2207,SC2223" 668 669 SHCK_FORMAT="tty" 670 SHCK_APPLY=false 671 SHCH_ARGS="-e $SHCK_EXCLUDE -f $SHCK_FORMAT" 672 673 if ge "$shellcheck_v" 0.4.0; then 674 SHCH_ARGS+=" -x" 675 else 676 echo "shellcheck $shellcheck_v detected, recommended >= 0.4.0." 677 fi 678 679 printf '%s\n' "${files[@]}" | xargs -P$(nproc) -n1 shellcheck $SHCH_ARGS &> shellcheck.log 680 if [[ -s shellcheck.log ]]; then 681 echo " Bash shellcheck errors detected!" 682 683 cat shellcheck.log 684 if $SHCK_APPLY; then 685 git apply shellcheck.log 686 echo "Bash errors were automatically corrected." 687 echo "Please remember to add the changes to your commit." 688 fi 689 rc=1 690 else 691 echo " OK" 692 fi 693 rm -f shellcheck.log 694 else 695 echo "You do not have shellcheck installed so your Bash static analysis is not being performed!" 696 fi 697 698 return $rc 699} 700 701function check_changelog() { 702 local rc=0 703 704 # Check if any of the public interfaces were modified by this patch. 705 # Warn the user to consider updating the changelog any changes 706 # are detected. 707 echo -n "Checking whether CHANGELOG.md should be updated..." 708 staged=$(git diff --name-only --cached .) 709 working=$(git status -s --porcelain --ignore-submodules | grep -iv "??" | awk '{print $2}') 710 files="$staged $working" 711 if [[ "$files" = " " ]]; then 712 files=$(git diff-tree --no-commit-id --name-only -r HEAD) 713 fi 714 715 has_changelog=0 716 for f in $files; do 717 if [[ $f == CHANGELOG.md ]]; then 718 # The user has a changelog entry, so exit. 719 has_changelog=1 720 break 721 fi 722 done 723 724 needs_changelog=0 725 if [ $has_changelog -eq 0 ]; then 726 for f in $files; do 727 if [[ $f == include/spdk/* ]] || [[ $f == scripts/rpc.py ]] || [[ $f == etc/* ]]; then 728 echo "" 729 echo -n "$f was modified. Consider updating CHANGELOG.md." 730 needs_changelog=1 731 fi 732 done 733 fi 734 735 if [ $needs_changelog -eq 0 ]; then 736 echo " OK" 737 else 738 echo "" 739 fi 740 741 return $rc 742} 743 744function check_json_rpc() { 745 local rc=0 746 747 echo -n "Checking that all RPCs are documented..." 748 while IFS='"' read -r _ rpc _; do 749 if ! grep -q "^### $rpc" doc/jsonrpc.md; then 750 echo "Missing JSON-RPC documentation for ${rpc}" 751 rc=1 752 fi 753 done < <(git grep -h -E "^SPDK_RPC_REGISTER\(" ':!test/*' ':!examples/*') 754 755 if [ $rc -eq 0 ]; then 756 echo " OK" 757 fi 758 return $rc 759} 760 761function check_markdown_format() { 762 local rc=0 763 764 if hash mdl 2> /dev/null; then 765 echo -n "Checking markdown files format..." 766 mdl -g -s $rootdir/mdl_rules.rb . > mdl.log || true 767 if [ -s mdl.log ]; then 768 echo " Errors in .md files detected:" 769 cat mdl.log 770 rc=1 771 else 772 echo " OK" 773 fi 774 rm -f mdl.log 775 else 776 echo "You do not have markdownlint installed so .md files not being checked!" 777 fi 778 779 return $rc 780} 781 782function check_rpc_args() { 783 local rc=0 784 785 echo -n "Checking rpc.py argument option names..." 786 grep add_argument scripts/rpc.py | $GNU_GREP -oP "(?<=--)[a-z0-9\-\_]*(?=\')" | grep "_" > badargs.log 787 788 if [[ -s badargs.log ]]; then 789 echo "rpc.py arguments with underscores detected!" 790 cat badargs.log 791 echo "Please convert the underscores to dashes." 792 rc=1 793 else 794 echo " OK" 795 fi 796 rm -f badargs.log 797 return $rc 798} 799 800function get_files_for_lic() { 801 local f_shebang="" f_suffix=() f_type=() f_all=() exceptions="" 802 803 f_shebang+="bash|" 804 f_shebang+="make|" 805 f_shebang+="perl|" 806 f_shebang+="python|" 807 f_shebang+="sh" 808 809 f_suffix+=("*.c") 810 f_suffix+=("*.cpp") 811 f_suffix+=("*.h") 812 f_suffix+=("*.go") 813 f_suffix+=("*.mk") 814 f_suffix+=("*.pl") 815 f_suffix+=("*.py") 816 f_suffix+=("*.sh") 817 f_suffix+=("*.yaml") 818 819 f_type+=("*Dockerfile") 820 f_type+=("*Makefile") 821 822 # Exclude files that may match the above types but should not 823 # fall under SPDX check. 824 exceptions+="include/linux|" 825 exceptions+="include/spdk/queue_extras.h" 826 827 mapfile -t f_all < <( 828 git ls-files "${f_suffix[@]}" "${f_type[@]}" 829 git grep -lE "^#!.*($f_shebang)" 830 ) 831 832 printf '%s\n' "${f_all[@]}" | sort -u | grep -vE "$exceptions" 833} 834 835function check_spdx_lic() { 836 local files_missing_license_header=() hint=() 837 local rc=0 838 839 hint+=("SPDX-License-Identifier: BSD-3-Clause") 840 hint+=("All rights reserved.") 841 842 printf 'Checking SPDX-license...' 843 844 mapfile -t files_missing_license_header < <( 845 grep -LE "SPDX-License-Identifier:.+" $(get_files_for_lic) 846 ) 847 848 if ((${#files_missing_license_header[@]} > 0)); then 849 printf '\nFollowing files are missing SPDX-license header:\n' 850 printf ' @%s\n' "${files_missing_license_header[@]}" 851 printf '\nExample:\n' 852 printf ' # %s\n' "${hint[@]}" 853 return 1 854 fi 855 856 printf 'OK\n' 857} 858 859function get_diffed_files() { 860 # Get files where changes are meant to be committed 861 git diff --name-only HEAD HEAD~1 862 # Get files from staging 863 git diff --name-only --cached HEAD 864 git diff --name-only HEAD 865} 866 867function get_diffed_dups() { 868 local files=("$@") diff=() _diff=() 869 870 # Sort and get rid of duplicates from the main list 871 mapfile -t files < <(printf '%s\n' "${files[@]}" | sort -u) 872 # Get staged|committed files 873 mapfile -t diff < <(get_diffed_files | sort -u) 874 875 if [[ ! -v CHECK_FORMAT_ONLY_DIFF ]]; then 876 # Just return the main list 877 printf '%s\n' "${files[@]}" 878 return 0 879 fi 880 881 if ((${#diff[@]} > 0)); then 882 # Check diff'ed files against the main list to see if they are a subset 883 # of it. If yes, then we return the duplicates which are the files that 884 # should be committed, modified. 885 mapfile -t _diff < <( 886 printf '%s\n' "${diff[@]}" "${files[@]}" | sort | uniq -d 887 ) 888 if ((${#_diff[@]} > 0)); then 889 printf '%s\n' "${_diff[@]}" 890 return 0 891 fi 892 fi 893} 894 895function check_extern_c() { 896 local files_missing_extern_c=() 897 898 printf 'Checking extern "C"... ' 899 900 mapfile -t files_missing_extern_c < <( 901 grep -LE "extern \"C\"" $(git ls-files -- ./include/spdk/*.h) 902 ) 903 904 if ((${#files_missing_extern_c[@]} > 0)); then 905 printf '\nFollowing header files are missing extern C:\n' 906 printf ' %s\n' "${files_missing_extern_c[@]}" 907 return 1 908 fi 909 910 printf 'OK\n' 911} 912 913rc=0 914 915check_permissions || rc=1 916check_c_style || rc=1 917 918GIT_VERSION=$(git --version | cut -d' ' -f3) 919 920if version_lt "1.9.5" "${GIT_VERSION}"; then 921 # git <1.9.5 doesn't support pathspec magic exclude 922 echo " Your git version is too old to perform all tests. Please update git to at least 1.9.5 version..." 923 exit $rc 924fi 925 926check_comment_style || rc=1 927check_markdown_format || rc=1 928check_spaces_before_tabs || rc=1 929check_trailing_whitespace || rc=1 930check_forbidden_functions || rc=1 931check_cunit_style || rc=1 932check_eof || rc=1 933check_posix_includes || rc=1 934check_naming_conventions || rc=1 935check_include_style || rc=1 936check_opts_structs || rc=1 937check_attr_packed || rc=1 938check_python_style || rc=1 939check_bash_style || rc=1 940check_bash_static_analysis || rc=1 941check_changelog || rc=1 942check_json_rpc || rc=1 943check_rpc_args || rc=1 944check_spdx_lic || rc=1 945check_golang_style || rc=1 946check_extern_c || rc=1 947 948exit $rc 949