| Ted Kremenek | b098288 | 2008-03-25 22:35:32 +0000 | [diff] [blame] | 1 | #!/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 |  | 
 | 16 | import sys | 
 | 17 | import subprocess | 
 | 18 | import os | 
 | 19 |  | 
 | 20 | def error(message): | 
 | 21 |     print >> sys.stderr, 'ccc: ' + message | 
 | 22 |     sys.exit(1) | 
 | 23 |  | 
| Seo Sanghyeon | d389465 | 2008-04-04 11:02:21 +0000 | [diff] [blame] | 24 | def quote(arg): | 
 | 25 |     if '"' in arg: | 
 | 26 |         return repr(arg) | 
 | 27 |     return arg | 
 | 28 |  | 
| Ted Kremenek | b098288 | 2008-03-25 22:35:32 +0000 | [diff] [blame] | 29 | def run(args): | 
| Ted Kremenek | 6e9d38e | 2008-04-07 23:27:54 +0000 | [diff] [blame] | 30 |     # We MUST print to stderr.  Some clients use the stdout output of | 
 | 31 |     # gcc for various purposes. | 
 | 32 |     print >> sys.stderr, ' '.join(map(quote, args)) | 
 | 33 |     print >> sys.stderr | 
| Ted Kremenek | b098288 | 2008-03-25 22:35:32 +0000 | [diff] [blame] | 34 |     code = subprocess.call(args) | 
 | 35 |     if code > 255: | 
 | 36 |         code = 1 | 
 | 37 |     if code: | 
 | 38 |         sys.exit(code) | 
 | 39 |  | 
| Ted Kremenek | 39a7973 | 2008-04-19 18:42:26 +0000 | [diff] [blame] | 40 | def compile(args,verbose): | 
| Ted Kremenek | 6e9d38e | 2008-04-07 23:27:54 +0000 | [diff] [blame] | 41 |   # We MUST print to stderr.  Some clients use the stdout output of | 
 | 42 |   # gcc for various purposes. | 
| Ted Kremenek | b098288 | 2008-03-25 22:35:32 +0000 | [diff] [blame] | 43 |   print >> sys.stderr, '\n' | 
 | 44 |   command = 'gcc'.split() | 
 | 45 |   run(command + args) | 
 | 46 |  | 
 | 47 | def remove_pch_extension(path): | 
 | 48 |       i = path.rfind('.gch') | 
 | 49 |       if i < 0: | 
 | 50 |           return path | 
 | 51 |       return path[:i] | 
 | 52 |  | 
| Ted Kremenek | f22eacb | 2008-04-18 22:00:56 +0000 | [diff] [blame] | 53 | def analyze(clang, args,language,output,files,verbose,htmldir): | 
| Ted Kremenek | 8cb53fb | 2008-04-03 21:29:11 +0000 | [diff] [blame] | 54 |     if language.find("c++") > 0: | 
| Ted Kremenek | b098288 | 2008-03-25 22:35:32 +0000 | [diff] [blame] | 55 |       return | 
 | 56 |    | 
| Ted Kremenek | b098288 | 2008-03-25 22:35:32 +0000 | [diff] [blame] | 57 |     print_args = [] | 
 | 58 |      | 
| Ted Kremenek | 09c2ad6 | 2008-03-31 18:25:05 +0000 | [diff] [blame] | 59 |     if verbose: | 
| Ted Kremenek | 6e9d38e | 2008-04-07 23:27:54 +0000 | [diff] [blame] | 60 |       # We MUST print to stderr.  Some clients use the stdout output of | 
 | 61 |       # gcc for various purposes. | 
| Ted Kremenek | 09c2ad6 | 2008-03-31 18:25:05 +0000 | [diff] [blame] | 62 |       print >> sys.stderr, ' '.join(['\n[LOCATION]:', os.getcwd(), '\n' ]) | 
 | 63 |       i = 0 | 
 | 64 |       while i < len(args): | 
| Ted Kremenek | b098288 | 2008-03-25 22:35:32 +0000 | [diff] [blame] | 65 |         print_args.append(''.join([ '\'', args[i], '\'' ])) | 
 | 66 |         i += 1 | 
 | 67 |      | 
 | 68 |     if language.find("header") > 0: | 
 | 69 |       target = remove_pch_extension(output) | 
 | 70 |       command = 'cp'.split() | 
 | 71 |       args = command + files + target.split()       | 
 | 72 |     else: | 
| Ted Kremenek | f22eacb | 2008-04-18 22:00:56 +0000 | [diff] [blame] | 73 |       command = clang.split() + '-checker-cfref'.split() | 
| Ted Kremenek | 69b6442 | 2008-03-31 21:20:32 +0000 | [diff] [blame] | 74 |       args = command + args; | 
 | 75 |        | 
 | 76 |       if htmldir is not None: | 
 | 77 |         args.append('-o') | 
 | 78 |         print_args.append('-o') | 
 | 79 |         args.append(htmldir) | 
 | 80 |         print_args.append(htmldir) | 
| Ted Kremenek | 09c2ad6 | 2008-03-31 18:25:05 +0000 | [diff] [blame] | 81 |      | 
| Ted Kremenek | 6e9d38e | 2008-04-07 23:27:54 +0000 | [diff] [blame] | 82 |     if verbose:  | 
 | 83 |       # We MUST print to stderr.  Some clients use the stdout output of | 
 | 84 |       # gcc for various purposes.  | 
| Ted Kremenek | 09c2ad6 | 2008-03-31 18:25:05 +0000 | [diff] [blame] | 85 |       print >> sys.stderr, ' '.join(command+print_args) | 
 | 86 |       print >> sys.stderr, '\n' | 
| Ted Kremenek | b098288 | 2008-03-25 22:35:32 +0000 | [diff] [blame] | 87 |        | 
| Ted Kremenek | b098288 | 2008-03-25 22:35:32 +0000 | [diff] [blame] | 88 |     subprocess.call(args) | 
 | 89 |  | 
 | 90 | def link(args): | 
 | 91 |     command = 'gcc'.split() | 
 | 92 |     run(command + args) | 
 | 93 |  | 
 | 94 | def extension(path): | 
 | 95 |     return path.split(".")[-1] | 
 | 96 |  | 
 | 97 | def changeextension(path, newext): | 
 | 98 |     i = path.rfind('.') | 
 | 99 |     if i < 0: | 
 | 100 |         return path | 
 | 101 |     j = path.rfind('/', 0, i) | 
 | 102 |     print path | 
 | 103 |     if j < 0: | 
 | 104 |         return path[:i] + "." + newext | 
 | 105 |     return path[j+1:i] + "." + newext | 
 | 106 |  | 
 | 107 | def inferlanguage(extension): | 
 | 108 |   if extension == "c": | 
 | 109 |       return "c" | 
 | 110 |   elif extension in ["cpp", "cc"]: | 
 | 111 |       return "c++" | 
 | 112 |   elif extension == "i": | 
 | 113 |       return "c-cpp-output" | 
 | 114 |   elif extension == "m": | 
 | 115 |       return "objective-c" | 
 | 116 |   elif extension == "mi": | 
 | 117 |       return "objective-c-cpp-output" | 
 | 118 |   else: | 
 | 119 |       return "unknown" | 
 | 120 |  | 
 | 121 | def main(args): | 
 | 122 |     old_args = args | 
 | 123 |     action = 'link' | 
 | 124 |     output = '' | 
 | 125 |     compile_opts = [ ] | 
 | 126 |     link_opts = [ ] | 
 | 127 |     files = [] | 
 | 128 |     save_temps = 0 | 
 | 129 |     language = '' | 
 | 130 |      | 
| Ted Kremenek | 09c2ad6 | 2008-03-31 18:25:05 +0000 | [diff] [blame] | 131 |     verbose = 0 | 
| Ted Kremenek | f22eacb | 2008-04-18 22:00:56 +0000 | [diff] [blame] | 132 |     clang = "clang" | 
 | 133 |      | 
| Ted Kremenek | 09c2ad6 | 2008-03-31 18:25:05 +0000 | [diff] [blame] | 134 |      | 
 | 135 |     if os.environ.get('CCC_ANALYZER_VERBOSE') is not None: | 
 | 136 |       verbose =1 | 
| Ted Kremenek | f22eacb | 2008-04-18 22:00:56 +0000 | [diff] [blame] | 137 |        | 
 | 138 |     clang_env = os.environ.get('CLANG')  | 
 | 139 |      | 
 | 140 |     if clang_env is not None: | 
 | 141 |       clang = clang_env | 
| Ted Kremenek | 09c2ad6 | 2008-03-31 18:25:05 +0000 | [diff] [blame] | 142 |      | 
 | 143 |     htmldir = os.environ.get('CCC_ANALYZER_HTML') | 
| Ted Kremenek | 09c2ad6 | 2008-03-31 18:25:05 +0000 | [diff] [blame] | 144 |        | 
| Ted Kremenek | b098288 | 2008-03-25 22:35:32 +0000 | [diff] [blame] | 145 |     i = 0 | 
 | 146 |     while i < len(args): | 
 | 147 |         arg = args[i] | 
 | 148 |  | 
 | 149 |         # Modes ccc supports | 
 | 150 |         if arg == '-E': | 
 | 151 |             action = 'preprocess' | 
 | 152 |         if arg == '-c': | 
 | 153 |             action = 'compile' | 
 | 154 |         if arg.startswith('-print-prog-name'): | 
 | 155 |             action = 'print-prog-name' | 
 | 156 |         if arg == '-save-temps': | 
 | 157 |             save_temps = 1 | 
 | 158 |  | 
 | 159 |         # Options with no arguments that should pass through | 
 | 160 |         if arg in ['-v']: | 
 | 161 |             compile_opts.append(arg) | 
 | 162 |             link_opts.append(arg) | 
 | 163 |          | 
 | 164 |         # Options with one argument that should be ignored | 
| Ted Kremenek | d0eef02 | 2008-04-21 20:28:01 +0000 | [diff] [blame] | 165 |         if arg in ['--param', '-u']: | 
| Ted Kremenek | b098288 | 2008-03-25 22:35:32 +0000 | [diff] [blame] | 166 |             i += 1 | 
 | 167 |  | 
 | 168 |         # Prefix matches for the compile mode | 
 | 169 |         if arg[:2] in ['-D', '-I', '-U', '-F']: | 
 | 170 |             if not arg[2:]: | 
 | 171 |                 arg += args[i+1] | 
 | 172 |                 i += 1 | 
 | 173 |             compile_opts.append(arg) | 
 | 174 |         if arg[:5] in ['-std=']: | 
 | 175 |             compile_opts.append(arg) | 
 | 176 |  | 
 | 177 |         # Options with one argument that should pass through | 
| Ted Kremenek | d0eef02 | 2008-04-21 20:28:01 +0000 | [diff] [blame] | 178 |         if arg in ['-include', '-isysroot', '-arch']: | 
| Ted Kremenek | b098288 | 2008-03-25 22:35:32 +0000 | [diff] [blame] | 179 |             compile_opts.append(arg) | 
 | 180 |             compile_opts.append(args[i+1]) | 
 | 181 |             i += 1 | 
 | 182 |  | 
 | 183 |         # Prefix matches for the link mode | 
 | 184 |         if arg[:2] in ['-l', '-L', '-O', '-F']: | 
 | 185 |             if arg == '-O': arg = '-O1' | 
 | 186 |             if arg == '-Os': arg = '-O2' | 
 | 187 |             link_opts.append(arg) | 
 | 188 |  | 
 | 189 |         # Options with one argument that should pass through | 
| Ted Kremenek | d0eef02 | 2008-04-21 20:28:01 +0000 | [diff] [blame] | 190 |         if arg in ['-framework', '-isysroot', '-arch']: | 
| Ted Kremenek | b098288 | 2008-03-25 22:35:32 +0000 | [diff] [blame] | 191 |             link_opts.append(arg) | 
 | 192 |             link_opts.append(args[i+1]) | 
 | 193 |             i += 1 | 
 | 194 |  | 
 | 195 |         # Input files | 
 | 196 |         if arg == '-filelist': | 
 | 197 |             f = open(args[i+1]) | 
 | 198 |             for line in f: | 
 | 199 |                 files.append(line.strip()) | 
 | 200 |             f.close() | 
 | 201 |             i += 1 | 
 | 202 |         if arg == '-x': | 
 | 203 |             language = args[i+1] | 
 | 204 |             i += 1 | 
 | 205 |         if arg[0] != '-': | 
 | 206 |             files.append(arg) | 
 | 207 |  | 
 | 208 |         # Output file | 
 | 209 |         if arg == '-o': | 
 | 210 |             output = args[i+1] | 
 | 211 |             i += 1 | 
 | 212 |  | 
 | 213 |         i += 1 | 
 | 214 |  | 
 | 215 |     if action == 'print-prog-name': | 
 | 216 |         # assume we can handle everything | 
 | 217 |         print sys.argv[0] | 
 | 218 |         return | 
 | 219 |  | 
 | 220 |     if not files: | 
 | 221 |         error('no input files') | 
 | 222 |  | 
 | 223 |     if action == 'preprocess' or save_temps: | 
 | 224 |       compile(args) | 
 | 225 |  | 
 | 226 |     if action == 'compile' or save_temps: | 
 | 227 |         for i, file in enumerate(files): | 
 | 228 |             if not language: | 
 | 229 |                 language = inferlanguage(extension(file)) | 
 | 230 |             if save_temps and action != "compile": | 
 | 231 |                 # Need a temporary output file | 
 | 232 |                 coutput = changeextension(file, "o"); | 
 | 233 |                 files[i] = coutput | 
 | 234 |             elif not output: | 
 | 235 |                 coutput = changeextension(file, "o") | 
 | 236 |             else: | 
 | 237 |                 coutput = output | 
 | 238 |             analyze_args = [ file ] | 
 | 239 |             if language != 'unknown': | 
 | 240 |               analyze_args = analyze_args + [ '-x', language ] | 
 | 241 |             analyze_args = analyze_args + compile_opts | 
| Ted Kremenek | f22eacb | 2008-04-18 22:00:56 +0000 | [diff] [blame] | 242 |             analyze(clang, analyze_args, language, output, files, verbose, htmldir) | 
| Ted Kremenek | b098288 | 2008-03-25 22:35:32 +0000 | [diff] [blame] | 243 |         compile(args) | 
 | 244 |  | 
 | 245 |  | 
 | 246 |     if action == 'link': | 
 | 247 |         link(args) | 
 | 248 | #        analyze(link_opts) | 
 | 249 |  | 
 | 250 | if __name__ == '__main__': | 
 | 251 |     main(sys.argv[1:]) |