file isExecutable
More MySpace removal.
#!/usr/bin/python -Wi:tempnam # written by Ryan Govostes (rgovostes@gmail.com) for the Adium project # Change these as you see fit. "-checker-simple" : False, # Perform simple path-sensitive checks "-checker-cfref" : True, # Run the [Core] Foundation reference count checker "-warn-dead-stores" : True, # Warn about stores to dead variables # "-warn-uninit-values" : False, # Warn about uses of uninitialized variables (buggy?) "-warn-objc-methodsigs" : True, # Warn about Objective-C method signatures with type incompatibilities "-warn-objc-missing-dealloc" : True, # Warn about Objective-C classes that lack a correct implementation of -dealloc "-warn-objc-unused-ivars" : True, # Warn about private ivars that are never used build_configuration = "Debug" # Some functions we'll be using... def which(what, softfail = False): Calls the `which` program to look up the desired executable in the path. If it is not found, the function will look in the current working directory for an executable file of the name. If found, the absolute path to the program is returned. Otherwise, if the softfail argument is not set, the script terminates. subby = subprocess.Popen(["which", what], stdout = subprocess.PIPE) path = subby.stdout.read().rstrip() sys.exit("Failed to execute the `which` program.") # Check the current directory if os.path.isfile(what) and os.access(what, os.X_OK): path = os.path.realpath(what) sys.exit("Could not locate the `%s` program." % what) # Make sure we have clang and ccc-analyze. if not which("clang", True): if not which("ccc-analyzer", True): missing.append("the ccc-analyzer script") print >> sys.stderr, "Could not locate %s." % (missing[0]) print >> sys.stderr, "Could not locate %s and %s." % (missing[0], missing[1]) Install the needed files in the Analyzer directory or anywhere in the path. See <http://clang.llvm.org/StaticAnalysisUsage.html#Obtaining>.""") # If Clang has been updated, we want to know about the available analysis # options and warn the user if they've changed. # Ask clang to print its usage help subby = subprocess.Popen([which("clang"), "--help-hidden"], stdout = subprocess.PIPE) # Now we're going to look through and gather all of the analysis options optpattern = re.compile("^ (-[^\s]+)(\s+- .*)$") discovered_options = None for line in subby.stdout: # Look for the start marker if type(discovered_options) != dict: if line == " Available Source Code Analyses:": # If we didn't match, we've gone beyond the analysis options option = optpattern.match(line) # Filter out some options we don't care about if option.group(1) == "-cfg-dump" or \
option.group(1) == "-cfg-view" or \
option.group(1) == "-dump-live-variables" or \
option.group(1) == "-warn-uninit-values": discovered_options[option.group(1)] = option.group(2) # Next we're going to see if we've set any options that don't exist for opt in analyzer_options: if not opt in discovered_options: if analyzer_options[opt]: afterword = " (disabled)" analyzer_options[opt] = False print >> sys.stderr, "Analyzer option %s is not one that Clang understands%s." % (opt, afterword) # And the other direction: are there any options we don't know about? for opt in discovered_options: if not opt in analyzer_options: print "Clang supports the following options that are not used:" print "\t%s%s" % (opt, discovered_options[opt]) # Set up the Clang HTML output directory. # Make sure the Adium.xcodeproj is here adiumdir = os.path.realpath(os.path.join(os.pardir, os.pardir)) if not os.path.isdir(os.path.join(adiumdir, "Adium.xcodeproj")): print >> sys.stderr, "Could not locate Adium's Xcode project bundle. (You must run this script from the Analyzer directory.)" # Get the SVN revision number svnpath = which("svn", True) subby = subprocess.Popen([svnpath, "info"], stdout = subprocess.PIPE) revmatch = re.search("Revision: ([0-9]+)", subby.stdout.read()) print "Could not determine SVN revision of Adium source." revision = revmatch.group(1) # Ask make to run so we can get some options from it. This is a cheat, relying # on Adium's Makefile which reads the Xcode defaults to see if the user has # specified a build directory. # It does not, however, read the .pbxproj file to see if it has been changed. # This would be a useful patch for the Makefile. subby = subprocess.Popen(["make", "-p", "--dry-run", "-f" + os.path.join(adiumdir, "Makefile")], stdout = subprocess.PIPE) (makeout, _) = subby.communicate() # Get the build directory buildmatch = re.search("BUILD_DIR = (.*)", makeout) sys.exit("Could not determine the location of the Adium build directory.") # This is a bit of a hack, but since we're one level deep, move up and then builddir = os.path.realpath(os.path.expanduser(buildmatch.group(1))) analyzerdir = os.path.join(builddir, "Analyzer") # If the analyzer directory isn't available, we need to make it if not os.path.isdir(analyzerdir): sys.exit("Could not create the build directory %s" % analyzerdir) # Ensure we have write access if not os.access(analyzerdir, os.W_OK): sys.exit("Cannot write to build directory %s" % analyzerdir) # Now we're going to make a directory for this revision. We're going to rename # the old version if it exists. htmldir = os.path.join(analyzerdir, "r" + revision) if os.path.exists(htmldir): oldhtmldir = htmldir + "-" + str(int(os.stat(htmldir).st_ctime)) shutil.move(htmldir, oldhtmldir) sys.exit("Could not move previous output files out of the way.") # Set up the Clang environment. Most of the code in this section is adapted from # Clang's scan-build script. Many of the comments are lifted from it. # Start with our environment as it is and build on it clangenv[key] = os.environ[key] # Rather than use GCC, we will use ccc-analyzer, the GCC interceptor provided clangenv["CC"] = which("ccc-analyzer") # We need to specify where Clang is clangenv["CLANG"] = which("clang") # This is the HTML directory to output to clangenv["CCC_ANALYZER_HTML"] = htmldir # These are the analyses to run, as set at the head of the file for opt in analyzer_options: if analyzer_options[opt]: clangenv["CCC_ANALYZER_ANALYSIS"] = analyses # These are for debugging verbosity clangenv["CCC_ANALYZER_VERBOSE"] = "1" clangenv["CCC_ANALYZER_LOG"] = "1" # When $CC is set, xcodebuild uses it to do all linking, even if we are linking # C++ object files. Set $LDPLUSPLUS so that xcode uses `g++` when linking them. clangenv["LDPLUSPLUS"] = which("g++") # Construct the command we will use to build. # Here's the basic command, based on the Adium Makefile buildcmd = [which("xcodebuild"), "-project", "Adium.xcodeproj", "-configuration", build_configuration, "build"] # Now add on some extra options, courtesy of the scan-build folks buildcmd.append("-nodistribute") # Disable distributed builds buildcmd.append("GCC_PRECOMPILE_PREFIX_HEADER=NO") # Disable PCH files until Clang supports them #buildcmd.append("-PBXBuildsContinueAfterErrors=YES") # Continue building even if a build error occurs # To ensure that we get a complete build, we want to move the build # configuration directory out of the way. If they interrupt the process, # we have to move it back. configdir = os.path.join(builddir, build_configuration) needsRestore = os.path.exists(configdir) tempdir = os.tempnam(builddir, build_configuration + "-") shutil.move(configdir, tempdir) sys.exit("Could not move build files out of the way.") # The following code has to be run in a try...except block so that if the user # interrupts, we can revert changes. # This will start us going subby = subprocess.Popen(buildcmd, stdout = subprocess.PIPE, env = clangenv, cwd = os.path.join(os.getcwd(), os.pardir, os.pardir)) print "Building Adium using %s configuration." % build_configuration sys.stdout.write("Waiting for static analysis to begin...") prevlen = len("Waiting for static analysis to begin...") # Read in each line of output to see if it's of interest to us pattern = re.compile("^ANALYZE\s*(.*?): (.*?\.[mc]) (.*)$") for line in subby.stdout: match = pattern.match(line) path = match.group(2)[len(adiumdir) + 1:] output = "Analyzing " + os.path.basename(path) + "..." lendiff = prevlen - len(output) output = output + " " * lendiff + "\x08" * lendiff sys.stdout.write("\r" + output) # Shut Python's default KeyboardInterrupt message up except KeyboardInterrupt: # Now whatever happened, we want to move the build directory back # Print out our current status output = "User interrupted build process -- gracefully aborting." output = "Static analysis complete. " numreports = len(os.listdir(htmldir)) output += "No reports generated." output += "1 report generated." output += "%d reports generated." % numreports lendiff = prevlen - len(output) output = output + " " * lendiff + "\x08" * lendiff if os.path.exists(configdir): shutil.move(tempdir, configdir) print "Could not restore original build files from %s" % tempdir[len(adiumdir) + 1:] # Postprocess the generated reports. # Walk the report directory and parse stuff output = "Post-processing reports..." prevlen = len("Post-processing reports...") for f in os.listdir(htmldir): pth = os.path.join(htmldir, f) if not os.path.isfile(pth) or not f.startswith("report-"): output = "Post-processing report %s" % f lendiff = prevlen - len(output) output = output + " " * lendiff + "\x08" * lendiff sys.stdout.write("\r" + output) # Feed the file into the parser p = reports.ReportParser(adiumdir) reportlist.append(p.report) output = "Done post-processing reports." lendiff = prevlen - len(output) output = output + " " * lendiff + "\x08" * lendiff sys.stdout.write("\r" + output + "\n") # Write out a property list that contains all of the report summaries. Later we # can compare this with different summaries to see which problems are new. print "Writing out Summary.plist file...", # Open up the output file outfile = open(os.path.join(htmldir, "Summary.plist"), "w") outfile.write(reports.summaryPlistForReports(reportlist, revision))