lint 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. #! /bin/bash
  2. #
  3. # Routine sanity checks for libpqxx source tree.
  4. #
  5. # Optionally, set environment variable "srcdir" to the source directory. It
  6. # defaults to the parent directory of the one where this script is. This trick
  7. # requires bash (or a close equivalent) as the shell.
  8. set -eu -o pipefail
  9. SRCDIR="${srcdir:-$(dirname "${BASH_SOURCE[0]}")/..}"
  10. PQXXVERSION="$(cd "$SRCDIR" && "$SRCDIR/tools/extract_version")"
  11. ARGS="${1:-}"
  12. # Check that all source code is ASCII.
  13. #
  14. # I'd love to have rich Unicode, but I can live without it. But we don't want
  15. # any surprises in contributions.
  16. check_ascii() {
  17. local exotics=$(
  18. find -name \*.cxx -o -name \*.hxx |
  19. xargs cat |
  20. tr -d '\011-\176' |
  21. wc -c
  22. )
  23. if [ $exotics != 0 ]
  24. then
  25. echo >&2 "There's a non-ASCII character somewhere."
  26. exit 1
  27. fi
  28. }
  29. # This version must be at the top of the NEWS file.
  30. check_news_version() {
  31. if ! head -n1 $SRCDIR/NEWS | grep -q "^$PQXXVERSION\$"
  32. then
  33. cat <<EOF >&2
  34. Version $PQXXVERSION is not at the top of NEWS.
  35. EOF
  36. exit 1
  37. fi
  38. }
  39. # Count number of times header $1 is included from each of given input files.
  40. # Output is lines of <filename>:<count>, one line per file, sorted.
  41. count_includes() {
  42. local HEADER_NAME WS PAT
  43. HEADER_NAME="$1"
  44. shift
  45. WS="[[:space:]]*"
  46. PAT="^${WS}#${WS}include${WS}[<\"]$HEADER_NAME[>\"]"
  47. # It's OK for the grep to fail.
  48. (grep -c "$PAT" $* || /bin/true) | sort
  49. }
  50. # Check that any includes of $1-pre.hxx are matched by $1-post.hxx ones.
  51. match_pre_post_headers() {
  52. local NAME TEMPDIR PRE POST HEADERS
  53. NAME="$1"
  54. TEMPDIR="$(mktemp -d)"
  55. if test -z "$TEMPDIR"
  56. then
  57. echo >&2 "Could not create temporary directory."
  58. exit 1
  59. fi
  60. PRE="$TEMPDIR/pre"
  61. POST="$TEMPDIR/post"
  62. HEADERS=$(find include/pqxx/* -type f | grep -v '\.swp$')
  63. count_includes \
  64. $SRCDIR/NAME-pre.hxx $HEADERS >"$PRE"
  65. count_includes \
  66. $SRCDIR/NAME-post.hxx $HEADERS >"$POST"
  67. DIFF="$(diff "$PRE" "$POST")" || /bin/true
  68. rm -r -- "$TEMPDIR"
  69. if test -n "$DIFF"
  70. then
  71. cat <<EOF >&2
  72. Mismatched pre/post header pairs:
  73. $DIFF
  74. EOF
  75. exit 1
  76. fi
  77. }
  78. # Any file that includes header-pre.hxx must also include header-post.hxx, and
  79. # vice versa. Similar for ignore-deprecated-{pre|post}.hxx.
  80. check_compiler_internal_headers() {
  81. match_pre_post_headers "pqxx/internal/header"
  82. match_pre_post_headers "pqxx/internal/ignore-deprecated"
  83. }
  84. cpplint() {
  85. local cxxflags dialect includes
  86. if which clang-tidy >/dev/null
  87. then
  88. if [ -e compile_flags ]
  89. then
  90. # Pick out relevant flags, but leave out the rest.
  91. # If we're not compiling with clang, compile_flags may contain
  92. # options that clang-tidy doesn't recognise.
  93. dialect="$(grep -o -- '-std=[^[:space:]]*' compile_flags || true)"
  94. includes="$(
  95. grep -o -- '-I[[:space:]]*[^[:space:]]*' compile_flags ||
  96. true)"
  97. else
  98. dialect=""
  99. includes=""
  100. fi
  101. cxxflags="$dialect $includes"
  102. # TODO: Please, is there any way we can parallelise this?
  103. # TODO: I'd like cppcoreguidelines-*, but it's a tsunami of false positives.
  104. # TODO: Some useful checks in abseil-*, but it recommends "use our library."
  105. # TODO: Check test/, but tolerate some of the dubious stuff tests do.
  106. clang-tidy \
  107. $(find $SRCDIR/src $SRCDIR/tools -name \*.cxx) \
  108. --checks=boost-*, \
  109. -- \
  110. -I$SRCDIR/include -Iinclude $cxxflags
  111. fi
  112. # Run Facebook's "infer" static analyser, if available.
  113. # Instructions here: https://fbinfer.com/docs/getting-started/
  114. if which infer >/dev/null
  115. then
  116. # This will work in an out-of-tree build, but either way it does
  117. # require a successful "configure", or a cmake with the "make"
  118. # generator.
  119. infer capture -- make -j$(nproc)
  120. infer run
  121. fi
  122. }
  123. pylint() {
  124. local PYFILES="$SRCDIR/tools/*.py $SRCDIR/tools/splitconfig"
  125. echo "Skipping pocketlint; it's not up to date with Python3."
  126. # if which pocketlint >/dev/null
  127. # then
  128. # pocketlint $PYFILES
  129. # fi
  130. if which pyflakes3 >/dev/null
  131. then
  132. pyflakes3 $PYFILES
  133. fi
  134. }
  135. main() {
  136. local full="no"
  137. for arg in $ARGS
  138. do
  139. case $arg in
  140. -h|--help)
  141. cat <<EOF
  142. Perform static checks on libpqxx build tree.
  143. Usage:
  144. $0 -h|--help -- print this message and exit.
  145. $0 -f|--full -- perform full check, including C++ analysis.
  146. $0 -- perform default check.
  147. EOF
  148. exit 0
  149. ;;
  150. -f|--full)
  151. full="yes"
  152. ;;
  153. *)
  154. echo >&2 "Unknown argument: '$arg'"
  155. exit 1
  156. ;;
  157. esac
  158. done
  159. check_ascii
  160. pylint
  161. check_news_version
  162. check_compiler_internal_headers
  163. if [ $full == "yes" ]
  164. then
  165. cpplint
  166. fi
  167. }
  168. main