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