15a15d92206f8ef357c49d6993a6ec4a85bde7b8fTed Kremenek#!/usr/bin/python
25a15d92206f8ef357c49d6993a6ec4a85bde7b8fTed Kremenek
35a15d92206f8ef357c49d6993a6ec4a85bde7b8fTed Kremenek# [PR 11661] Note that we hardwire to /usr/bin/python because we
45a15d92206f8ef357c49d6993a6ec4a85bde7b8fTed Kremenek# want to the use the system version of Python on Mac OS X.
55a15d92206f8ef357c49d6993a6ec4a85bde7b8fTed Kremenek# This one has the scripting bridge enabled.
67aaa9535815a9423e2574a524022c8118cc1aa3bTed Kremenek
77aaa9535815a9423e2574a524022c8118cc1aa3bTed Kremenekimport os
82775b935fbd68b150d432338f0f316f6fc81674aTed Kremenekimport subprocess
97aaa9535815a9423e2574a524022c8118cc1aa3bTed Kremenekimport sys
107aaa9535815a9423e2574a524022c8118cc1aa3bTed Kremenekimport re
117aaa9535815a9423e2574a524022c8118cc1aa3bTed Kremenekimport tempfile
127aaa9535815a9423e2574a524022c8118cc1aa3bTed Kremenekimport shutil
137aaa9535815a9423e2574a524022c8118cc1aa3bTed Kremenekimport stat
14962da0baff023e3dfa5e79908ac033aed5b5311fTed Kremenekfrom AppKit import *
157aaa9535815a9423e2574a524022c8118cc1aa3bTed Kremenek
167aaa9535815a9423e2574a524022c8118cc1aa3bTed Kremenekdef FindClangSpecs(path):
172775b935fbd68b150d432338f0f316f6fc81674aTed Kremenek  print "(+) Searching for xcspec file in: ", path
187aaa9535815a9423e2574a524022c8118cc1aa3bTed Kremenek  for root, dirs, files in os.walk(path):
197aaa9535815a9423e2574a524022c8118cc1aa3bTed Kremenek    for f in files:
207aaa9535815a9423e2574a524022c8118cc1aa3bTed Kremenek      if f.endswith(".xcspec") and f.startswith("Clang LLVM"):
217aaa9535815a9423e2574a524022c8118cc1aa3bTed Kremenek        yield os.path.join(root, f)
227aaa9535815a9423e2574a524022c8118cc1aa3bTed Kremenek
237aaa9535815a9423e2574a524022c8118cc1aa3bTed Kremenekdef ModifySpec(path, pathToChecker):
247aaa9535815a9423e2574a524022c8118cc1aa3bTed Kremenek  t = tempfile.NamedTemporaryFile(delete=False)
257aaa9535815a9423e2574a524022c8118cc1aa3bTed Kremenek  foundAnalyzer = False
267aaa9535815a9423e2574a524022c8118cc1aa3bTed Kremenek  with open(path) as f:
277aaa9535815a9423e2574a524022c8118cc1aa3bTed Kremenek    for line in f:
287aaa9535815a9423e2574a524022c8118cc1aa3bTed Kremenek      if not foundAnalyzer:
297aaa9535815a9423e2574a524022c8118cc1aa3bTed Kremenek        if line.find("Static Analyzer") >= 0:
307aaa9535815a9423e2574a524022c8118cc1aa3bTed Kremenek          foundAnalyzer = True
317aaa9535815a9423e2574a524022c8118cc1aa3bTed Kremenek      else:
327aaa9535815a9423e2574a524022c8118cc1aa3bTed Kremenek        m = re.search('^(\s*ExecPath\s*=\s*")', line)
337aaa9535815a9423e2574a524022c8118cc1aa3bTed Kremenek        if m:
347aaa9535815a9423e2574a524022c8118cc1aa3bTed Kremenek          line = "".join([m.group(0), pathToChecker, '";\n'])
357aaa9535815a9423e2574a524022c8118cc1aa3bTed Kremenek      t.write(line)
367aaa9535815a9423e2574a524022c8118cc1aa3bTed Kremenek  t.close()
378d69937281b790666f48f71ac8ad7821e60baceeTed Kremenek  print "(+) processing:", path
38757a9d1f005ffb8a628a247a11a9869cf8f336c0Ted Kremenek  try:
39757a9d1f005ffb8a628a247a11a9869cf8f336c0Ted Kremenek    shutil.copy(t.name, path)
40757a9d1f005ffb8a628a247a11a9869cf8f336c0Ted Kremenek    os.chmod(path, stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH)
41757a9d1f005ffb8a628a247a11a9869cf8f336c0Ted Kremenek  except IOError, why:
428d69937281b790666f48f71ac8ad7821e60baceeTed Kremenek    print "    (-) Cannot update file:", why, "\n"
43757a9d1f005ffb8a628a247a11a9869cf8f336c0Ted Kremenek  except OSError, why:
448d69937281b790666f48f71ac8ad7821e60baceeTed Kremenek    print "    (-) Cannot update file:", why, "\n"
457aaa9535815a9423e2574a524022c8118cc1aa3bTed Kremenek  os.unlink(t.name)
467aaa9535815a9423e2574a524022c8118cc1aa3bTed Kremenek
477aaa9535815a9423e2574a524022c8118cc1aa3bTed Kremenekdef main():
487aaa9535815a9423e2574a524022c8118cc1aa3bTed Kremenek  from optparse import OptionParser
497aaa9535815a9423e2574a524022c8118cc1aa3bTed Kremenek  parser = OptionParser('usage: %prog [options]')
507aaa9535815a9423e2574a524022c8118cc1aa3bTed Kremenek  parser.set_description(__doc__)
517aaa9535815a9423e2574a524022c8118cc1aa3bTed Kremenek  parser.add_option("--use-checker-build", dest="path",
527aaa9535815a9423e2574a524022c8118cc1aa3bTed Kremenek                    help="Use the Clang located at the provided absolute path, e.g. /Users/foo/checker-1")
537aaa9535815a9423e2574a524022c8118cc1aa3bTed Kremenek  parser.add_option("--use-xcode-clang", action="store_const", 
547aaa9535815a9423e2574a524022c8118cc1aa3bTed Kremenek                    const="$(CLANG)", dest="default",
557aaa9535815a9423e2574a524022c8118cc1aa3bTed Kremenek                    help="Use the Clang bundled with Xcode")
567aaa9535815a9423e2574a524022c8118cc1aa3bTed Kremenek  (options, args) = parser.parse_args()
577aaa9535815a9423e2574a524022c8118cc1aa3bTed Kremenek  if options.path is None and options.default is None:
587aaa9535815a9423e2574a524022c8118cc1aa3bTed Kremenek    parser.error("You must specify a version of Clang to use for static analysis.  Specify '-h' for details")
597aaa9535815a9423e2574a524022c8118cc1aa3bTed Kremenek
60962da0baff023e3dfa5e79908ac033aed5b5311fTed Kremenek  # determine if Xcode is running
61962da0baff023e3dfa5e79908ac033aed5b5311fTed Kremenek  for x in NSWorkspace.sharedWorkspace().runningApplications():
62962da0baff023e3dfa5e79908ac033aed5b5311fTed Kremenek    if x.localizedName().find("Xcode") >= 0:
638d69937281b790666f48f71ac8ad7821e60baceeTed Kremenek      print "(-) You must quit Xcode first before modifying its configuration files."
64962da0baff023e3dfa5e79908ac033aed5b5311fTed Kremenek      return
65962da0baff023e3dfa5e79908ac033aed5b5311fTed Kremenek
667aaa9535815a9423e2574a524022c8118cc1aa3bTed Kremenek  if options.path:
677aaa9535815a9423e2574a524022c8118cc1aa3bTed Kremenek    # Expand tildes.
687aaa9535815a9423e2574a524022c8118cc1aa3bTed Kremenek    path = os.path.expanduser(options.path)
697aaa9535815a9423e2574a524022c8118cc1aa3bTed Kremenek    if not path.endswith("clang"):
708d69937281b790666f48f71ac8ad7821e60baceeTed Kremenek      print "(+) Using Clang bundled with checker build:", path
717aaa9535815a9423e2574a524022c8118cc1aa3bTed Kremenek      path = os.path.join(path, "bin", "clang");
727aaa9535815a9423e2574a524022c8118cc1aa3bTed Kremenek    else:
738d69937281b790666f48f71ac8ad7821e60baceeTed Kremenek      print "(+) Using Clang located at:", path
747aaa9535815a9423e2574a524022c8118cc1aa3bTed Kremenek  else:
758d69937281b790666f48f71ac8ad7821e60baceeTed Kremenek    print "(+) Using the Clang bundled with Xcode"
767aaa9535815a9423e2574a524022c8118cc1aa3bTed Kremenek    path = options.default
77962da0baff023e3dfa5e79908ac033aed5b5311fTed Kremenek  
78b8971b27817608bd268dda18ff3e0b4e61377cd0Ted Kremenek  try:
79b8971b27817608bd268dda18ff3e0b4e61377cd0Ted Kremenek    xcode_path = subprocess.check_output(["xcode-select", "-print-path"])
80b8971b27817608bd268dda18ff3e0b4e61377cd0Ted Kremenek  except AttributeError:
81b8971b27817608bd268dda18ff3e0b4e61377cd0Ted Kremenek    # Fall back to the default install location when using Python < 2.7.0
82b8971b27817608bd268dda18ff3e0b4e61377cd0Ted Kremenek    xcode_path = "/Developer"
832775b935fbd68b150d432338f0f316f6fc81674aTed Kremenek  if (re.search("Xcode.app", xcode_path)):
842775b935fbd68b150d432338f0f316f6fc81674aTed Kremenek    # Cut off the 'Developer' dir, as the xcspec lies in another part
852775b935fbd68b150d432338f0f316f6fc81674aTed Kremenek    # of the Xcode.app subtree.
862775b935fbd68b150d432338f0f316f6fc81674aTed Kremenek    xcode_path = os.path.dirname(xcode_path)
872775b935fbd68b150d432338f0f316f6fc81674aTed Kremenek  
882775b935fbd68b150d432338f0f316f6fc81674aTed Kremenek  for x in FindClangSpecs(xcode_path):
897aaa9535815a9423e2574a524022c8118cc1aa3bTed Kremenek    ModifySpec(x, path)
907aaa9535815a9423e2574a524022c8118cc1aa3bTed Kremenek
917aaa9535815a9423e2574a524022c8118cc1aa3bTed Kremenekif __name__ == '__main__':
927aaa9535815a9423e2574a524022c8118cc1aa3bTed Kremenek  main()
937aaa9535815a9423e2574a524022c8118cc1aa3bTed Kremenek
94