diff options
Diffstat (limited to 'utils/analyzer/SATestBuild.py')
-rw-r--r-- | utils/analyzer/SATestBuild.py | 163 |
1 files changed, 123 insertions, 40 deletions
diff --git a/utils/analyzer/SATestBuild.py b/utils/analyzer/SATestBuild.py index fd4bc8a..9412358 100644 --- a/utils/analyzer/SATestBuild.py +++ b/utils/analyzer/SATestBuild.py @@ -42,39 +42,66 @@ import os import csv import sys import glob +import math import shutil import time import plistlib from subprocess import check_call, CalledProcessError -# Project map stores info about all the "registered" projects. -ProjectMapFile = "projectMap.csv" - -# Names of the project specific scripts. -# The script that needs to be executed before the build can start. -CleanupScript = "cleanup_run_static_analyzer.sh" -# This is a file containing commands for scan-build. -BuildScript = "run_static_analyzer.cmd" - -# The log file name. -LogFolderName = "Logs" -BuildLogName = "run_static_analyzer.log" -# Summary file - contains the summary of the failures. Ex: This info can be be -# displayed when buildbot detects a build failure. -NumOfFailuresInSummary = 10 -FailuresSummaryFileName = "failures.txt" -# Summary of the result diffs. -DiffsSummaryFileName = "diffs.txt" - -# The scan-build result directory. -SBOutputDirName = "ScanBuildResults" -SBOutputDirReferencePrefix = "Ref" - -# The list of checkers used during analyzes. -# Currently, consists of all the non experimental checkers. -Checkers="experimental.security.taint,core,deadcode,security,unix,osx" - -Verbose = 1 +#------------------------------------------------------------------------------ +# Helper functions. +#------------------------------------------------------------------------------ + +def detectCPUs(): + """ + Detects the number of CPUs on a system. Cribbed from pp. + """ + # Linux, Unix and MacOS: + if hasattr(os, "sysconf"): + if os.sysconf_names.has_key("SC_NPROCESSORS_ONLN"): + # Linux & Unix: + ncpus = os.sysconf("SC_NPROCESSORS_ONLN") + if isinstance(ncpus, int) and ncpus > 0: + return ncpus + else: # OSX: + return int(capture(['sysctl', '-n', 'hw.ncpu'])) + # Windows: + if os.environ.has_key("NUMBER_OF_PROCESSORS"): + ncpus = int(os.environ["NUMBER_OF_PROCESSORS"]) + if ncpus > 0: + return ncpus + return 1 # Default + +def which(command, paths = None): + """which(command, [paths]) - Look up the given command in the paths string + (or the PATH environment variable, if unspecified).""" + + if paths is None: + paths = os.environ.get('PATH','') + + # Check for absolute match first. + if os.path.exists(command): + return command + + # Would be nice if Python had a lib function for this. + if not paths: + paths = os.defpath + + # Get suffixes to search. + # On Cygwin, 'PATHEXT' may exist but it should not be used. + if os.pathsep == ';': + pathext = os.environ.get('PATHEXT', '').split(';') + else: + pathext = [''] + + # Search the paths... + for path in paths.split(os.pathsep): + for ext in pathext: + p = os.path.join(path, command + ext) + if os.path.exists(p): + return p + + return None # Make sure we flush the output after every print statement. class flushfile(object): @@ -104,6 +131,52 @@ def getSBOutputDirName(IsReferenceBuild) : else : return SBOutputDirName +#------------------------------------------------------------------------------ +# Configuration setup. +#------------------------------------------------------------------------------ + +# Find Clang for static analysis. +Clang = which("clang", os.environ['PATH']) +if not Clang: + print "Error: cannot find 'clang' in PATH" + sys.exit(-1) + +# Number of jobs. +Jobs = math.ceil(detectCPUs() * 0.75) + +# Project map stores info about all the "registered" projects. +ProjectMapFile = "projectMap.csv" + +# Names of the project specific scripts. +# The script that needs to be executed before the build can start. +CleanupScript = "cleanup_run_static_analyzer.sh" +# This is a file containing commands for scan-build. +BuildScript = "run_static_analyzer.cmd" + +# The log file name. +LogFolderName = "Logs" +BuildLogName = "run_static_analyzer.log" +# Summary file - contains the summary of the failures. Ex: This info can be be +# displayed when buildbot detects a build failure. +NumOfFailuresInSummary = 10 +FailuresSummaryFileName = "failures.txt" +# Summary of the result diffs. +DiffsSummaryFileName = "diffs.txt" + +# The scan-build result directory. +SBOutputDirName = "ScanBuildResults" +SBOutputDirReferencePrefix = "Ref" + +# The list of checkers used during analyzes. +# Currently, consists of all the non experimental checkers. +Checkers="alpha.unix.SimpleStream,alpha.security.taint,core,deadcode,security,unix,osx" + +Verbose = 1 + +#------------------------------------------------------------------------------ +# Test harness logic. +#------------------------------------------------------------------------------ + # Run pre-processing script if any. def runCleanupScript(Dir, PBuildLogFile): ScriptPath = os.path.join(Dir, CleanupScript) @@ -129,13 +202,19 @@ def runScanBuild(Dir, SBOutputDir, PBuildLogFile): BuildScriptPath = os.path.join(Dir, BuildScript) if not os.path.exists(BuildScriptPath): print "Error: build script is not defined: %s" % BuildScriptPath - sys.exit(-1) - SBOptions = "-plist-html -o " + SBOutputDir + " " + sys.exit(-1) + SBOptions = "--use-analyzer " + Clang + " " + SBOptions += "-plist-html -o " + SBOutputDir + " " SBOptions += "-enable-checker " + Checkers + " " try: SBCommandFile = open(BuildScriptPath, "r") SBPrefix = "scan-build " + SBOptions + " " for Command in SBCommandFile: + # If using 'make', auto imply a -jX argument + # to speed up analysis. xcodebuild will + # automatically use the maximum number of cores. + if Command.startswith("make "): + Command += "-j" + Jobs SBCommand = SBPrefix + Command if Verbose == 1: print " Executing: %s" % (SBCommand,) @@ -160,17 +239,20 @@ def isValidSingleInputFile(FileName): (Ext == ".m") | (Ext == "")) : return True return False - + # Run analysis on a set of preprocessed files. -def runAnalyzePreprocessed(Dir, SBOutputDir): +def runAnalyzePreprocessed(Dir, SBOutputDir, Mode): if os.path.exists(os.path.join(Dir, BuildScript)): print "Error: The preprocessed files project should not contain %s" % \ BuildScript raise Exception() - CmdPrefix = "clang -cc1 -analyze -analyzer-output=plist -w " + CmdPrefix = Clang + " -cc1 -analyze -analyzer-output=plist -w " CmdPrefix += "-analyzer-checker=" + Checkers +" -fcxx-exceptions -fblocks " + if (Mode == 2) : + CmdPrefix += "-std=c++11 " + PlistPath = os.path.join(Dir, SBOutputDir, "date") FailPath = os.path.join(PlistPath, "failures"); os.makedirs(FailPath); @@ -208,7 +290,7 @@ def runAnalyzePreprocessed(Dir, SBOutputDir): if Failed == False: os.remove(LogFile.name); -def buildProject(Dir, SBOutputDir, IsScanBuild, IsReferenceBuild): +def buildProject(Dir, SBOutputDir, ProjectBuildMode, IsReferenceBuild): TBegin = time.time() BuildLogPath = os.path.join(SBOutputDir, LogFolderName, BuildLogName) @@ -238,10 +320,10 @@ def buildProject(Dir, SBOutputDir, IsScanBuild, IsReferenceBuild): try: runCleanupScript(Dir, PBuildLogFile) - if IsScanBuild: + if (ProjectBuildMode == 1): runScanBuild(Dir, SBOutputDir, PBuildLogFile) else: - runAnalyzePreprocessed(Dir, SBOutputDir) + runAnalyzePreprocessed(Dir, SBOutputDir, ProjectBuildMode) if IsReferenceBuild : runCleanupScript(Dir, PBuildLogFile) @@ -395,7 +477,7 @@ def updateSVN(Mode, ProjectsMap): print "Error: SVN update failed." sys.exit(-1) -def testProject(ID, IsScanBuild, IsReferenceBuild=False, Dir=None): +def testProject(ID, ProjectBuildMode, IsReferenceBuild=False, Dir=None): print " \n\n--- Building project %s" % (ID,) TBegin = time.time() @@ -409,7 +491,7 @@ def testProject(ID, IsScanBuild, IsReferenceBuild=False, Dir=None): RelOutputDir = getSBOutputDirName(IsReferenceBuild) SBOutputDir = os.path.join(Dir, RelOutputDir) - buildProject(Dir, SBOutputDir, IsScanBuild, IsReferenceBuild) + buildProject(Dir, SBOutputDir, ProjectBuildMode, IsReferenceBuild) checkBuild(SBOutputDir) @@ -427,8 +509,9 @@ def testAll(IsReferenceBuild = False, UpdateSVN = False): if (len(I) != 2) : print "Error: Rows in the ProjectMapFile should have 3 entries." raise Exception() - if (not ((I[1] == "1") | (I[1] == "0"))): - print "Error: Second entry in the ProjectMapFile should be 0 or 1." + if (not ((I[1] == "0") | (I[1] == "1") | (I[1] == "2"))): + print "Error: Second entry in the ProjectMapFile should be 0" \ + " (single file), 1 (project), or 2(single file c++11)." raise Exception() # When we are regenerating the reference results, we might need to |