summaryrefslogtreecommitdiff
path: root/scripts/tartan
blob: 85b4105fbbe75c446166b0476317fbb65fa878a3 (plain)
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
#!/bin/bash

clang_bin_dir=`dirname "$0"`
clang_prefix="$clang_bin_dir/.."

# Try and find the real Clang executable. $TARTAN_CC trumps everything.
# Otherwise assume clang is in the same directory as this script if they’re both
# installed. Failing that, use a hard-coded Clang path.
if [ "x$TARTAN_CC" != "x" ]; then
	real_clang="$TARTAN_CC"
elif [ "x$GNOME_CLANG_CC" != "x" ]; then
	# Fallback from before project rename
	real_clang="$GNOME_CLANG_CC"
elif [ -f "$clang_prefix/bin/clang" ]; then
	real_clang="$clang_prefix/bin/clang"
elif [ -f `which clang` ]; then
	real_clang=`which clang`
else
	echo "Error: Could not find clang executable. Set TARTAN_CC to the absolute path of the real clang executable." >& 2
	exit 1
fi

# Extract the clang version.
# clang --version returns something like:
#     clang version 3.3 (tags/RELEASE_33/rc3)
#     Target: x86_64-redhat-linux-gnu
#     Thread model: posix
# or:
#     clang version 3.4.2 (http://llvm.org/git/clang.git 65173e04eacb68ff89a58fbff14979eb318896c9) (http://llvm.org/git/llvm.git 5c6aa738fb3325ae499454877f1e2926d2368135)
#     Target: x86_64-unknown-linux-gnu
#     Thread model: posix
# or:
#     Vendor-specific clang version 3.6.2-1bo1 (tags/RELEASE_362/final) (based on LLVM 3.6.2)
#     Target: x86_64-unknown-linux-gnu
#     Thread model: posix
clang_version=`"$real_clang" --version | head -n1 | sed 's/\([^0-9]*\)\([[0-9]]*\)\.\([[0-9]]*\)\(\.[[0-9]]*\)\?\(svn\)\?\(.*\)/\2.\3/'`

# Sanity check.
if [ "$clang_version" == "" ]; then
	echo "Error: Could not extract Clang version from output:" >& 2
	"$real_clang" --version >& 2
	exit 4
fi

# Try and find the Tartan plugin. $TARTAN_PLUGIN trumps everything.
if [ "x$TARTAN_PLUGIN" != "x" ]; then
	plugin_path="$TARTAN_PLUGIN"
elif [ "x$GNOME_CLANG_PLUGIN" != "x" ]; then
	# Fallback from before project rename
	plugin_path="$GNOME_CLANG_PLUGIN"
elif [ -f "$clang_bin_dir/../clang-plugin/.libs/libtartan.so" ]; then
	# Uninstalled, from the source directory.
	plugin_path="$clang_bin_dir/../clang-plugin/.libs/libtartan.so"
elif [ -f "$clang_prefix/lib64/tartan/$clang_version/libtartan.so" ]; then
	# 64-bit installed.
	plugin_path="$clang_prefix/lib64/tartan/$clang_version/libtartan.so"
elif [ -f "$clang_prefix/lib/tartan/$clang_version/libtartan.so" ]; then
	# 32-bit installed.
	plugin_path="$clang_prefix/lib/tartan/$clang_version/libtartan.so"
elif [ ! -d "$clang_prefix/lib/tartan/$clang_version/" ]; then
	echo "Error: Unsupported version of Clang ‘$clang_version’. Recompile Tartan for this version of Clang, set TARTAN_CC to a supported Clang version, or set TARTAN_PLUGIN to a plugin which supports this version." >& 2
	exit 2
else
	echo "Error: Could not find libtartan.so. Set TARTAN_PLUGIN to the absolute path of the Tartan plugin." >& 2
	exit 2
fi

plugin_name=tartan

# Process TARTAN_OPTIONS, prefixing everything with -plugin-arg-tartan to
# ensure Clang passes it to (only) the plugin. Also do GNOME_CLANG_OPTIONS as
# a fallback from before the project rename.
plugin_options=()
for arg in $GNOME_CLANG_OPTIONS; do
	plugin_options+=( "-plugin-arg-$plugin_name" )
	plugin_options+=( "$arg" )
done
for arg in $TARTAN_OPTIONS; do
	plugin_options+=( "-plugin-arg-$plugin_name" )
	plugin_options+=( "$arg" )
done

# Detect whether we’re running under scan-build. If we are, -### --analyze will
# be passed by ccc-analyzer to us. We should append our plugin flags and pass
# through to Clang, which will output a series of commands to be executed to
# perform the analysis, keeping the plugin arguments. Similarly for
# -### -fsyntax-only, except then the arguments do not need escaping as they are
# being passed directly to the compiler.
#
# Another possibility is that the user is trying to run Tartan directly, e.g. as
# `make CC=tartan`. This will work, but no static analysis will be performed.

include_plugin_flags=0
escape_plugin_flags=0
argv=("$@")

containsElement () {
	local e
	for e in "${@:2}"; do [[ "$e" == "$1" ]] && return 0; done
	return 1
}

if containsElement "--analyze" "${argv[@]}"; then
	include_plugin_flags=1
	escape_plugin_flags=1
elif containsElement "-fsyntax-only" "${argv[@]}"; then
	include_plugin_flags=1
	escape_plugin_flags=0
elif containsElement "-analyze" "${argv[@]}"; then
	include_plugin_flags=1
	escape_plugin_flags=0
elif containsElement "--version" "${argv[@]}"; then
	# This is passed to us during ./configure (e.g. tartan-build ./configure)
	# so take the opportunity to output Tartan version information.
	echo "tartan: Running under tartan-build" >& 2
elif ! containsElement "-analyze" "${argv[@]}"; then
	echo "Warning: Running $0 with static analysis disabled. Use tartan-build to enable static analysis." >& 2
fi

# -### mode always requires escaping.
if containsElement "-###" "${argv[@]}"; then
	escape_plugin_flags=1
fi

# Sanity check that (somehow) the Tartan plugin flags have not already made it
# into the argument list. This can happen if both configure and make are run
# under tartan-builder.
if [ "$include_plugin_flags" = "1" ] &&
   containsElement "$plugin_path" "${argv[@]}"; then
	include_plugin_flags=0
fi

# The -analyzer-checker argument loads all 'tartan.*' static checkers.
# The -add-plugin argument loads the tartan AST frontend plugin.
add_plugin=( "-load" "$plugin_path" \
             "-add-plugin" "$plugin_name" \
             "-analyzer-checker" "$plugin_name" )

if [ "$escape_plugin_flags" = "1" ]; then
	_add_plugin=()
	for arg in "${add_plugin[@]}"; do
		_add_plugin+=( "-Xclang" "$arg" )
	done
	for arg in "${add_plugin[@]}"; do
		_add_plugin+=( "-Xanalyzer" "$arg" )
	done
	add_plugin=( "${_add_plugin[@]}" )
fi

if [ "$include_plugin_flags" = "1" ]; then
	# Exec Clang with the plugin loaded.
	if [ "$V" = "1" ]; then
		echo "$real_clang" \
			${argv[@]} \
			${add_plugin[@]} \
			${plugin_options[@]} \
			$GNOME_CLANG_CFLAGS \
			$TARTAN_CFLAGS
	fi

	exec "$real_clang" \
		${argv[@]} \
		${add_plugin[@]} \
		${plugin_options[@]} \
		$GNOME_CLANG_CFLAGS \
		$TARTAN_CFLAGS
else
	# Pass through to Clang with the arguments unchanged.
	if [ "$V" = "1" ]; then
		echo "$real_clang" \
			${argv[@]}
	fi

	exec "$real_clang" \
		${argv[@]}
fi