blob: 6588f6bd2495f27d779e0f5db6fdf54fff5056bd [file] [log] [blame]
Anna Zaksf0c41162011-10-06 23:26:27 +00001#!/usr/bin/env python
2
3"""
4Static Analyzer qualification infrastructure.
5
6The goal is to test the analyzer against different projects, check for failures,
7compare results, and measure performance.
8
9Repository Directory will contain sources of the projects as well as the
10information on how to build them and the expected output.
11Repository Directory structure:
12 - ProjectMap file
13 - Historical Performance Data
14 - Project Dir1
15 - ReferenceOutput
16 - Project Dir2
17 - ReferenceOutput
18 ..
19
20To test the build of the analyzer one would:
21 - Copy over a copy of the Repository Directory. (TODO: Prefer to ensure that
22 the build directory does not pollute the repository to min network traffic).
23 - Build all projects, until error. Produce logs to report errors.
24 - Compare results.
25
26The files which should be kept around for failure investigations:
27 RepositoryCopy/Project DirI/ScanBuildResults
28 RepositoryCopy/Project DirI/run_static_analyzer.log
29
30Assumptions (TODO: shouldn't need to assume these.):
31 The script is being run from the Repository Directory.
Anna Zaks42a44632011-11-02 20:46:50 +000032 The compiler for scan-build and scan-build are in the PATH.
Anna Zaksf0c41162011-10-06 23:26:27 +000033 export PATH=/Users/zaks/workspace/c2llvm/build/Release+Asserts/bin:$PATH
34
35For more logging, set the env variables:
36 zaks:TI zaks$ export CCC_ANALYZER_LOG=1
37 zaks:TI zaks$ export CCC_ANALYZER_VERBOSE=1
38"""
39import CmpRuns
40
41import os
42import csv
43import sys
44import glob
Ted Kremenekf9a539d2012-08-28 20:40:04 +000045import math
Anna Zaksf0c41162011-10-06 23:26:27 +000046import shutil
47import time
48import plistlib
Gabor Horvath93fde942015-06-30 15:31:17 +000049import argparse
Anna Zaks4720a732011-11-05 05:20:48 +000050from subprocess import check_call, CalledProcessError
Anna Zaksf0c41162011-10-06 23:26:27 +000051
Ted Kremenek42c14422012-08-28 20:40:02 +000052#------------------------------------------------------------------------------
53# Helper functions.
54#------------------------------------------------------------------------------
Anna Zaksf0c41162011-10-06 23:26:27 +000055
Ted Kremenekf9a539d2012-08-28 20:40:04 +000056def detectCPUs():
57 """
58 Detects the number of CPUs on a system. Cribbed from pp.
59 """
60 # Linux, Unix and MacOS:
61 if hasattr(os, "sysconf"):
62 if os.sysconf_names.has_key("SC_NPROCESSORS_ONLN"):
63 # Linux & Unix:
64 ncpus = os.sysconf("SC_NPROCESSORS_ONLN")
65 if isinstance(ncpus, int) and ncpus > 0:
66 return ncpus
67 else: # OSX:
68 return int(capture(['sysctl', '-n', 'hw.ncpu']))
69 # Windows:
70 if os.environ.has_key("NUMBER_OF_PROCESSORS"):
71 ncpus = int(os.environ["NUMBER_OF_PROCESSORS"])
72 if ncpus > 0:
73 return ncpus
74 return 1 # Default
75
Ted Kremenek6cb20802012-08-28 20:20:52 +000076def which(command, paths = None):
77 """which(command, [paths]) - Look up the given command in the paths string
78 (or the PATH environment variable, if unspecified)."""
79
80 if paths is None:
81 paths = os.environ.get('PATH','')
82
83 # Check for absolute match first.
84 if os.path.exists(command):
85 return command
86
87 # Would be nice if Python had a lib function for this.
88 if not paths:
89 paths = os.defpath
90
91 # Get suffixes to search.
92 # On Cygwin, 'PATHEXT' may exist but it should not be used.
93 if os.pathsep == ';':
94 pathext = os.environ.get('PATHEXT', '').split(';')
95 else:
96 pathext = ['']
97
98 # Search the paths...
99 for path in paths.split(os.pathsep):
100 for ext in pathext:
101 p = os.path.join(path, command + ext)
102 if os.path.exists(p):
103 return p
104
105 return None
106
Anna Zaksde1f7f8b2012-01-10 18:10:25 +0000107# Make sure we flush the output after every print statement.
108class flushfile(object):
109 def __init__(self, f):
110 self.f = f
111 def write(self, x):
112 self.f.write(x)
113 self.f.flush()
114
115sys.stdout = flushfile(sys.stdout)
116
Anna Zaksf0c41162011-10-06 23:26:27 +0000117def getProjectMapPath():
118 ProjectMapPath = os.path.join(os.path.abspath(os.curdir),
119 ProjectMapFile)
120 if not os.path.exists(ProjectMapPath):
121 print "Error: Cannot find the Project Map file " + ProjectMapPath +\
122 "\nRunning script for the wrong directory?"
123 sys.exit(-1)
124 return ProjectMapPath
125
126def getProjectDir(ID):
127 return os.path.join(os.path.abspath(os.curdir), ID)
128
Jordan Rose01ac5722012-06-01 16:24:38 +0000129def getSBOutputDirName(IsReferenceBuild) :
Anna Zaks4720a732011-11-05 05:20:48 +0000130 if IsReferenceBuild == True :
131 return SBOutputDirReferencePrefix + SBOutputDirName
132 else :
133 return SBOutputDirName
134
Ted Kremenek42c14422012-08-28 20:40:02 +0000135#------------------------------------------------------------------------------
136# Configuration setup.
137#------------------------------------------------------------------------------
138
139# Find Clang for static analysis.
140Clang = which("clang", os.environ['PATH'])
141if not Clang:
142 print "Error: cannot find 'clang' in PATH"
143 sys.exit(-1)
144
Ted Kremenekf9a539d2012-08-28 20:40:04 +0000145# Number of jobs.
Jordan Rose88329242012-11-16 17:41:21 +0000146Jobs = int(math.ceil(detectCPUs() * 0.75))
Ted Kremenekf9a539d2012-08-28 20:40:04 +0000147
Ted Kremenek42c14422012-08-28 20:40:02 +0000148# Project map stores info about all the "registered" projects.
149ProjectMapFile = "projectMap.csv"
150
151# Names of the project specific scripts.
152# The script that needs to be executed before the build can start.
153CleanupScript = "cleanup_run_static_analyzer.sh"
154# This is a file containing commands for scan-build.
155BuildScript = "run_static_analyzer.cmd"
156
157# The log file name.
158LogFolderName = "Logs"
159BuildLogName = "run_static_analyzer.log"
160# Summary file - contains the summary of the failures. Ex: This info can be be
161# displayed when buildbot detects a build failure.
162NumOfFailuresInSummary = 10
163FailuresSummaryFileName = "failures.txt"
164# Summary of the result diffs.
165DiffsSummaryFileName = "diffs.txt"
166
167# The scan-build result directory.
168SBOutputDirName = "ScanBuildResults"
169SBOutputDirReferencePrefix = "Ref"
170
171# The list of checkers used during analyzes.
Alp Tokerd4733632013-12-05 04:47:09 +0000172# Currently, consists of all the non-experimental checkers, plus a few alpha
Jordan Rose10ad0812013-04-05 17:55:07 +0000173# checkers we don't want to regress on.
Anna Zaks8a020312014-10-31 17:40:14 +0000174Checkers="alpha.unix.SimpleStream,alpha.security.taint,cplusplus.NewDeleteLeaks,core,cplusplus,deadcode,security,unix,osx"
Ted Kremenek42c14422012-08-28 20:40:02 +0000175
176Verbose = 1
177
178#------------------------------------------------------------------------------
179# Test harness logic.
180#------------------------------------------------------------------------------
181
Anna Zaksf0c41162011-10-06 23:26:27 +0000182# Run pre-processing script if any.
Anna Zaks42a44632011-11-02 20:46:50 +0000183def runCleanupScript(Dir, PBuildLogFile):
184 ScriptPath = os.path.join(Dir, CleanupScript)
Anna Zaksf0c41162011-10-06 23:26:27 +0000185 if os.path.exists(ScriptPath):
186 try:
187 if Verbose == 1:
188 print " Executing: %s" % (ScriptPath,)
189 check_call("chmod +x %s" % ScriptPath, cwd = Dir,
190 stderr=PBuildLogFile,
191 stdout=PBuildLogFile,
192 shell=True)
193 check_call(ScriptPath, cwd = Dir, stderr=PBuildLogFile,
194 stdout=PBuildLogFile,
195 shell=True)
196 except:
197 print "Error: The pre-processing step failed. See ", \
198 PBuildLogFile.name, " for details."
199 sys.exit(-1)
200
201# Build the project with scan-build by reading in the commands and
202# prefixing them with the scan-build options.
203def runScanBuild(Dir, SBOutputDir, PBuildLogFile):
204 BuildScriptPath = os.path.join(Dir, BuildScript)
205 if not os.path.exists(BuildScriptPath):
206 print "Error: build script is not defined: %s" % BuildScriptPath
Ted Kremenek6cb20802012-08-28 20:20:52 +0000207 sys.exit(-1)
208 SBOptions = "--use-analyzer " + Clang + " "
209 SBOptions += "-plist-html -o " + SBOutputDir + " "
Anna Zaks26573c52011-11-08 22:41:25 +0000210 SBOptions += "-enable-checker " + Checkers + " "
Jordan Roseb18179d2013-01-24 23:07:59 +0000211 SBOptions += "--keep-empty "
Anna Zaks7b4f8a42013-05-31 02:31:09 +0000212 # Always use ccc-analyze to ensure that we can locate the failures
213 # directory.
214 SBOptions += "--override-compiler "
Anna Zaksf0c41162011-10-06 23:26:27 +0000215 try:
216 SBCommandFile = open(BuildScriptPath, "r")
217 SBPrefix = "scan-build " + SBOptions + " "
218 for Command in SBCommandFile:
Jordan Rose7bd91862013-09-06 16:12:41 +0000219 Command = Command.strip()
Gabor Horvath93fde942015-06-30 15:31:17 +0000220 if len(Command) == 0:
221 continue;
Ted Kremenekf9a539d2012-08-28 20:40:04 +0000222 # If using 'make', auto imply a -jX argument
223 # to speed up analysis. xcodebuild will
224 # automatically use the maximum number of cores.
Jordan Rose64e4cf02012-11-26 19:59:57 +0000225 if (Command.startswith("make ") or Command == "make") and \
226 "-j" not in Command:
Jordan Rose88329242012-11-16 17:41:21 +0000227 Command += " -j%d" % Jobs
Anna Zaksf0c41162011-10-06 23:26:27 +0000228 SBCommand = SBPrefix + Command
229 if Verbose == 1:
230 print " Executing: %s" % (SBCommand,)
231 check_call(SBCommand, cwd = Dir, stderr=PBuildLogFile,
232 stdout=PBuildLogFile,
233 shell=True)
234 except:
235 print "Error: scan-build failed. See ",PBuildLogFile.name,\
236 " for details."
Anna Zaks4720a732011-11-05 05:20:48 +0000237 raise
Anna Zaksf0c41162011-10-06 23:26:27 +0000238
Anna Zaks4720a732011-11-05 05:20:48 +0000239def hasNoExtension(FileName):
240 (Root, Ext) = os.path.splitext(FileName)
241 if ((Ext == "")) :
242 return True
243 return False
244
245def isValidSingleInputFile(FileName):
246 (Root, Ext) = os.path.splitext(FileName)
247 if ((Ext == ".i") | (Ext == ".ii") |
248 (Ext == ".c") | (Ext == ".cpp") |
249 (Ext == ".m") | (Ext == "")) :
250 return True
251 return False
Ted Kremenek6cb20802012-08-28 20:20:52 +0000252
Anna Zaks4720a732011-11-05 05:20:48 +0000253# Run analysis on a set of preprocessed files.
Anna Zaksa2f970b2012-09-06 23:30:27 +0000254def runAnalyzePreprocessed(Dir, SBOutputDir, Mode):
Anna Zaks4720a732011-11-05 05:20:48 +0000255 if os.path.exists(os.path.join(Dir, BuildScript)):
256 print "Error: The preprocessed files project should not contain %s" % \
257 BuildScript
258 raise Exception()
259
Ted Kremenek6cb20802012-08-28 20:20:52 +0000260 CmdPrefix = Clang + " -cc1 -analyze -analyzer-output=plist -w "
Anna Zaksa08c6b72012-01-21 01:11:35 +0000261 CmdPrefix += "-analyzer-checker=" + Checkers +" -fcxx-exceptions -fblocks "
Anna Zaks4720a732011-11-05 05:20:48 +0000262
Anna Zaksa2f970b2012-09-06 23:30:27 +0000263 if (Mode == 2) :
264 CmdPrefix += "-std=c++11 "
265
Anna Zaks4720a732011-11-05 05:20:48 +0000266 PlistPath = os.path.join(Dir, SBOutputDir, "date")
267 FailPath = os.path.join(PlistPath, "failures");
268 os.makedirs(FailPath);
269
270 for FullFileName in glob.glob(Dir + "/*"):
271 FileName = os.path.basename(FullFileName)
272 Failed = False
273
274 # Only run the analyzes on supported files.
275 if (hasNoExtension(FileName)):
276 continue
277 if (isValidSingleInputFile(FileName) == False):
278 print "Error: Invalid single input file %s." % (FullFileName,)
279 raise Exception()
280
281 # Build and call the analyzer command.
282 OutputOption = "-o " + os.path.join(PlistPath, FileName) + ".plist "
283 Command = CmdPrefix + OutputOption + os.path.join(Dir, FileName)
284 LogFile = open(os.path.join(FailPath, FileName + ".stderr.txt"), "w+b")
285 try:
286 if Verbose == 1:
287 print " Executing: %s" % (Command,)
288 check_call(Command, cwd = Dir, stderr=LogFile,
289 stdout=LogFile,
290 shell=True)
291 except CalledProcessError, e:
292 print "Error: Analyzes of %s failed. See %s for details." \
293 "Error code %d." % \
294 (FullFileName, LogFile.name, e.returncode)
295 Failed = True
296 finally:
297 LogFile.close()
298
299 # If command did not fail, erase the log file.
300 if Failed == False:
301 os.remove(LogFile.name);
302
Anna Zaksa2f970b2012-09-06 23:30:27 +0000303def buildProject(Dir, SBOutputDir, ProjectBuildMode, IsReferenceBuild):
Anna Zaksf0c41162011-10-06 23:26:27 +0000304 TBegin = time.time()
305
Anna Zaks4720a732011-11-05 05:20:48 +0000306 BuildLogPath = os.path.join(SBOutputDir, LogFolderName, BuildLogName)
Anna Zaksf0c41162011-10-06 23:26:27 +0000307 print "Log file: %s" % (BuildLogPath,)
Anna Zaks4720a732011-11-05 05:20:48 +0000308 print "Output directory: %s" %(SBOutputDir, )
309
Anna Zaksf0c41162011-10-06 23:26:27 +0000310 # Clean up the log file.
311 if (os.path.exists(BuildLogPath)) :
312 RmCommand = "rm " + BuildLogPath
313 if Verbose == 1:
Anna Zaks42a44632011-11-02 20:46:50 +0000314 print " Executing: %s" % (RmCommand,)
Anna Zaksf0c41162011-10-06 23:26:27 +0000315 check_call(RmCommand, shell=True)
Anna Zaks4720a732011-11-05 05:20:48 +0000316
317 # Clean up scan build results.
318 if (os.path.exists(SBOutputDir)) :
319 RmCommand = "rm -r " + SBOutputDir
320 if Verbose == 1:
321 print " Executing: %s" % (RmCommand,)
322 check_call(RmCommand, shell=True)
323 assert(not os.path.exists(SBOutputDir))
324 os.makedirs(os.path.join(SBOutputDir, LogFolderName))
Anna Zaksf0c41162011-10-06 23:26:27 +0000325
326 # Open the log file.
327 PBuildLogFile = open(BuildLogPath, "wb+")
Anna Zaksf0c41162011-10-06 23:26:27 +0000328
Anna Zaks4720a732011-11-05 05:20:48 +0000329 # Build and analyze the project.
330 try:
Anna Zaks42a44632011-11-02 20:46:50 +0000331 runCleanupScript(Dir, PBuildLogFile)
Anna Zaks42a44632011-11-02 20:46:50 +0000332
Anna Zaksa2f970b2012-09-06 23:30:27 +0000333 if (ProjectBuildMode == 1):
Anna Zaks4720a732011-11-05 05:20:48 +0000334 runScanBuild(Dir, SBOutputDir, PBuildLogFile)
335 else:
Anna Zaksa2f970b2012-09-06 23:30:27 +0000336 runAnalyzePreprocessed(Dir, SBOutputDir, ProjectBuildMode)
Anna Zaks4720a732011-11-05 05:20:48 +0000337
338 if IsReferenceBuild :
Anna Zaks42a44632011-11-02 20:46:50 +0000339 runCleanupScript(Dir, PBuildLogFile)
340
Anna Zaksf0c41162011-10-06 23:26:27 +0000341 finally:
342 PBuildLogFile.close()
343
344 print "Build complete (time: %.2f). See the log for more details: %s" % \
345 ((time.time()-TBegin), BuildLogPath)
346
347# A plist file is created for each call to the analyzer(each source file).
348# We are only interested on the once that have bug reports, so delete the rest.
349def CleanUpEmptyPlists(SBOutputDir):
350 for F in glob.glob(SBOutputDir + "/*/*.plist"):
351 P = os.path.join(SBOutputDir, F)
352
353 Data = plistlib.readPlist(P)
354 # Delete empty reports.
355 if not Data['files']:
356 os.remove(P)
357 continue
358
359# Given the scan-build output directory, checks if the build failed
360# (by searching for the failures directories). If there are failures, it
361# creates a summary file in the output directory.
362def checkBuild(SBOutputDir):
363 # Check if there are failures.
364 Failures = glob.glob(SBOutputDir + "/*/failures/*.stderr.txt")
365 TotalFailed = len(Failures);
366 if TotalFailed == 0:
Jordan Rose9858b122012-08-31 00:36:30 +0000367 CleanUpEmptyPlists(SBOutputDir)
368 Plists = glob.glob(SBOutputDir + "/*/*.plist")
Alp Tokerd4733632013-12-05 04:47:09 +0000369 print "Number of bug reports (non-empty plist files) produced: %d" %\
Jordan Rose9858b122012-08-31 00:36:30 +0000370 len(Plists)
Anna Zaksf0c41162011-10-06 23:26:27 +0000371 return;
372
373 # Create summary file to display when the build fails.
Anna Zaks4720a732011-11-05 05:20:48 +0000374 SummaryPath = os.path.join(SBOutputDir, LogFolderName, FailuresSummaryFileName)
Anna Zaksf0c41162011-10-06 23:26:27 +0000375 if (Verbose > 0):
Anna Zaks4720a732011-11-05 05:20:48 +0000376 print " Creating the failures summary file %s" % (SummaryPath,)
Anna Zaksf0c41162011-10-06 23:26:27 +0000377
378 SummaryLog = open(SummaryPath, "w+")
379 try:
380 SummaryLog.write("Total of %d failures discovered.\n" % (TotalFailed,))
381 if TotalFailed > NumOfFailuresInSummary:
382 SummaryLog.write("See the first %d below.\n"
383 % (NumOfFailuresInSummary,))
384 # TODO: Add a line "See the results folder for more."
385
386 FailuresCopied = NumOfFailuresInSummary
387 Idx = 0
Jordan Rosedc191a12012-06-01 16:24:43 +0000388 for FailLogPathI in Failures:
Anna Zaksf0c41162011-10-06 23:26:27 +0000389 if Idx >= NumOfFailuresInSummary:
390 break;
391 Idx += 1
392 SummaryLog.write("\n-- Error #%d -----------\n" % (Idx,));
393 FailLogI = open(FailLogPathI, "r");
394 try:
395 shutil.copyfileobj(FailLogI, SummaryLog);
396 finally:
397 FailLogI.close()
398 finally:
399 SummaryLog.close()
400
Anna Zaks5acd9602012-01-04 23:53:50 +0000401 print "Error: analysis failed. See ", SummaryPath
Anna Zaksf0c41162011-10-06 23:26:27 +0000402 sys.exit(-1)
403
404# Auxiliary object to discard stdout.
405class Discarder(object):
406 def write(self, text):
407 pass # do nothing
408
409# Compare the warnings produced by scan-build.
Gabor Horvath93fde942015-06-30 15:31:17 +0000410# Strictness defines the success criteria for the test:
411# 0 - success if there are no crashes or analyzer failure.
412# 1 - success if there are no difference in the number of reported bugs.
413# 2 - success if all the bug reports are identical.
414def runCmpResults(Dir, Strictness = 0):
Anna Zaksf0c41162011-10-06 23:26:27 +0000415 TBegin = time.time()
416
417 RefDir = os.path.join(Dir, SBOutputDirReferencePrefix + SBOutputDirName)
418 NewDir = os.path.join(Dir, SBOutputDirName)
419
420 # We have to go one level down the directory tree.
421 RefList = glob.glob(RefDir + "/*")
422 NewList = glob.glob(NewDir + "/*")
Anna Zaks4720a732011-11-05 05:20:48 +0000423
Jordan Rosec7b992e2013-06-10 19:34:30 +0000424 # Log folders are also located in the results dir, so ignore them.
425 RefLogDir = os.path.join(RefDir, LogFolderName)
426 if RefLogDir in RefList:
427 RefList.remove(RefLogDir)
Anna Zaks4720a732011-11-05 05:20:48 +0000428 NewList.remove(os.path.join(NewDir, LogFolderName))
429
Anna Zaksf0c41162011-10-06 23:26:27 +0000430 if len(RefList) == 0 or len(NewList) == 0:
431 return False
432 assert(len(RefList) == len(NewList))
433
434 # There might be more then one folder underneath - one per each scan-build
435 # command (Ex: one for configure and one for make).
436 if (len(RefList) > 1):
437 # Assume that the corresponding folders have the same names.
438 RefList.sort()
439 NewList.sort()
440
441 # Iterate and find the differences.
Anna Zaks767d3562011-11-08 19:56:31 +0000442 NumDiffs = 0
Anna Zaksf0c41162011-10-06 23:26:27 +0000443 PairList = zip(RefList, NewList)
444 for P in PairList:
445 RefDir = P[0]
446 NewDir = P[1]
447
448 assert(RefDir != NewDir)
449 if Verbose == 1:
450 print " Comparing Results: %s %s" % (RefDir, NewDir)
451
452 DiffsPath = os.path.join(NewDir, DiffsSummaryFileName)
453 Opts = CmpRuns.CmpOptions(DiffsPath)
454 # Discard everything coming out of stdout (CmpRun produces a lot of them).
455 OLD_STDOUT = sys.stdout
456 sys.stdout = Discarder()
457 # Scan the results, delete empty plist files.
Gabor Horvath93fde942015-06-30 15:31:17 +0000458 NumDiffs, ReportsInRef, ReportsInNew = \
459 CmpRuns.dumpScanBuildResultsDiff(RefDir, NewDir, Opts, False)
Anna Zaksf0c41162011-10-06 23:26:27 +0000460 sys.stdout = OLD_STDOUT
Anna Zaks767d3562011-11-08 19:56:31 +0000461 if (NumDiffs > 0) :
462 print "Warning: %r differences in diagnostics. See %s" % \
463 (NumDiffs, DiffsPath,)
Gabor Horvath93fde942015-06-30 15:31:17 +0000464 if Strictness >= 2 and NumDiffs > 0:
465 print "Error: Diffs found in strict mode (2)."
466 sys.exit(-1)
467 elif Strictness >= 1 and ReportsInRef != ReportsInNew:
468 print "Error: The number of results are different in strict mode (1)."
469 sys.exit(-1)
Anna Zaksf0c41162011-10-06 23:26:27 +0000470
471 print "Diagnostic comparison complete (time: %.2f)." % (time.time()-TBegin)
Anna Zaks767d3562011-11-08 19:56:31 +0000472 return (NumDiffs > 0)
Anna Zaks4720a732011-11-05 05:20:48 +0000473
Anna Zaks4c1ef9762012-02-03 06:35:23 +0000474def updateSVN(Mode, ProjectsMap):
475 try:
476 ProjectsMap.seek(0)
477 for I in csv.reader(ProjectsMap):
478 ProjName = I[0]
Jordan Rose01ac5722012-06-01 16:24:38 +0000479 Path = os.path.join(ProjName, getSBOutputDirName(True))
Anna Zaks4c1ef9762012-02-03 06:35:23 +0000480
481 if Mode == "delete":
482 Command = "svn delete %s" % (Path,)
483 else:
484 Command = "svn add %s" % (Path,)
Anna Zaksf0c41162011-10-06 23:26:27 +0000485
Anna Zaks4c1ef9762012-02-03 06:35:23 +0000486 if Verbose == 1:
487 print " Executing: %s" % (Command,)
Jordan Rosedc191a12012-06-01 16:24:43 +0000488 check_call(Command, shell=True)
Anna Zaks4c1ef9762012-02-03 06:35:23 +0000489
490 if Mode == "delete":
491 CommitCommand = "svn commit -m \"[analyzer tests] Remove " \
492 "reference results.\""
493 else:
494 CommitCommand = "svn commit -m \"[analyzer tests] Add new " \
495 "reference results.\""
496 if Verbose == 1:
497 print " Executing: %s" % (CommitCommand,)
Jordan Rosedc191a12012-06-01 16:24:43 +0000498 check_call(CommitCommand, shell=True)
Anna Zaks4c1ef9762012-02-03 06:35:23 +0000499 except:
500 print "Error: SVN update failed."
501 sys.exit(-1)
502
Gabor Horvath93fde942015-06-30 15:31:17 +0000503def testProject(ID, ProjectBuildMode, IsReferenceBuild=False, Dir=None, Strictness = 0):
Anna Zaks4720a732011-11-05 05:20:48 +0000504 print " \n\n--- Building project %s" % (ID,)
505
Anna Zaksf0c41162011-10-06 23:26:27 +0000506 TBegin = time.time()
507
508 if Dir is None :
509 Dir = getProjectDir(ID)
510 if Verbose == 1:
511 print " Build directory: %s." % (Dir,)
512
513 # Set the build results directory.
Jordan Rose01ac5722012-06-01 16:24:38 +0000514 RelOutputDir = getSBOutputDirName(IsReferenceBuild)
Anna Zaks4c1ef9762012-02-03 06:35:23 +0000515 SBOutputDir = os.path.join(Dir, RelOutputDir)
516
Anna Zaksa2f970b2012-09-06 23:30:27 +0000517 buildProject(Dir, SBOutputDir, ProjectBuildMode, IsReferenceBuild)
Anna Zaksf0c41162011-10-06 23:26:27 +0000518
519 checkBuild(SBOutputDir)
520
Jordan Rose9858b122012-08-31 00:36:30 +0000521 if IsReferenceBuild == False:
Gabor Horvath93fde942015-06-30 15:31:17 +0000522 runCmpResults(Dir, Strictness)
Anna Zaksf0c41162011-10-06 23:26:27 +0000523
524 print "Completed tests for project %s (time: %.2f)." % \
525 (ID, (time.time()-TBegin))
526
Gabor Horvath93fde942015-06-30 15:31:17 +0000527def testAll(IsReferenceBuild = False, UpdateSVN = False, Strictness = 0):
Anna Zaksf0c41162011-10-06 23:26:27 +0000528 PMapFile = open(getProjectMapPath(), "rb")
Anna Zaks4c1ef9762012-02-03 06:35:23 +0000529 try:
530 # Validate the input.
531 for I in csv.reader(PMapFile):
Anna Zaks4720a732011-11-05 05:20:48 +0000532 if (len(I) != 2) :
533 print "Error: Rows in the ProjectMapFile should have 3 entries."
534 raise Exception()
Anna Zaksa2f970b2012-09-06 23:30:27 +0000535 if (not ((I[1] == "0") | (I[1] == "1") | (I[1] == "2"))):
536 print "Error: Second entry in the ProjectMapFile should be 0" \
537 " (single file), 1 (project), or 2(single file c++11)."
Anna Zaks4720a732011-11-05 05:20:48 +0000538 raise Exception()
Anna Zaks4c1ef9762012-02-03 06:35:23 +0000539
540 # When we are regenerating the reference results, we might need to
541 # update svn. Remove reference results from SVN.
542 if UpdateSVN == True:
Jordan Rose01ac5722012-06-01 16:24:38 +0000543 assert(IsReferenceBuild == True);
Anna Zaks4c1ef9762012-02-03 06:35:23 +0000544 updateSVN("delete", PMapFile);
545
546 # Test the projects.
547 PMapFile.seek(0)
548 for I in csv.reader(PMapFile):
Gabor Horvath93fde942015-06-30 15:31:17 +0000549 testProject(I[0], int(I[1]), IsReferenceBuild, None, Strictness)
Anna Zaks4c1ef9762012-02-03 06:35:23 +0000550
551 # Add reference results to SVN.
552 if UpdateSVN == True:
553 updateSVN("add", PMapFile);
554
Anna Zaks4720a732011-11-05 05:20:48 +0000555 except:
556 print "Error occurred. Premature termination."
557 raise
Anna Zaksf0c41162011-10-06 23:26:27 +0000558 finally:
559 PMapFile.close()
560
561if __name__ == '__main__':
Gabor Horvath93fde942015-06-30 15:31:17 +0000562 # Parse command line arguments.
563 Parser = argparse.ArgumentParser(description='Test the Clang Static Analyzer.')
564 Parser.add_argument('--strictness', dest='strictness', type=int, default=0,
565 help='0 to fail on runtime errors, 1 to fail when the number\
566 of found bugs are different from the reference, 2 to \
567 fail on any difference from the reference. Default is 0.')
568 Parser.add_argument('-r', dest='regenerate', action='store_true', default=False,
569 help='Regenerate reference output.')
570 Parser.add_argument('-rs', dest='update_reference', action='store_true',
571 default=False, help='Regenerate reference output and update svn.')
572 Args = Parser.parse_args()
573
Anna Zaks4c1ef9762012-02-03 06:35:23 +0000574 IsReference = False
575 UpdateSVN = False
Gabor Horvath93fde942015-06-30 15:31:17 +0000576 Strictness = Args.strictness
577 if Args.regenerate:
578 IsReference = True
579 elif Args.update_reference:
580 IsReference = True
581 UpdateSVN = True
Anna Zaks4c1ef9762012-02-03 06:35:23 +0000582
Gabor Horvath93fde942015-06-30 15:31:17 +0000583 testAll(IsReference, UpdateSVN, Strictness)