1#!/usr/bin/python 2 3# [PR 11661] Note that we hardwire to /usr/bin/python because we 4# want to the use the system version of Python on Mac OS X. 5# This one has the scripting bridge enabled. 6 7import sys 8if sys.version_info < (2, 7): 9 print "set-xcode-analyzer requires Python 2.7 or later" 10 sys.exit(1) 11 12import os 13import subprocess 14import re 15import tempfile 16import shutil 17import stat 18from AppKit import * 19 20def FindClangSpecs(path): 21 print "(+) Searching for xcspec file in: ", path 22 for root, dirs, files in os.walk(path): 23 for f in files: 24 if f.endswith(".xcspec") and f.startswith("Clang LLVM"): 25 yield os.path.join(root, f) 26 27def ModifySpec(path, isBuiltinAnalyzer, pathToChecker): 28 t = tempfile.NamedTemporaryFile(delete=False) 29 foundAnalyzer = False 30 with open(path) as f: 31 if isBuiltinAnalyzer: 32 # First search for CLANG_ANALYZER_EXEC. Newer 33 # versions of Xcode set EXEC_PATH to be CLANG_ANALYZER_EXEC. 34 with open(path) as f2: 35 for line in f2: 36 if line.find("CLANG_ANALYZER_EXEC") >= 0: 37 pathToChecker = "$(CLANG_ANALYZER_EXEC)" 38 break 39 # Now create a new file. 40 for line in f: 41 if not foundAnalyzer: 42 if line.find("Static Analyzer") >= 0: 43 foundAnalyzer = True 44 else: 45 m = re.search('^(\s*ExecPath\s*=\s*")', line) 46 if m: 47 line = "".join([m.group(0), pathToChecker, '";\n']) 48 # Do not modify further ExecPath's later in the xcspec. 49 foundAnalyzer = False 50 t.write(line) 51 t.close() 52 print "(+) processing:", path 53 try: 54 shutil.copy(t.name, path) 55 os.chmod(path, stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH) 56 except IOError, why: 57 print " (-) Cannot update file:", why, "\n" 58 except OSError, why: 59 print " (-) Cannot update file:", why, "\n" 60 os.unlink(t.name) 61 62def main(): 63 from optparse import OptionParser 64 parser = OptionParser('usage: %prog [options]') 65 parser.set_description(__doc__) 66 parser.add_option("--use-checker-build", dest="path", 67 help="Use the Clang located at the provided absolute path, e.g. /Users/foo/checker-1") 68 parser.add_option("--use-xcode-clang", action="store_const", 69 const="$(CLANG)", dest="default", 70 help="Use the Clang bundled with Xcode") 71 (options, args) = parser.parse_args() 72 if options.path is None and options.default is None: 73 parser.error("You must specify a version of Clang to use for static analysis. Specify '-h' for details") 74 75 # determine if Xcode is running 76 for x in NSWorkspace.sharedWorkspace().runningApplications(): 77 if x.localizedName().find("Xcode") >= 0: 78 print "(-) You must quit Xcode first before modifying its configuration files." 79 sys.exit(1) 80 81 isBuiltinAnalyzer = False 82 if options.path: 83 # Expand tildes. 84 path = os.path.expanduser(options.path) 85 if not path.endswith("clang"): 86 print "(+) Using Clang bundled with checker build:", path 87 path = os.path.join(path, "bin", "clang"); 88 else: 89 print "(+) Using Clang located at:", path 90 else: 91 print "(+) Using the Clang bundled with Xcode" 92 path = options.default 93 isBuiltinAnalyzer = True 94 95 try: 96 xcode_path = subprocess.check_output(["xcode-select", "-print-path"]) 97 except AttributeError: 98 # Fall back to the default install location when using Python < 2.7.0 99 xcode_path = "/Developer" 100 if (xcode_path.find(".app/") != -1): 101 # Cut off the 'Developer' dir, as the xcspec lies in another part 102 # of the Xcode.app subtree. 103 xcode_path = xcode_path.rsplit('/Developer', 1)[0] 104 105 foundSpec = False 106 for x in FindClangSpecs(xcode_path): 107 foundSpec = True 108 ModifySpec(x, isBuiltinAnalyzer, path) 109 110 if foundSpec == False: 111 print "(-) No compiler configuration file was found. Xcode's analyzer has not been updated." 112 113if __name__ == '__main__': 114 main() 115