1#!/bin/bash 2# -*- indent-tabs-mode:nil; -*- 3 4 5# Process doxygen log to generate sorted list of top offenders. 6# 7 8me=$(basename $0) 9DIR="$(dirname $0)" 10# Trick to get the absolute path, since doxygen prefixes errors that way 11ROOT=$(cd "$DIR/.."; pwd -P) 12 13# Known log files 14STANDARDLOGFILE=doxygen.log 15WARNINGSLOGFILE=doxygen.warnings.log 16# Default choice: generate it 17LOG="$DIR/$WARNINGSLOGFILE" 18 19# Verbose log 20VERBLOG="$DIR/doxygen.verbose.log" 21 22 23# Options ------------------------------ 24# 25 26# One line sysnopsis, continue 27function synopsis_short 28{ 29 echo "Usage: $me [-beithv] [-s <log-file> | -l | -w] [-m <module>] [-f <regex>] [-F <regex>]" 30} 31 32# Two line synopsis, then exit with error 33function synopsis 34{ 35 synopsis_short 36 echo " -h For detailed usage" 37 exit 1 38} 39 40# Full help, then exit with no error 41function usage 42{ 43 synopsis_short 44 cat <<-EOF 45 46 Run doxygen to generate all errors; report error counts 47 by module and file. 48 49 -i Skip the build, try print-introspected-doxygen anyway. 50 51 -s Skip doxygen run; use existing <log-file>. 52 -w Skip doxygen run; use existing warnings log doc/$WARNINGSLOGFILE 53 -l Skip doxygen run; use the normal doxygen log doc/$STANDARDLOGFILE 54 55 -b Omit the blacklist filter of files whose warnings we ignore 56 -e Filter out warnings from */examples/* 57 -t Filter out warnings from */test/* 58 -m Only include files matching src/<module> 59 -f Only include files matching the <regex> 60 -F Exclude files matching the <regex> 61 62 -v Show detailed output from each step. 63 -h Print this usage message 64 65 The default behavior is to modify doxygen.conf temporarily to 66 report all undocumented elements, and to reduce the run time. 67 The output of this special run is kept in doc/$WARNINGSLOGFILE. 68 To further reduce the run time, the -i option also skips 69 print-introspected-doxygen, so waf doesn\'t have to compile 70 any modified files at all. 71 72 The -f, -l, and -s options skip the doxygen run altogether. 73 The first two use a specified or the standard log file; 74 the -s option uses the warnings log from a prior run. 75 Only the first of -f <log-file>, -s, or -l will have effect. 76 77 The -e and -t options exclude examples and test directories 78 from the counts. The -m option only includes a specific module. 79 The -F option only includes files (or warnings) matching the <regex>. 80 The -m and -F options append the relevant warnings after the 81 numerical report. These can be used in any combination. 82 83EOF 84 exit 0 85} 86 87# Messaging ---------------------------- 88# 89 90# Arg -v Verbosity level 91verbosity=0 92 93function verbose 94{ 95 if [ "$1" == "-n" ]; then 96 if [ $verbosity -eq 1 ]; then 97 echo "$me: ${2}..." 98 else 99 echo -n "${2}..." 100 fi 101 elif [ $verbosity -eq 1 ]; then 102 echo "$me: $1 $2" 103 else 104 echo "$2" 105 fi 106} 107 108# Use file handle 6 for verbose output 109rm -f $VERBLOG 110exec 6>$VERBLOG 111 112function status_report 113{ 114 local status="$1" 115 local long_msg="$2" 116 local exitonerr="${3:-yes}" 117 if [ $status -eq 0 ]; then 118 [[ $verbosity -eq 1 && -e $VERBLOG ]] && cat $VERBLOG 119 verbose "$long_msg " "done." 120 [[ -e $VERBLOG ]] && rm -f $VERBLOG 121 else 122 if [ $exitonerr == "yes" ]; then 123 verbose "$long_msg " "FAILED. Details:" 124 [[ -e $VERBLOG ]] && cat $VERBLOG && rm -f $VERBLOG 125 exit 1 126 else 127 verbose "$long_msg " "FAILED, continuing" 128 [[ -e $VERBLOG ]] && cat $VERBLOG && rm -f $VERBLOG 129 fi 130 fi 131} 132 133 134# Argument processing ------------------ 135# 136 137# -f argument 138use_filearg=0 139logfile_arg= 140# -l 141use_standard=0 142# skip doxygen run; using existing log file 143skip_doxy=0 144# skip print-introspected-doxygen, avoiding a build 145skip_intro=0 146 147# Filtering flags 148filter_blacklist=1 149filter_examples=0 150filter_test=0 151explicit_m_option=0 152filter_module="" 153explicit_f_option=0 154filter_in="" 155filter_out="" 156 157while getopts :bef:F:hilm:s:tvw option ; do 158 159 case $option in 160 (b) filter_blacklist=0 ;; 161 162 (e) filter_examples=1 ;; 163 164 (f) filter_in="$OPTARG" 165 explicit_f_option=1 166 ;; 167 168 (F) filter_out="$OPTARG" ;; 169 170 (h) usage ;; 171 172 (i) skip_intro=1 ;; 173 174 (l) use_standard=1 ;; 175 176 (m) filter_module="$OPTARG" 177 explicit_m_option=1 178 ;; 179 180 (s) use_filearg=1 181 logfile_arg="$OPTARG" 182 ;; 183 184 (t) filter_test=1 ;; 185 186 (v) verbosity=1 187 exec 6>&1 188 ;; 189 190 (w) use_filearg=1 191 logfile_arg="$DIR/$WARNINGSLOGFILE" 192 ;; 193 194 (:) echo "$me: Missing argument to -$OPTARG" ; synopsis ;; 195 196 (\?) echo "$me: Invalid option: -$OPTARG" ; synopsis ;; 197 198 esac 199done 200 201function checklogfile 202{ 203 if [ -e "$1" ] ; then 204 skip_doxy=1 205 LOG="$1" 206 else 207 echo "$me: log file $1 does not exist." 208 synopsis 209 fi 210} 211 212# Log file ----------------------------- 213# 214 215if [[ $use_filearg -eq 1 && "${logfile_arg:-}" != "" ]] ; then 216 checklogfile "$logfile_arg" 217elif [ $use_standard -eq 1 ]; then 218 checklogfile "$DIR/$STANDARDLOGFILE" 219fi 220 221# Log filters -------------------------- 222# 223 224# Append a regular expression to a parameter 225# with '\|' alternation operator if the parameter wasn't empty to begin with. 226function REappend 227{ 228 local param="$1" 229 local token="$2" 230 231 eval "${param}=\"${!param:-}${!param:+\\|}$token\"" 232} 233 234# Explicit -f or -m with empty args should filter out all, not pass all 235[[ $explicit_f_option -eq 1 && "${filter_in:-}" == "" ]] && filter_out=".*" 236[[ $explicit_m_option -eq 1 && "${filter_module:-}" == "" ]] && filter_out=".*" 237 238# Filter in regular expression for -m and -f 239filter_inRE="" 240[[ "$filter_module" != "" ]] && REappend filter_inRE src/$filter_module 241[[ "$filter_in" != "" ]] && REappend filter_inRE "$filter_in" 242 243# Blacklist filter of files whose warnings we ignore 244filter_blacklistRE="" 245 246# External files: adding our own doxygen makes diffs with upstream very hard 247# cairo-wideint 248REappend filter_blacklistRE "cairo-wideint" 249 250# Functions with varying numbers of arguments 251# This is temporary until we move to C++-14 252REappend filter_blacklistRE "Schedule(Time" 253REappend filter_blacklistRE "ScheduleWithContext(uint32_t" 254REappend filter_blacklistRE "Schedule\\(Now\\|Destroy\\)(\\(MEM\\|void\\)" 255 256# ATTRIBUTE_HELPER_CPP( and _HEADER( 257REappend filter_blacklistRE "ATTRIBUTE_HELPER_\\(CPP\\|HEADER\\)" 258 259# Filter out regular expression for black list, -e, -t and -F 260filter_outRE="" 261[[ $filter_blacklist -eq 1 ]] && REappend filter_outRE "$filter_blacklistRE" 262[[ $filter_examples -eq 1 ]] && REappend filter_outRE "/examples/" 263[[ $filter_test -eq 1 ]] && REappend filter_outRE "/test/" 264[[ "$filter_out" != "" ]] && REappend filter_outRE "$filter_out" 265 266 267# Configuration ------------------------ 268# 269 270function on_off 271{ 272 if [[ "${!1:-}" != "" && "${!1}" != "0" ]] ; then 273 echo "ON" 274 else 275 echo "off" 276 fi 277} 278 279if [ $verbosity -eq 1 ]; then 280 echo 281 echo "$me:" 282 echo " Verbose: $(on_off verbosity)" 283 echo " Skip build: $(on_off skip_intro)" 284 echo " Log file to use: $LOG" 285 echo " Module filter: $(on_off filter_module) $filter_module" 286 echo " Examples filter: $(on_off filter_examples)" 287 echo " Tests filter: $(on_off filter_test)" 288 echo " Blacklist filter: $(on_off filter_blacklist)" 289 echo " Filter in: $(on_off filter_in) $filter_in" 290 echo " Filter out: $(on_off filter_out) $filter_out" 291 echo 292 293 # Show the resulting filters here, in addition to below 294 echo " Net result of all filters:" 295 [[ "${filter_inRE:-}" != "" ]] && echo " Filtering in: \"$filter_inRE\"" 296 [[ "${filter_outRE:-}" != "" ]] && echo " Filtering out: \"$filter_outRE\"" 297 298 echo 299fi 300 301 302# Run doxygen ------------------------- 303# 304 305if [ $skip_doxy -eq 1 ]; then 306 echo 307 echo "Skipping doxygen run, using existing log file $LOG" 308 309else 310 311 # Modify doxygen.conf to generate all the warnings 312 # (We also suppress dot graphs, so shorten the run time.) 313 314 conf=doc/doxygen.conf 315 cp $conf ${conf}.bak 316 cat <<-EOF >> $conf 317 318 # doxygen.warnings.report.sh: 319 EXTRACT_ALL = no 320 HAVE_DOT = no 321 CLASS_DIAGRAMS = no 322 WARNINGS = no 323 SOURCE_BROWSER = no 324 HTML_OUTPUT html-warn 325 WARN_LOGFILE = doc/$WARNINGSLOGFILE 326EOF 327 328 329 intro_h="introspected-doxygen.h" 330 if [ $skip_intro -eq 1 ]; then 331 verbose "" "Skipping ./waf build" 332 verbose -n "Trying print-introspected-doxygen with doxygen build" 333 (cd "$ROOT" && ./waf --run-no-build print-introspected-doxygen >doc/$intro_h 2>&6 ) 334 status_report $? "./waf --run print-introspected-doxygen" noexit 335 else 336 # Run introspection, which may require a build 337 verbose -n "Building" 338 (cd "$ROOT" && ./waf build >&6 2>&6 ) 339 status_report $? "./waf build" 340 verbose -n "Running print-introspected-doxygen with doxygen build" 341 (cd "$ROOT" && ./waf --run-no-build print-introspected-doxygen >doc/$intro_h 2>&6 ) 342 status_report $? "./waf --run print-introspected-doxygen" 343 fi 344 345 # Waf insists on writing cruft to stdout 346 sed -i.bak -E '/^Waf:/d' doc/$intro_h 347 rm doc/$intro_h.bak 348 349 verbose -n "Rebuilding doxygen docs with full errors" 350 (cd "$ROOT" && ./waf --doxygen-no-build >&6 2>&6 ) 351 status_report $? "./waf --doxygen-no-build" 352 353 # Swap back to original config 354 rm -f $conf 355 mv -f $conf.bak $conf 356fi 357 358# Filter log file 359function filter_log 360{ 361 local flog; 362 flog=$( cat "$LOG" | grep "^$ROOT" ) 363 364 [[ "${filter_inRE:-}" != "" ]] && flog=$( echo "$flog" | grep "$filter_inRE" ) 365 [[ "${filter_outRE:-}" != "" ]] && flog=$( echo "$flog" | grep -v "$filter_outRE" ) 366 367 flog=$( \ 368 echo "$flog" | \ 369 sort -t ':' -k1,1 -k2,2n | \ 370 uniq \ 371 ) 372 373 echo "$flog" 374} 375 376# Analyze the log ---------------------- 377# 378# Show the resulting filters 379echo 380echo "Net result of all filters:" 381[[ "${filter_inRE:-}" != "" ]] && echo "Filtering in \"$filter_inRE\"" 382[[ "${filter_outRE:-}" != "" ]] && echo "Filtering out \"$filter_outRE\"" 383 384verbose -n "Filtering the doxygen log" 385 386# List of module directories (e.g, "src/core/model") 387undocmods=$( \ 388 filter_log | \ 389 cut -d ':' -f 1 | \ 390 sed "s|$ROOT/||g" | \ 391 cut -d '/' -f 1-3 | \ 392 sort | \ 393 uniq -c | \ 394 sort -nr \ 395 ) 396 397# Number of directories 398modcount=$( \ 399 echo "$undocmods" | \ 400 wc -l | \ 401 sed 's/^[ \t]*//;s/[ \t]*$//' \ 402 ) 403 404# For a function with multiple undocumented parameters, 405# Doxygen prints the additional parameters on separate lines, 406# so they don't show up in the totals above. 407# Rather than work too hard to get the exact number for each file, 408# we just list the total here. 409addlparam=$( \ 410 grep "^ parameter '" "$LOG" | \ 411 wc -l | \ 412 sed 's/^[ \t]*//;s/[ \t]*$//' \ 413 ) 414 415# Total number of warnings 416warncount=$( \ 417 echo "$undocmods" | \ 418 awk '{total += $1}; END {print total}' \ 419 ) 420warncount=$((warncount + addlparam)) 421 422# List of files appearing in the log 423undocfiles=$( \ 424 filter_log | \ 425 cut -d ':' -f 1 | \ 426 sed "s|$ROOT||g" | \ 427 cut -d '/' -f 2- | \ 428 sort | \ 429 uniq -c | \ 430 sort -k 2 \ 431 ) 432 433# Sorted by number, decreasing 434undocsort=$(echo "$undocfiles" | sort -k1nr,2 ) 435 436# Total number of files 437filecount=$( \ 438 echo "$undocfiles" | \ 439 wc -l | \ 440 sed 's/^[ \t]*//;s/[ \t]*$//' \ 441 ) 442 443# Filtered in warnings 444filterin= 445if [ "${filter_inRE:-}" != "" ] ; then 446 filterin=$( \ 447 filter_log | \ 448 sed "s|$ROOT/||g" \ 449 ) 450fi 451 452status_report 0 "Filter" 453echo 454 455 456# Summarize the log -------------------- 457# 458 459echo 460echo "Report of Doxygen warnings" 461echo "----------------------------------------" 462echo 463echo "(All counts are lower bounds.)" 464echo 465echo "Warnings by module/directory:" 466echo 467echo "Count Directory" 468echo "----- ----------------------------------" 469echo "$undocmods" 470echo " $addlparam additional undocumented parameters." 471echo "----------------------------------------" 472printf "%6d total warnings\n" $warncount 473printf "%6d directories with warnings\n" $modcount 474echo 475echo 476echo "Warnings by file (alphabetical)" 477echo 478echo "Count File" 479echo "----- ----------------------------------" 480echo "$undocfiles" 481echo "----------------------------------------" 482printf "%6d files with warnings\n" $filecount 483echo 484echo 485echo "Warnings by file (numerical)" 486echo 487echo "Count File" 488echo "----- ----------------------------------" 489echo "$undocsort" 490echo "----------------------------------------" 491printf "%6d files with warnings\n" $filecount 492echo 493echo 494echo "Doxygen Warnings Summary" 495echo "----------------------------------------" 496printf "%6d directories\n" $modcount 497printf "%6d files\n" $filecount 498printf "%6d warnings\n" $warncount 499 500# Return status based on warnings 501exit_status=$((warncount > 0)) 502 503# if [ "${filter_inRE:-}" != "" ] ; then 504# if [ "$filterin" != "" ] ; then 505# echo 506# echo 507# echo "Filtered Warnings" 508# echo "========================================" 509# echo "$filterin" 510# exit_status=1 511# else 512# exit_status=0 513# fi 514# fi 515 516if [ "$filterin" != "" ] ; then 517 echo 518 echo 519 echo "Filtered Warnings" 520 echo "========================================" 521 echo "$filterin" 522 exit_status=1 523else 524 exit_status=0 525fi 526 527status_report 0 $me 528 529exit $exit_status 530