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
12print_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.
24EXTENDED_SED_FLAG='-E'
25echo -n | sed $EXTENDED_SED_FLAG 's/a/b/' 2>/dev/null || EXTENDED_SED_FLAG='-r'
26
27if [[ "$1" == "-v" ]]; then
28    verbose=$1
29    shift
30fi
31
32guessing_cc=0
33
34if [[ -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
46fi
47
48if [[ -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
56fi
57
58diagtool=$(which diagtool)
59if [[ -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
66fi
67
68
69tool=$1
70shift
71
72if [[ -z "$tool" ]]; then
73    print_usage
74    exit 1
75elif [[ "$tool" == "xcodebuild" ]]; then
76    dry_run='-dry-run'
77    set_compiler="CC='$CC' CXX='$CXX'"
78elif [[ "$tool" == "make" ]]; then
79    dry_run='-n'
80    set_compiler="CC='$CC' CXX='$CXX'"
81else
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
88fi
89
90escape () {
91    echo $@ | sed 's:[]:\\|/.+*?^$(){}[]:\\&:g'
92}
93
94escCC=$(escape $CC)
95escCXX=$(escape $CXX)
96command=$(
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
107if [[ -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
116fi
117
118# Chop off trailing '&&', '||', and ';'
119command=${command%%&&*}
120command=${command%%||*}
121command=${command%%;*}
122
123[[ -n "$verbose" ]] && echo $command
124eval $command
125