diff options
author | vlefevre <vlefevre@280ebfd0-de03-0410-8827-d642c229c3f4> | 2016-04-08 15:28:59 +0000 |
---|---|---|
committer | vlefevre <vlefevre@280ebfd0-de03-0410-8827-d642c229c3f4> | 2016-04-08 15:28:59 +0000 |
commit | 5c8bf561df637053b4b0d3504e634c93128a3236 (patch) | |
tree | 6d3850aefe7b1f07b2e27ce48f4fdf0c1e817bf4 /tools | |
parent | 9df4283f8be36e42017db0aa5159c726cf502887 (diff) | |
download | mpfr-5c8bf561df637053b4b0d3504e634c93128a3236.tar.gz |
[tools/mpfrlint]
* Switched to zsh.
* Much more meaningful error messages.
* Fix: in the mpfr.texi punctuation test, ignore the comments.
git-svn-id: svn://scm.gforge.inria.fr/svn/mpfr/trunk@10256 280ebfd0-de03-0410-8827-d642c229c3f4
Diffstat (limited to 'tools')
-rwxr-xr-x | tools/mpfrlint | 223 |
1 files changed, 171 insertions, 52 deletions
diff --git a/tools/mpfrlint b/tools/mpfrlint index 80099ebe9..830263a11 100755 --- a/tools/mpfrlint +++ b/tools/mpfrlint @@ -1,36 +1,129 @@ -#!/usr/bin/env bash +#!/usr/bin/env zsh # Check possible problems in the MPFR source. +set -e +setopt EXTENDED_GLOB + # mpfrlint can be run from the tools directory -dir=`pwd` -[ -d src ] || [ "`basename "$dir"`" != tools ] || cd .. +dir=$PWD +[[ -d src ]] || [[ $dir:t != tools ]] || cd .. +err=0 + +if [[ -t 1 ]] then + term=1 + pfx="" + sfx="" +fi + +err-if-output() +{ + local dir checkoutput=1 checkstatus=1 output st h line + while [[ $1 = -* ]] + do + case $1 in + --dir=*) + dir=${1#--dir=} ;; + -o) + # The command generates output even in case of success; + # do not regard this as an error. + checkoutput="" ;; + -t) + # The command normally returns with a non-zero exit status; + # do not regard this as an error. + checkstatus="" ;; + *) + echo "unrecognized option '$1' for $0" >&2 + exit 1 ;; + esac + shift + done + [[ -n $dir ]] && pushd $dir + set +e + output=(${(f)"$($@[2,-1] 2>&1)"}) + st=$? + if [[ ( -n "$checkstatus" && $st -ne 0 ) || + ( -n "$checkoutput" && -n "$output" ) ]] then + if [[ -n "$1" && -t 1 ]] then + [[ -n "$pfx" ]] || pfx=$(tput 2>/dev/null bold) + [[ -n "$sfx" ]] || sfx=$(tput 2>/dev/null sgr0) + fi + h=${1:+$pfx$1:$sfx } + for line in ${output:-"exit status = $st"} + do + printf "%s%s\n" $h $line + done + err=1 + fi + set -e + [[ -n $dir ]] && popd +} + +if [[ $1 = test ]] then + export LC_ALL=C + err-if-output Err-e exit 17 + err-if-output Err-f false + err-if-output Err-t true + err-if-output "" echo "Empty first argument" + err-if-output "1 line" echo foo + err-if-output "2 lines" printf "foo\nbar\n" + err-if-output -o Err-f false + err-if-output -o Err-t true + err-if-output -o "cp test" cp + err-if-output -o "1 line" echo foo + err-if-output -t Err-f false + err-if-output -t Err-t true + err-if-output -t error echo output + echo "Test done." + exit +: <<EOF +The output should be: + +Err-e: exit status = 17 +Err-f: exit status = 1 +Empty first argument +1 line: foo +2 lines: foo +2 lines: bar +Err-f: exit status = 1 +cp test: cp: missing file operand +cp test: Try 'cp --help' for more information. +error: output +Test done. + +EOF +fi + +############################################################################ # Detect the possible use of forbidden macros in mpfr.h, such as those # starting with "HAVE_" or "WANT_". Public macros defined by MPFR must # start with "MPFR_". -perl -ne '/^#/ && ! /^# *error / or next; while (/\b([_A-Z]+)\b/g) - { my $m = $1; +err-if-output -t "" perl -ne ' + /^#/ && ! /^# *error / or next; while (/\b([_A-Z]+)\b/g) { + my $m = $1; $m =~ /^(_*MPFR_|_*GMP_|__(GNUC|ICC|STDC)(_|$)|_MSC_|U?INTMAX_C$)/ and next; print "Forbidden macro in mpfr.h line $.: $m\n" }' src/mpfr.h -grep '^# *include *<math\.h>' src/*.c +err-if-output -t "math.h" grep '^# *include *<math\.h>' src/*.c flaglist="underflow|overflow|divby0|nanflag|inexflag|erangeflag" grep -E "mpfr_($flaglist)_p" src/*.{c,h} | \ grep -v -i 'mpfr_clear_' | \ grep -v '^src/exceptions.c:' | \ grep -v '^src/mpfr-impl.h:#define mpfr_.*_p()' | \ - grep -v '^src/mpfr.h:__MPFR_DECLSPEC ' + grep -v '^src/mpfr.h:__MPFR_DECLSPEC ' | \ + err-if-output "flags" cat grep -E "mpfr_(clear|set)_($flaglist) *\(" src/*.{c,h} | \ grep -v '^src/exceptions.c:' | \ - grep -v '^src/mpfr.h:' + grep -v '^src/mpfr.h:' | \ + err-if-output "flags" cat grconf() { - grep -v '^dnl ' acinclude.m4 configure.ac | grep -E "$1" && \ - echo "in grconf '$1'" + grep -v '^dnl ' acinclude.m4 configure.ac | \ + err-if-output -t "grconf '$1'" grep -E "$1" } grconf '^(.*if +|[[:space:]]*)(test|\[).* == ' @@ -39,49 +132,55 @@ grconf '[^a-z][ef]?grep[^a-z]' grconf '[^a-z]sed[^a-z]' # Note: In the source, ignore everything related to mini-gmp. -srctests=`find src tests -name '*.[ch]' ! -name '*mini-gmp.*'` +srctests=({src,tests}/**/*.[ch]~*mini-gmp.*) +#srctests=(`find src tests -name '*.[ch]' ! -name '*mini-gmp.*'`) -grep GMP_LIMB_BITS $srctests +err-if-output -t "GMP_LIMB_BITS" grep GMP_LIMB_BITS $srctests -grep GMP_RND $srctests | grep -v '#define GMP_RND' +grep GMP_RND $srctests | err-if-output -t "GMP_RND*" grep -v '#define GMP_RND' # Note: exceptions __gmpn_rootrem and __gmpn_sbpi1_divappr_q # are normally used only if WANT_GMP_INTERNALS is defined. grep '__gmp[nz]_' $srctests | \ grep -v __gmpn_rootrem | \ - grep -v __gmpn_sbpi1_divappr_q + grep -v __gmpn_sbpi1_divappr_q | \ + err-if-output "GMP internals" cat -grep -E 'mp_(src)?ptr' $srctests +err-if-output -t "mp_ptr and mp_srcptr" grep -E 'mp_(src)?ptr' $srctests # Do not use __mpfr_struct structure members in .c files. -grep -E '[^0-9a-z_]_mpfr_(prec|sign|exp|d)' {src,tests}/*.c +err-if-output -t "__mpfr_struct members" \ + grep -E '[^0-9a-z_]_mpfr_(prec|sign|exp|d)' {src,tests}/*.c # Detect some uses of "x != x" and "x == x". If this occurs in source code, # x is probably a native FP number (otherwise the test is useless), but in # such a case, the DOUBLE_ISNAN macro should be used. -grep '( *\([^[:space:]]*\) *[!=]= *\1 *)' $srctests +err-if-output -t "x != x and x == x tests" \ + grep '( *\([^[:space:]]*\) *[!=]= *\1 *)' $srctests for i in exp prec rnd do grep "[^a-z]mp_${i}_t" $srctests | \ grep -v "\(# *define\|# *ifndef\|typedef\) *mp_${i}_t" | \ - grep -v "\[mp_${i}_t\]" + grep -v "\[mp_${i}_t\]" | \ + err-if-output "mp_*_t" cat done for file in $srctests do - perl -e 'my $f = do { local $/; <> }; - while ($f =~ /MPFR_LOG_MSG\s*\(\s*\(.*?\)\s*\)/gs) { - my $s = $&; print "$ARGV: $s\n" if - index($s,"\\n\"") < 0 || $s !~ /"\s*,/ - }' $file + err-if-output "MPFR_LOG_MSG format" perl -e ' + my $f = do { local $/; <> }; + while ($f =~ /MPFR_LOG_MSG\s*\(\s*\(.*?\)\s*\)/gs) { + my $s = $&; print "$ARGV: $s\n" if + index($s,"\\n\"") < 0 || $s !~ /"\s*,/ + }' $file done # Do not use snprintf as it is not available in ISO C90. # Even on platforms where it is available, the prototype # may not be included (e.g. with gcc -ansi), so that the # code may be compiled incorrectly. -grep '[^a-z_]snprintf *([^)]' $srctests +err-if-output -t "snprintf" grep '[^a-z_]snprintf *([^)]' $srctests # Constant checking should use either MPFR_STAT_STATIC_ASSERT # or MPFR_ASSERTN(0) for not yet implemented corner cases. @@ -93,43 +192,46 @@ grep '[^a-z_]snprintf *([^)]' $srctests grep 'MPFR_ASSERT[DN][^a-z]*;' src/*.c | grep -v 'MPFR_ASSERTN *(0)' | \ grep -v '\(MPFR_EMAX_MAX\|MPFR_EXP_MAX\).*LONG_MAX' | \ grep -v '\(MPFR_EMIN_MIN\|MPFR_EXP_MIN\).*LONG_MIN' | \ - grep -v MPFR_BLOCK_EXCEP + grep -v MPFR_BLOCK_EXCEP | \ + err-if-output -t "Constant checking" cat # ASSERT and ASSERT_ALWAYS must not be used for assertion checking. # Use MPFR_STAT_STATIC_ASSERT for static assertions, otherwise either # MPFR_ASSERTD (debug mode / hint for the compiler) or MPFR_ASSERTN. -grep -E '[^_]ASSERT(_ALWAYS)? *(\(|$)' {src,tests}/*.c +err-if-output -t "ASSERT / ASSERT_ALWAYS" \ + grep -E '[^_]ASSERT(_ALWAYS)? *(\(|$)' {src,tests}/*.c # Use MPFR_TMP_LIMBS_ALLOC. -grep 'MPFR_TMP_ALLOC.*\(BYTES_PER_MP_LIMB\|sizeof.*mp_limb_t\)' src/*.c +err-if-output -t "Use MPFR_TMP_LIMBS_ALLOC" \ + grep 'MPFR_TMP_ALLOC.*\(BYTES_PER_MP_LIMB\|sizeof.*mp_limb_t\)' src/*.c # Use simple mp_limb_t constants: MPFR_LIMB_ZERO, MPFR_LIMB_ONE, etc. # possibly except in MPFR_STAT_STATIC_ASSERT grep '(mp_limb_t) *-\?[01]' $srctests | grep -v '#define MPFR_LIMB_' | \ - grep -v MPFR_STAT_STATIC_ASSERT + err-if-output -t "Use simple mp_limb_t constants" \ + grep -v MPFR_STAT_STATIC_ASSERT for file in $srctests */Makefile.am acinclude.m4 configure.ac do # Note: this is one less that the POSIX minimum limit in case # implementations are buggy like POSIX examples. :) - perl -ne "/.{2047,}/ and print \ - \"Line \$. of file '$file' has more than 2046 bytes.\n\"" "$file" + err-if-output "" perl -ne "/.{2047,}/ and print \ + \"Line \$. of file $file has more than 2046 bytes.\n\"" "$file" done # Code style: a sign decimal constant for mpfr_set_inf and mpfr_set_zero # should be either 1 or -1 (except for the tests in tset.c). grep -E 'mpfr_set_(inf|zero) *\([^,]*, *[-+]?([02-9]|1[^)])' $srctests | \ - grep -v tests/tset\\.c: + err-if-output -t "mpfr_set_(inf|zero) second argument" \ + grep -v tests/tset\\.c: # In general, one needs to include mpfr-impl.h (note that some platforms # such as MS Windows use a config.h, which is included by mpfr-impl.h). for file in src/*.c do - [ "$file" = src/jyn_asympt.c ] || \ - [ "$file" = src/mini-gmp.c ] || \ - [ "$file" = src/round_raw_generic.c ] || \ + [[ "$file" = src/(jyn_asympt|mini-gmp|round_raw_generic).c ]] || \ grep -q '^# *include *"\(mpfr-impl\|fits.*\|gen_inverse\|random_deviate\)\.h"' $file || \ - echo "Missing '#include \"mpfr-impl.h\"' in $file?" + { echo "Missing '#include \"mpfr-impl.h\"' in $file?" && err=1 } done # mpfr_printf-like functions shouldn't be used in the tests, @@ -138,19 +240,24 @@ for file in tests/*.c do sed '/#if\(def\| *defined *(\)\? *HAVE_STDARG/,/#\(endif\|else\) .*HAVE_STDARG/d /\/\*.*\*\//d - /\/\*/,/\*\//d' $file | grep -q "mpfr_[a-z]*printf" && \ - echo "$file contains unprotected mpfr_printf-like function calls" + /\/\*/,/\*\//d' $file | \ + err-if-output -t "unprotected mpfr_printf-like function call in $file" \ + grep "mpfr_[a-z]*printf" done -grep __gmp_ tests/*.c | grep -v '^tests/tabort_defalloc' -grep -E '[^a-z_](m|re)alloc *\(' tests/*.c | grep -v '^tests/memory.c:' +grep __gmp_ tests/*.c | \ + err-if-output -t "__gmp_" grep -v '^tests/tabort_defalloc' + +grep -E '[^a-z_](m|re)alloc *\(' tests/*.c | \ + err-if-output -t "alloc" grep -v '^tests/memory.c:' # Check a Texinfo rule (Section "Ending a Sentence") with common words # that end with a capital letter: # Use '@.' instead of a period, '@!' instead of an exclamation point, # and '@?' instead of a question mark at the end of a sentence that # does end with a capital letter. -grep -E '(LIP|MPFR?|NaN)\)?[.!?]' doc/mpfr.texi | grep -v '^\* .*::' +grep -E '(LIP|MPFR?|NaN)\)?[.!?]' doc/mpfr.texi | \ + err-if-output -t "mpfr.texi punctuation" grep -Ev '^(\* .*::|@c )' fdlv1="`sed -n '/Version / { s/.*Version // @@ -164,28 +271,33 @@ fdlv2="`sed -n '/GNU Free Documentation License/ { p q }' doc/mpfr.texi`" -[ "x$fdlv1" = "x$fdlv2" ] || cat <<EOF +[[ $fdlv1 = $fdlv2 ]] || { cat <<EOF && err=1 } GFDL versions differ: fdl.texi: $fdlv1 mpfr.texi: $fdlv2 EOF tools/ck-copyright-notice -which gcc > /dev/null 2> /dev/null && tools/ck-mparam + +if which gcc > /dev/null 2> /dev/null; then + tools/ck-mparam +else + echo "Warning! gcc is not installed. Cannot run ck-mparam." >&2 +fi texisvnd=`LC_ALL=C TZ=UTC svn info doc/mpfr.texi 2> /dev/null | sed -n 's/Last Changed Date:.*, [0-9]* \([A-Z][a-z][a-z] [0-9][0-9][0-9][0-9]\)).*/\1/p'` -if [ $? -eq 0 ] && [ -n "$texisvnd" ]; then +if [[ $? -eq 0 ]] && [[ -n "$texisvnd" ]] then texidate=`sed -n 's/@set UPDATED-MONTH \([A-Z][a-z][a-z]\).*\( [0-9][0-9][0-9][0-9]\)/\1\2/p' doc/mpfr.texi` - [ "$texidate" = "$texisvnd" ] || cat <<EOF + [[ $texidate = $texisvnd ]] || { cat <<EOF && err=1 } mpfr.texi's UPDATED-MONTH seems to be incorrect: mpfr.texi's UPDATED-MONTH: $texidate Last Changed Date in WC: $texisvnd EOF fi -acv1="`sed -n 's/.*autoconf \([0-9.]\+\) (at least).*/\1/p' doc/README.dev`" -acv2="`sed -n 's/AC_PREREQ(\([0-9.]\+\).*/\1/p' acinclude.m4`" -[ "x$acv1" = "x$acv2" ] || cat <<EOF +acv1=`sed -n 's/.*autoconf \([0-9.]\+\) (at least).*/\1/p' doc/README.dev` +acv2=`sed -n 's/AC_PREREQ(\([0-9.]\+\).*/\1/p' acinclude.m4` +[[ $acv1 = $acv2 ]] || { cat <<EOF && err=1 } autoconf minimal versions differ: README.dev: $acv1 acinclude.m4: $acv2 @@ -195,11 +307,18 @@ EOF # In particular, skip the openout.* files, which are created by "make pdf" # (they are in the doc/mpfr.t2p/mpfr.t2d/pdf/check_recorder directory, and # unfortunately codespell can't skip directories such as mpfr.t2p). -which codespell > /dev/null 2> /dev/null && - codespell -q3 -S 'algorithm2e.sty,algorithms.tex,texinfo.tex,openout.*' \ +if which codespell > /dev/null 2> /dev/null; then + err-if-output "codespell" codespell ${term:+--enable-colors} -q3 -S \ + 'algorithm2e.sty,algorithms.tex,texinfo.tex,openout.*' \ AUTHORS BUGS INSTALL NEWS README TODO doc examples $srctests +else + echo "Warning! codespell is not installed. Cannot check spelling." >&2 +fi + +cd $dir +$0:h/check_inits_clears -cd "$dir" -"`dirname -- "$0"`"/check_inits_clears +############################################################################ -true +[[ $err -eq 0 ]] || echo "Terminated with errors." >&2 +exit $err |