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 7 import sys 8 if sys.version_info < (2, 7): 9 print "set-xcode-analyzer requires Python 2.7 or later" 10 sys.exit(1) 11 12 import os 13 import subprocess 14 import re 15 import tempfile 16 import shutil 17 import stat 18 from AppKit import * 19 20 def 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 27 def 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 62 def 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 113 if __name__ == '__main__': 114 main() 115