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