blob: 50bd82d69d4351c7d4e5fd0d1b434ba544124600 [file] [log] [blame]
Ted Kremenekb0982882008-03-25 22:35:32 +00001#!/usr/bin/env python
2#
3# The LLVM Compiler Infrastructure
4#
5# This file is distributed under the University of Illinois Open Source
6# License. See LICENSE.TXT for details.
7#
8##===----------------------------------------------------------------------===##
9#
10# A reduced version of the 'ccc' script that is designed to handle off
11# actual compilation to gcc, but run the code passed to gcc through the
12# static analyzer.
13#
14##===----------------------------------------------------------------------===##
15
16import sys
17import subprocess
18import os
19
20def error(message):
Ted Kremenek61cd9882008-05-24 15:58:54 +000021 print >> sys.stderr, 'ccc: ' + message
22 sys.exit(1)
Ted Kremenekb0982882008-03-25 22:35:32 +000023
Seo Sanghyeond3894652008-04-04 11:02:21 +000024def quote(arg):
Ted Kremenek61cd9882008-05-24 15:58:54 +000025 if '"' in arg:
26 return repr(arg)
27 return arg
Seo Sanghyeond3894652008-04-04 11:02:21 +000028
Ted Kremenekb0982882008-03-25 22:35:32 +000029def run(args):
Ted Kremenek61cd9882008-05-24 15:58:54 +000030 code = subprocess.call(args)
31 if code > 255:
32 code = 1
33 if code:
34 sys.exit(code)
Ted Kremenekb0982882008-03-25 22:35:32 +000035
Ted Kremenekfe873542008-04-21 21:58:05 +000036def compile(args):
Ted Kremenekb0982882008-03-25 22:35:32 +000037 command = 'gcc'.split()
38 run(command + args)
39
40def remove_pch_extension(path):
Ted Kremenek61cd9882008-05-24 15:58:54 +000041 i = path.rfind('.gch')
42 if i < 0:
43 return path
44 return path[:i]
Ted Kremenekb0982882008-03-25 22:35:32 +000045
Ted Kremenek1262fc42008-05-14 20:10:33 +000046def analyze(clang, args,language,output,files,verbose,htmldir,file,analysis_type):
Ted Kremenek61cd9882008-05-24 15:58:54 +000047 if language.find("c++") > 0:
48 return
49
50 print_args = []
Ted Kremenekb0982882008-03-25 22:35:32 +000051
Ted Kremenek61cd9882008-05-24 15:58:54 +000052 if verbose:
53 # We MUST print to stderr. Some clients use the stdout output of
54 # gcc for various purposes.
55 print >> sys.stderr, ' '.join(['\n[LOCATION]:', os.getcwd(), '\n' ])
56 i = 0
57 while i < len(args):
58 print_args.append(''.join([ '\'', args[i], '\'' ]))
59 i += 1
Ted Kremeneka9525c92008-05-12 22:07:14 +000060
Ted Kremenek61cd9882008-05-24 15:58:54 +000061 RunAnalyzer = 0;
Ted Kremeneka9525c92008-05-12 22:07:14 +000062
Ted Kremenek61cd9882008-05-24 15:58:54 +000063 if language.find("header") > 0:
64 target = remove_pch_extension(output)
65 command = 'cp'.split()
66 args = command + files + target.split()
67 else:
68 command = clang.split() + analysis_type.split()
69 args = command + args;
70 RunAnalyzer = 1
Ted Kremeneka9525c92008-05-12 22:07:14 +000071
Ted Kremenek61cd9882008-05-24 15:58:54 +000072 if verbose == 2:
73 print >> sys.stderr, '#SHELL (cd ' + os.getcwd() + ' && ' + ' '.join(command + print_args) + ')\n'
Ted Kremeneka9525c92008-05-12 22:07:14 +000074
Ted Kremenek61cd9882008-05-24 15:58:54 +000075 if RunAnalyzer and htmldir is not None:
76 args.append('-o')
77 print_args.append('-o')
78 args.append(htmldir)
79 print_args.append(htmldir)
80
81 if verbose == 1:
82 # We MUST print to stderr. Some clients use the stdout output of
83 # gcc for various purposes.
84 print >> sys.stderr, ' '.join(command+print_args)
85 print >> sys.stderr, '\n'
Ted Kremeneka9525c92008-05-12 22:07:14 +000086
Ted Kremenek61cd9882008-05-24 15:58:54 +000087 subprocess.call(args)
Ted Kremenekb0982882008-03-25 22:35:32 +000088
89def extension(path):
90 return path.split(".")[-1]
91
92def changeextension(path, newext):
93 i = path.rfind('.')
94 if i < 0:
95 return path
96 j = path.rfind('/', 0, i)
97 print path
98 if j < 0:
99 return path[:i] + "." + newext
100 return path[j+1:i] + "." + newext
101
102def inferlanguage(extension):
103 if extension == "c":
104 return "c"
105 elif extension in ["cpp", "cc"]:
106 return "c++"
107 elif extension == "i":
108 return "c-cpp-output"
109 elif extension == "m":
110 return "objective-c"
111 elif extension == "mi":
112 return "objective-c-cpp-output"
Ted Kremenek48921c82008-05-14 20:20:46 +0000113 elif extension == "s":
Ted Kremenekbfd6a3f2008-05-14 20:17:17 +0000114 return "skip"
Ted Kremenekb0982882008-03-25 22:35:32 +0000115 else:
116 return "unknown"
117
118def main(args):
Ted Kremenek61cd9882008-05-24 15:58:54 +0000119 old_args = args
120 action = 'link'
121 output = ''
122 compile_opts = [ ]
123 link_opts = [ ]
124 files = []
125 save_temps = 0
126 language = ''
127
128 verbose = 0
129 clang = "clang"
130
131 # Forward to GCC.
132 compile(args)
133
134 # Set the analyzer flag.
135 analysis_type = os.environ.get('CCC_ANALYZER_ANALYSIS')
136
137 if analysis_type is not None:
138 analysis_type = "-" + analysis_type
139 else:
140 analysis_type = "-checker-cfref"
141
142 # Determine the level of verbosity.
143 if os.environ.get('CCC_ANALYZER_VERBOSE') is not None:
144 verbose = 1
Ted Kremenekb0982882008-03-25 22:35:32 +0000145
Ted Kremenek61cd9882008-05-24 15:58:54 +0000146 if os.environ.get('CCC_ANALYZER_LOG') is not None:
147 verbose = 2
Ted Kremenekf22eacb2008-04-18 22:00:56 +0000148
Ted Kremenek61cd9882008-05-24 15:58:54 +0000149 # Determine what clang executable to use.
150 clang_env = os.environ.get('CLANG')
151
152 if clang_env is not None:
153 clang = clang_env
154
155 # Get the HTML output directory.
156 htmldir = None
157
158 if analysis_type == "-checker-cfref":
159 htmldir = os.environ.get('CCC_ANALYZER_HTML')
160
161 # Process the arguments.
162 i = 0
163 while i < len(args):
164 arg = args[i]
165
166 # Modes ccc supports
167 if arg == '-E':
168 action = 'preprocess'
169 if arg == '-c':
170 action = 'compile'
171 if arg.startswith('-print-prog-name'):
172 action = 'print-prog-name'
173 if arg == '-save-temps':
174 save_temps = 1
175
176 # Options with no arguments that should pass through
177 if arg in ['-v']:
178 compile_opts.append(arg)
179 link_opts.append(arg)
Ted Kremenek1262fc42008-05-14 20:10:33 +0000180
Ted Kremenek61cd9882008-05-24 15:58:54 +0000181 # Options with one argument that should be ignored
182 if arg in ['--param', '-u']:
Ted Kremenekb0982882008-03-25 22:35:32 +0000183 i += 1
184
Ted Kremenek61cd9882008-05-24 15:58:54 +0000185 # Prefix matches for the compile mode
186 if arg[:2] in ['-D', '-I', '-U', '-F' ]:
187 if not arg[2:]:
188 arg += args[i+1]
189 i += 1
190 compile_opts.append(arg)
Ted Kremenekb0982882008-03-25 22:35:32 +0000191
Ted Kremenek61cd9882008-05-24 15:58:54 +0000192 if arg[:5] in ['-std=']:
193 compile_opts.append(arg)
Ted Kremenekb0982882008-03-25 22:35:32 +0000194
Ted Kremenek61cd9882008-05-24 15:58:54 +0000195 # Options with one argument that should pass through to compiler
196 if arg in [ '-include', '-idirafter', '-iprefix',
197 '-iquote', '-isystem', '-iwithprefix',
198 '-iwithprefixbefore']:
199 compile_opts.append(arg)
200 compile_opts.append(args[i+1])
201 i += 1
202
203 # Options with no argument that should pass through to compiler
204 if arg in [ '-nostdinc', '-fobjc-gc-only', '-fobjc-gc' ]:
205 compile_opts.append(arg)
Ted Kremenekb0982882008-03-25 22:35:32 +0000206
Ted Kremenek61cd9882008-05-24 15:58:54 +0000207 # Options with one argument that should pass through to linker
208 if arg == '-framework':
209 link_opts.append(arg)
210 link_opts.append(args[i+1])
211 i += 1
Ted Kremenekbfd6a3f2008-05-14 20:17:17 +0000212
Ted Kremenek61cd9882008-05-24 15:58:54 +0000213 # Options with one argument that should pass through to both
214 if arg in ['-isysroot', '-arch']:
215 compile_opts.append(arg)
216 compile_opts.append(args[i+1])
217 link_opts.append(arg)
218 link_opts.append(args[i+1])
219 i += 1
Ted Kremenekb0982882008-03-25 22:35:32 +0000220
Ted Kremenek61cd9882008-05-24 15:58:54 +0000221 # Prefix matches for the link mode
222 if arg[:2] in ['-l', '-L', '-O', '-F']:
223 if arg == '-O': arg = '-O1'
224 if arg == '-Os': arg = '-O2'
225 link_opts.append(arg)
Ted Kremenekb0982882008-03-25 22:35:32 +0000226
Ted Kremenek61cd9882008-05-24 15:58:54 +0000227 # Input files
228 if arg == '-filelist':
229 f = open(args[i+1])
230 for line in f:
231 files.append(line.strip())
232 f.close()
233 i += 1
234 if arg == '-x':
235 language = args[i+1]
236 i += 1
237 if arg[0] != '-':
238 files.append(arg)
239
240 # Output file
241 if arg == '-o':
242 output = args[i+1]
243 i += 1
244
245 i += 1
246
247 if action == 'print-prog-name':
248 # assume we can handle everything
249 print sys.argv[0]
250 return
251
252 if not files:
253 error('no input files')
254
255 if action == 'compile' or save_temps or action == 'link':
256 for i, file in enumerate(files):
257 if not language:
258 language = inferlanguage(extension(file))
259 if language == "skip":
260 continue
261
262 if save_temps and action != "compile":
263 # Need a temporary output file
264 coutput = changeextension(file, "o");
265 files[i] = coutput
266 elif not output:
267 coutput = changeextension(file, "o")
268 else:
269 coutput = output
270 analyze_args = [ file ]
271 if language != 'unknown':
272 analyze_args = [ '-x', language ] + analyze_args
273 analyze_args = analyze_args + compile_opts
274 analyze(clang, analyze_args, language, output, files, verbose, htmldir, file, analysis_type)
Ted Kremenekb0982882008-03-25 22:35:32 +0000275
276if __name__ == '__main__':
277 main(sys.argv[1:])