1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225
|
#! /bin/sh
#
# Detects style errors in the C++ source code. Run this from the top directory
# (which has the subdirectories build, src and utils).
#
# The output may include colour codes if and only if standard output is a
# terminal.
#
# Several check commands are used:
# * grep with a set of regular expressions
# * whitespace_checker (with detect_spurious_indentation.py as an inferior
# fallback)
# * krazy2 with a selected set of checks
#
# Results are cached (there are separate caches for colour/nocolour). The
# check commands do not write directly to a file at the cahce file location
# because that could leave behind an incomplete cache file that is newer than
# the source file in case that the check commands are interrupted. Instead
# write the output from the check commands to a temporary file and when that
# file is complete, move it to the cache file location.
#
# Instead of storing empty cache files for source files that do not give any
# errors, a per-directory timestamp file is stored. This drastically reduces
# the number of cache files. The drawback is that if the script is interrupted
# whilst checking a sequence of files with equal mtime, it will have to start
# over with that sequence the next time it is executed. But reducing the
# number of cache files is worth that minor inconvenience.
#
# NOTE
# This script uses [ FILE1 -nt FILE2 ] to test if FILE1 is newer than FILE2.
# But it behaves differently in different shells. Bash behaves as the info
# page for the test program from GNU Coreutils explains:
# `FILE1 -nt FILE2'
# True if FILE1 is newer (according to modification date) than
# FILE2, or if FILE1 exists and FILE2 does not.
#
# But zsh and dash behave differently in the case when FILE1 exits and FILE2
# does not. Therefore the FILE1 -nt FILE2 tests in this script are followed by
# || [ ! -e FILE2 ].
#
# So if you are reading this script, only know bash and wonder about the
# reduanancy; this is the reason for it.
CHECKER=utils/spurious_source_code/whitespace_checker
if [ ! -f $CHECKER ]; then
echo "WARNING: $CHECKER does not exist!"
echo "WARNING: Go to utils/spurious_source_code and build it with make!"
echo "WARNING: Else many checks will be done much slower or not at all!"
unset CHECKER
elif [ $CHECKER.adb -nt $CHECKER ]; then
echo "ERROR: $CHECKER is out of date!!!"
echo "ERROR: Go to utils/spurious_source_code and rebuild it with make!"
exit
fi
KRAZY2=$(which krazy2 2>/dev/null)
if [ -z $KRAZY2 ]; then
KRAZY2=/usr/local/Krazy2/bin/krazy2
if [ ! -f $KRAZY2 ]; then
echo "WARNING: krazy2 does not exist!"
echo "WARNING: Get it from:"
echo "WARNING: svn://anonsvn.kde.org/home/kde/trunk/quality/krazy2"
echo "WARNING: Else you will submit code with i. a. spelling errors!"
unset KRAZY2
fi
fi
# Which C++ checks from krazy2 should we use?
# captruefalse: use (fallback: regexps/using_macro_TRUE_FALSE)
# constref: undecided
# copyright: use
# cpp: undecided (complains about __APPLE__ and __GNUC__)
# doublequote_chars: only for Qt
# doxytags: undecided
# dpointer: only for shared libraries with binary compatibility reqs
# emptystrcompare: only for Qt
# endswithnewline: taken care of by whitespace_checker
# explicit: use
# foreach: undecided
# i18ncheckarg: only for Qt
# iconnames: only for KDE
# includes: use
# inline: use
# license: use
# nullstrassign: only for Qt
# nullstrcompare: only for Qt
# operators: use
# passbyvalue: use
# postfixop: use
# qbytearray: only for Qt
# qclasses: only for Qt
# qconnect: only for Qt
# qmethods: only for Qt
# qminmax: only for Qt
# qobject: only for Qt
# sigsandslots: only for Qt
# spelling: use
# strings: use
# syscalls: only for Qt
# typedefs: only for Qt
# camelcase: undecided
# contractions: use
# defines: undecided
# kdebug: only for KDE
# multiclasses: undecided
# null: use
# qenums: only for Qt
# style: gives false positives
# tipsandthis: only for Qt
KRAZY2CHECKS=captruefalse,copyright,explicit,includes,inline,license,operators,passbyvalue,postfixop,spelling,strings,contractions,null
# Make sure that several instances of this script running concurrently each
# have a separate temporary directory.
TMP_DIR=/tmp/spurious_source_code_detect.$PPID
rm -fr $TMP_DIR
mkdir $TMP_DIR || { echo "ERROR: could not create $TMP_DIR"; exit; }
REGEXPS_FILE=$TMP_DIR/regexps
for r in utils/spurious_source_code/regexps/*; do
if
[ -d $r ] &&
[ ! -f $r/disabled ] &&
( [ ! $CHECKER ] || [ ! -f $r/redundant_with_whitespace_checker ] ) &&
( [ ! $KRAZY2 ] || [ ! -f $r/redundant_with_krazy2 ] )
then
cat $r/regexps >> $REGEXPS_FILE
fi
done
[ -t 1 ] && COLOUR=.colourized || unset COLOUR
RESULT_FILE=$TMP_DIR/result
# Runs all checks on single source file. The result is written to
# $RESULT_FILE.
#
# Requirements on the result:
# * Error message lines must begin with <filename>:<linenumber> so that they
# can be parsed by tools like KDevelop.
# * Any colour codes produced by commands like "grep --colour" must be
# included if and only if $COLOUR is set.
#
# Parameters:
# $1
# The name of the source file to be checked.
check () {
egrep --with-filename --line-number $([ $COLOUR ] && echo --colour=always) -f $REGEXPS_FILE $1 > $RESULT_FILE
if [ $CHECKER ]; then
$CHECKER $1 >> $RESULT_FILE
else
utils/detect_spurious_indentation.py $1 >> $RESULT_FILE
fi
[ $KRAZY2 ] && $KRAZY2 --brief --export=textedit --check=$KRAZY2CHECKS $1 >> $RESULT_FILE
}
CACHE_DIR="build/stylecheck"
# Detects errors recursively and stores the result in a cache. The cache
# content (if any) is echoed to standard output.
#
# If a cached entry exists and is not older than the source file, no checks
# are done on that source file.
#
# Calls itself recursively for each subdirectory. Then checks all the source
# files in the current directory that are newer than the timestamp file there.
# Files are checked in mtime order, oldest first. After checking group of
# files that have the same mtime, the timestamp is is set to that mtime.
#
# Parameters:
# $1
# The name of the directory to start the search and check in.
detect () {
for d in $1/*; do
[ -d $d ] && detect $d
done
TIMESTAMP=$CACHE_DIR/$1/timestamp
PREVIOUS_FILE=$1
FILES=$(find $1 -maxdepth 1 -name "*.h" -or -name "*.cc")
if [ -n "$FILES" ]; then
FILES=$(ls -tr $FILES)
mkdir -p $CACHE_DIR/$1
for f in $(echo $FILES); do
CACHE_BASE=$CACHE_DIR/$f.stylecheck
CACHE_COLOUR=$CACHE_BASE.colourized
CACHE=$CACHE_BASE$COLOUR
if
[ $f -nt $TIMESTAMP ] || [ ! -e $TIMESTAMP ] ||
[ -s $CACHE_BASE ] || [ -s $CACHE_COLOUR ]
then
if [ $f -nt $CACHE ] || [ ! -e $CACHE ]; then
echo "Checking for errors in $f ..."
check $f
if [ -s $RESULT_FILE ]; then
mv $RESULT_FILE $CACHE
cat $CACHE
else
rm -f $CACHE_BASE $CACHE_COLOUR
fi
if
[ $f -nt $PREVIOUS_FILE ] &&
( [ $PREVIOUS_FILE -nt $TIMESTAMP ] || [ ! -e $TIMESTAMP ] )
then
touch --reference=$PREVIOUS_FILE $TIMESTAMP
fi
else
echo "Showing cached errors in $f ..."
cat $CACHE
fi
fi
PREVIOUS_FILE=$f
done
if [ $PREVIOUS_FILE -nt $TIMESTAMP ] || [ ! -e $TIMESTAMP ]; then
touch --reference=$PREVIOUS_FILE $TIMESTAMP
fi
fi
}
detect src
rm -fr $TMP_DIR
|