#!/bin/bash # diag-build: a tool showing enabled warnings in a project. # # diag-build acts as a wrapper for 'diagtool show-enabled', in the same way # that scan-build acts as a wrapper for the static analyzer. The common case is # simple: use 'diag-build make' or 'diag-build xcodebuild' to list the warnings # enabled for the first compilation command we see. Other build systems require # you to manually specify "dry-run" and "use $CC and $CXX"; if there is a build # system you are interested in, please add it to the switch statement. print_usage () { echo 'Usage: diag-build.sh [-v] xcodebuild [flags]' echo ' diag-build.sh [-v] make [flags]' echo ' diag-build.sh [-v] ' echo echo 'diagtool must be in your PATH' echo 'If using an alternate build command, you must ensure that' echo 'the compiler used matches the CC environment variable.' } # Mac OS X's BSD sed uses -E for extended regular expressions, # but GNU sed uses -r. Find out which one this system accepts. EXTENDED_SED_FLAG='-E' echo -n | sed $EXTENDED_SED_FLAG 's/a/b/' 2>/dev/null || EXTENDED_SED_FLAG='-r' if [[ "$1" == "-v" ]]; then verbose=$1 shift fi guessing_cc=0 if [[ -z "$CC" ]]; then guessing_cc=1 if [[ -x $(dirname $0)/clang ]]; then CC=$(dirname $0)/clang elif [[ ! -z $(which clang) ]]; then CC=$(which clang) else echo -n 'Error: could not find an appropriate compiler' echo ' to generate build commands.' 1>&2 echo 'Use the CC environment variable to set one explicitly.' 1>&2 exit 1 fi fi if [[ -z "$CXX" ]]; then if [[ -x $(dirname $0)/clang++ ]]; then CXX=$(dirname $0)/clang++ elif [[ ! -z $(which clang++) ]]; then CXX=$(which clang++) else CXX=$CC fi fi diagtool=$(which diagtool) if [[ -z "$diagtool" ]]; then if [[ -x $(dirname $0)/diagtool ]]; then diagtool=$(dirname $0)/diagtool else echo 'Error: could not find diagtool.' 1>&2 exit 1 fi fi tool=$1 shift if [[ -z "$tool" ]]; then print_usage exit 1 elif [[ "$tool" == "xcodebuild" ]]; then dry_run='-dry-run' set_compiler="CC='$CC' CXX='$CXX'" elif [[ "$tool" == "make" ]]; then dry_run='-n' set_compiler="CC='$CC' CXX='$CXX'" else echo "Warning: unknown build system '$tool'" 1>&2 if [[ $guessing_cc -eq 1 ]]; then # FIXME: We really only need $CC /or/ $CXX echo 'Error: $CC must be set for other build systems' 1>&2 exit 1 fi fi escape () { echo $@ | sed 's:[]:\\|/.+*?^$(){}[]:\\&:g' } escCC=$(escape $CC) escCXX=$(escape $CXX) command=$( eval $tool $dry_run $set_compiler $@ 2>/dev/null | # Remove "if" early on so we can find the right command line. sed $EXTENDED_SED_FLAG "s:^[[:blank:]]*if[[:blank:]]{1,}::g" | # Combine lines with trailing backslashes sed -e :a -e '/\\$/N; s/\\\n//; ta' | grep -E "^[[:blank:]]*($escCC|$escCXX)" | head -n1 | sed $EXTENDED_SED_FLAG "s:($escCC|$escCXX):${diagtool//:/\\:} show-enabled:g" ) if [[ -z "$command" ]]; then echo 'Error: could not find any build commands.' 1>&2 if [[ "$tool" != "xcodebuild" ]]; then # xcodebuild always echoes the compile commands on their own line, # but other tools give no such guarantees. echo -n 'This may occur if your build system embeds the call to ' 2>&1 echo -n 'the compiler in a larger expression. ' 2>&1 fi exit 2 fi # Chop off trailing '&&', '||', and ';' command=${command%%&&*} command=${command%%||*} command=${command%%;*} [[ -n "$verbose" ]] && echo $command eval $command