| 1 | #!/bin/bash |
| 2 | |
| 3 | # diag-build: a tool showing enabled warnings in a project. |
| 4 | # |
| 5 | # diag-build acts as a wrapper for 'diagtool show-enabled', in the same way |
| 6 | # that scan-build acts as a wrapper for the static analyzer. The common case is |
| 7 | # simple: use 'diag-build make' or 'diag-build xcodebuild' to list the warnings |
| 8 | # enabled for the first compilation command we see. Other build systems require |
| 9 | # you to manually specify "dry-run" and "use $CC and $CXX"; if there is a build |
| 10 | # system you are interested in, please add it to the switch statement. |
| 11 | |
| 12 | print_usage () { |
| 13 | echo 'Usage: diag-build.sh [-v] xcodebuild [flags]' |
| 14 | echo ' diag-build.sh [-v] make [flags]' |
| 15 | echo ' diag-build.sh [-v] <other build command>' |
| 16 | echo |
| 17 | echo 'diagtool must be in your PATH' |
| 18 | echo 'If using an alternate build command, you must ensure that' |
| 19 | echo 'the compiler used matches the CC environment variable.' |
| 20 | } |
| 21 | |
| 22 | # Mac OS X's BSD sed uses -E for extended regular expressions, |
| 23 | # but GNU sed uses -r. Find out which one this system accepts. |
| 24 | EXTENDED_SED_FLAG='-E' |
| 25 | echo -n | sed $EXTENDED_SED_FLAG 's/a/b/' 2>/dev/null || EXTENDED_SED_FLAG='-r' |
| 26 | |
| 27 | if [[ "$1" == "-v" ]]; then |
| 28 | verbose=$1 |
| 29 | shift |
| 30 | fi |
| 31 | |
| 32 | guessing_cc=0 |
| 33 | |
| 34 | if [[ -z "$CC" ]]; then |
| 35 | guessing_cc=1 |
| 36 | if [[ -x $(dirname $0)/clang ]]; then |
| 37 | CC=$(dirname $0)/clang |
| 38 | elif [[ ! -z $(which clang) ]]; then |
| 39 | CC=$(which clang) |
| 40 | else |
| 41 | echo -n 'Error: could not find an appropriate compiler' |
| 42 | echo ' to generate build commands.' 1>&2 |
| 43 | echo 'Use the CC environment variable to set one explicitly.' 1>&2 |
| 44 | exit 1 |
| 45 | fi |
| 46 | fi |
| 47 | |
| 48 | if [[ -z "$CXX" ]]; then |
| 49 | if [[ -x $(dirname $0)/clang++ ]]; then |
| 50 | CXX=$(dirname $0)/clang++ |
| 51 | elif [[ ! -z $(which clang++) ]]; then |
| 52 | CXX=$(which clang++) |
| 53 | else |
| 54 | CXX=$CC |
| 55 | fi |
| 56 | fi |
| 57 | |
| 58 | diagtool=$(which diagtool) |
| 59 | if [[ -z "$diagtool" ]]; then |
| 60 | if [[ -x $(dirname $0)/diagtool ]]; then |
| 61 | diagtool=$(dirname $0)/diagtool |
| 62 | else |
| 63 | echo 'Error: could not find diagtool.' 1>&2 |
| 64 | exit 1 |
| 65 | fi |
| 66 | fi |
| 67 | |
| 68 | |
| 69 | tool=$1 |
| 70 | shift |
| 71 | |
| 72 | if [[ -z "$tool" ]]; then |
| 73 | print_usage |
| 74 | exit 1 |
| 75 | elif [[ "$tool" == "xcodebuild" ]]; then |
| 76 | dry_run='-dry-run' |
| 77 | set_compiler="CC='$CC' CXX='$CXX'" |
| 78 | elif [[ "$tool" == "make" ]]; then |
| 79 | dry_run='-n' |
| 80 | set_compiler="CC='$CC' CXX='$CXX'" |
| 81 | else |
| 82 | echo "Warning: unknown build system '$tool'" 1>&2 |
| 83 | if [[ $guessing_cc -eq 1 ]]; then |
| 84 | # FIXME: We really only need $CC /or/ $CXX |
| 85 | echo 'Error: $CC must be set for other build systems' 1>&2 |
| 86 | exit 1 |
| 87 | fi |
| 88 | fi |
| 89 | |
| 90 | escape () { |
| 91 | echo $@ | sed 's:[]:\\|/.+*?^$(){}[]:\\&:g' |
| 92 | } |
| 93 | |
| 94 | escCC=$(escape $CC) |
| 95 | escCXX=$(escape $CXX) |
| 96 | command=$( |
| 97 | eval $tool $dry_run $set_compiler $@ 2>/dev/null | |
| 98 | # Remove "if" early on so we can find the right command line. |
| 99 | sed $EXTENDED_SED_FLAG "s:^[[:blank:]]*if[[:blank:]]{1,}::g" | |
| 100 | # Combine lines with trailing backslashes |
| 101 | sed -e :a -e '/\\$/N; s/\\\n//; ta' | |
| 102 | grep -E "^[[:blank:]]*($escCC|$escCXX)" | |
| 103 | head -n1 | |
| 104 | sed $EXTENDED_SED_FLAG "s:($escCC|$escCXX):${diagtool//:/\\:} show-enabled:g" |
| 105 | ) |
| 106 | |
| 107 | if [[ -z "$command" ]]; then |
| 108 | echo 'Error: could not find any build commands.' 1>&2 |
| 109 | if [[ "$tool" != "xcodebuild" ]]; then |
| 110 | # xcodebuild always echoes the compile commands on their own line, |
| 111 | # but other tools give no such guarantees. |
| 112 | echo -n 'This may occur if your build system embeds the call to ' 2>&1 |
| 113 | echo -n 'the compiler in a larger expression. ' 2>&1 |
| 114 | fi |
| 115 | exit 2 |
| 116 | fi |
| 117 | |
| 118 | # Chop off trailing '&&', '||', and ';' |
| 119 | command=${command%%&&*} |
| 120 | command=${command%%||*} |
| 121 | command=${command%%;*} |
| 122 | |
| 123 | [[ -n "$verbose" ]] && echo $command |
| 124 | eval $command |
| 125 | |