#!/usr/bin/env python # # The LLVM Compiler Infrastructure # # This file is distributed under the University of Illinois Open Source # License. See LICENSE.TXT for details. # ##===----------------------------------------------------------------------===## # # A reduced version of the 'ccc' script that is designed to handle off # actual compilation to gcc, but run the code passed to gcc through the # static analyzer. # ##===----------------------------------------------------------------------===## import sys import subprocess import os def error(message): print >> sys.stderr, 'ccc: ' + message sys.exit(1) def quote(arg): if '"' in arg: return repr(arg) return arg def run(args): code = subprocess.call(args) if code > 255: code = 1 if code: sys.exit(code) def compile(args): command = 'gcc'.split() run(command + args) def remove_pch_extension(path): i = path.rfind('.gch') if i < 0: return path return path[:i] def analyze(clang, args,language,output,files,verbose,htmldir,file,analysis_type): if language.rfind("c++") >= 0: return RunAnalyzer = 0; if language.find("header") > 0: target = remove_pch_extension(output) command = ['cp'] args = command + files + [ target ] else: command = clang.split() + analysis_type.split() args = command + args; RunAnalyzer = 1 print_args = [] if verbose: # We MUST print to stderr. Some clients use the stdout output of # gcc for various purposes. print >> sys.stderr, ' '.join(['\n[LOCATION]:', os.getcwd(), '\n' ]) i = 0 while i < len(args): print_args.append(''.join([ '\'', args[i], '\'' ])) i += 1 if verbose == 2: print >> sys.stderr, '#SHELL (cd ' + os.getcwd() + ' && ' + ' '.join(print_args) + ')\n' if RunAnalyzer and htmldir is not None: args.append('-o') print_args.append('-o') args.append(htmldir) print_args.append(htmldir) if verbose == 1: # We MUST print to stderr. Some clients use the stdout output of # gcc for various purposes. print >> sys.stderr, ' '.join(print_args) print >> sys.stderr, '\n' subprocess.call(args) def extension(path): return path.split(".")[-1] def changeextension(path, newext): i = path.rfind('.') if i < 0: return path j = path.rfind('/', 0, i) if j < 0: return path[:i] + "." + newext return path[j+1:i] + "." + newext def inferlanguage(extension): if extension == "c": return "c" elif extension in ["cpp", "cc"]: return "c++" elif extension == "i": return "c-cpp-output" elif extension == "m": return "objective-c" elif extension == "mi": return "objective-c-cpp-output" elif extension in [ "s", "o", "a", "so" ]: return "skip" else: return "skip" # Skip files with unknown extensions. This # deviates from ccc, but this works very well for # the analyzer. def main(args): old_args = args action = 'link' output = '' compile_opts = [ ] link_opts = [ ] files = [] save_temps = 0 language = '' verbose = 0 clang = "clang" # Forward to GCC. compile(args) # Set the analyzer flag. analysis_type = os.environ.get('CCC_ANALYZER_ANALYSIS') if analysis_type is not None: analysis_type = "-" + analysis_type else: analysis_type = "-warn-dead-stores -checker-cfref" # Determine the level of verbosity. if os.environ.get('CCC_ANALYZER_VERBOSE') is not None: verbose = 1 if os.environ.get('CCC_ANALYZER_LOG') is not None: verbose = 2 # Determine what clang executable to use. clang_env = os.environ.get('CLANG') if clang_env is not None: clang = clang_env # Get the HTML output directory. htmldir = os.environ.get('CCC_ANALYZER_HTML') # Process the arguments. i = 0 while i < len(args): arg = args[i] # Modes ccc supports if arg == '-E': action = 'preprocess' if arg == '-c': action = 'compile' if arg.startswith('-print-prog-name'): action = 'print-prog-name' if arg == '-save-temps': save_temps = 1 # Options with no arguments that should pass through if arg in ['-v']: compile_opts.append(arg) link_opts.append(arg) # Options with one argument that should be ignored if arg in ['--param', '-u']: i += 1 # Prefix matches for the compile mode if arg[:2] in ['-D', '-I', '-U', '-F' ]: if not arg[2:]: arg += args[i+1] i += 1 compile_opts.append(arg) if arg[:5] in ['-std=']: compile_opts.append(arg) # Options with one argument that should pass through to compiler if arg in [ '-include', '-idirafter', '-iprefix', '-iquote', '-isystem', '-iwithprefix', '-iwithprefixbefore']: compile_opts.append(arg) compile_opts.append(args[i+1]) i += 1 # Options with no argument that should pass through to compiler if arg in [ '-nostdinc', '-fobjc-gc-only', '-fobjc-gc' ]: compile_opts.append(arg) # Options with one argument that should pass through to linker if arg == '-framework': link_opts.append(arg) link_opts.append(args[i+1]) i += 1 # Options with one argument that should pass through to both if arg in ['-isysroot', '-arch']: compile_opts.append(arg) compile_opts.append(args[i+1]) link_opts.append(arg) link_opts.append(args[i+1]) i += 1 # Prefix matches for the link mode if arg[:2] in ['-l', '-L', '-O', '-F']: if arg == '-O': arg = '-O1' if arg == '-Os': arg = '-O2' link_opts.append(arg) # Input files if arg == '-filelist': f = open(args[i+1]) for line in f: files.append(line.strip()) f.close() i += 1 if arg == '-x': language = args[i+1] i += 1 if arg[0] != '-': files.append(arg) # Output file if arg == '-o': output = args[i+1] i += 1 # Arguments we currently ignore with one option. if arg in ['-install_name', '-exported_symbols_list', '-current_version', '-compatibility_version', '-init', '-e', '-seg1addr', '-bundle_loader', '-multiply_defined']: i += 1 # Arguments we currently ignore with three options. if arg in ['-sectorder']: i += 3 i += 1 if action == 'print-prog-name': # assume we can handle everything print sys.argv[0] return if not files: error('no input files') if action == 'compile' or save_temps or action == 'link': for i, file in enumerate(files): file_language = language if not language: file_language = inferlanguage(extension(file)) if file_language == "skip": continue if save_temps and action != "compile": # Need a temporary output file coutput = changeextension(file, "o"); files[i] = coutput elif not output: coutput = changeextension(file, "o") else: coutput = output analyze_args = [ file ] if file_language != 'unknown': analyze_args = [ '-x', file_language ] + analyze_args analyze_args = analyze_args + compile_opts analyze(clang, analyze_args, language, output, files, verbose, htmldir, file, analysis_type) if __name__ == '__main__': main(sys.argv[1:])