Home | History | Annotate | Download | only in scan-build
      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