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