diff options
Diffstat (limited to 'tools/scan-build')
-rwxr-xr-x | tools/scan-build/ccc-analyzer | 25 | ||||
-rwxr-xr-x | tools/scan-build/scan-build | 162 | ||||
-rwxr-xr-x | tools/scan-build/set-xcode-analyzer | 10 |
3 files changed, 139 insertions, 58 deletions
diff --git a/tools/scan-build/ccc-analyzer b/tools/scan-build/ccc-analyzer index 8717225..bb6dd95 100755 --- a/tools/scan-build/ccc-analyzer +++ b/tools/scan-build/ccc-analyzer @@ -31,11 +31,11 @@ my $DefaultCCompiler; my $DefaultCXXCompiler; if (`uname -a` =~ m/Darwin/) { - $DefaultCCompiler = 'clang'; - $DefaultCXXCompiler = 'clang++'; + $DefaultCCompiler = 'clang'; + $DefaultCXXCompiler = 'clang++'; } else { - $DefaultCCompiler = 'gcc'; - $DefaultCXXCompiler = 'g++'; + $DefaultCCompiler = 'gcc'; + $DefaultCXXCompiler = 'g++'; } if ($FindBin::Script =~ /c\+\+-analyzer/) { @@ -252,6 +252,7 @@ sub Analyze { print $ofh $_; print STDERR $_; } + close $ofh; waitpid($pid,0); close(FROM_CHILD); @@ -269,7 +270,7 @@ sub Analyze { $HtmlDir, $ParserRejects, $ofile); } else { ProcessClangFailure($Clang, $Lang, $file, \@CmdArgsSansAnalyses, - $HtmlDir, $OtherError, $ofile); + $HtmlDir, $OtherError, $ofile); } } else { @@ -389,6 +390,7 @@ my %LangMap = ( 'cxx' => 'c++', 'txx' => 'c++', 'cc' => 'c++', + 'C' => 'c++', 'ii' => 'c++', 'i' => 'c-cpp-output', 'm' => 'objective-c', @@ -489,6 +491,15 @@ foreach (my $i = 0; $i < scalar(@ARGV); ++$i) { while ($Cnt > 0) { ++$i; --$Cnt; push @CompileOpts, $ARGV[$i]; } next; } + if ($Arg =~ /-msse.*/) { + push @CompileOpts,$Arg; + next; + } + # Handle the case where there isn't a space after -iquote + if ($Arg =~ /-iquote.*/) { + push @CompileOpts,$Arg; + next; + } # Options with possible arguments that should pass through to linker. if (defined $LinkerOptionMap{$ArgKey}) { @@ -617,7 +628,7 @@ if ($Action eq 'compile' or $Action eq 'link') { my @Archs = keys %ArchsSeen; # Skip the file if we don't support the architectures specified. exit 0 if ($HadArch && scalar(@Archs) == 0); - + foreach my $file (@Files) { # Determine the language for the file. my $FileLang = $Lang; @@ -671,7 +682,7 @@ if ($Action eq 'compile' or $Action eq 'link') { $ResultFile = $f; # If the HtmlDir is not set, we sould clean up the plist files. if (!defined $HtmlDir || -z $HtmlDir) { - $CleanupFile = $f; + $CleanupFile = $f; } } } diff --git a/tools/scan-build/scan-build b/tools/scan-build/scan-build index a13b235..32eecc0 100755 --- a/tools/scan-build/scan-build +++ b/tools/scan-build/scan-build @@ -17,6 +17,7 @@ use warnings; use FindBin qw($RealBin); use Digest::MD5; use File::Basename; +use File::Find; use Term::ANSIColor; use Term::ANSIColor qw(:constants); use Cwd qw/ getcwd abs_path /; @@ -57,6 +58,16 @@ sub Diag { } } +sub ErrorDiag { + if ($UseColor) { + print STDERR BOLD, RED "$Prog: "; + print STDERR RESET, RED @_; + print STDERR RESET; + } else { + print STDERR "$Prog: @_"; + } +} + sub DiagCrashes { my $Dir = shift; Diag ("The analyzer encountered problems on some source files.\n"); @@ -67,14 +78,14 @@ sub DiagCrashes { sub DieDiag { if ($UseColor) { - print BOLD, RED "$Prog: "; - print RESET, RED @_; - print RESET; + print STDERR BOLD, RED "$Prog: "; + print STDERR RESET, RED @_; + print STDERR RESET; } else { - print "$Prog: ", @_; + print STDERR "$Prog: ", @_; } - exit(0); + exit 1; } ##----------------------------------------------------------------------------## @@ -89,7 +100,7 @@ if (grep /^--help-checkers$/, @ARGV) { my ($sign, $name, @text) = split ' ', $_; print $name, $/ if $sign eq '+'; } - exit 1; + exit 0; } ##----------------------------------------------------------------------------## @@ -110,13 +121,8 @@ sub GetHTMLRunDir { my $Dir = shift @_; my $TmpMode = 0; if (!defined $Dir) { - if (`uname` =~ /Darwin/) { - $Dir = $ENV{'TMPDIR'}; - if (!defined $Dir) { $Dir = "/tmp"; } - } - else { - $Dir = "/tmp"; - } + $Dir = $ENV{'TMPDIR'}; + if (!defined $Dir) { $Dir = "/tmp"; } $TmpMode = 1; } @@ -288,10 +294,11 @@ sub UpdateInFilePath { sub AddStatLine { my $Line = shift; my $Stats = shift; + my $File = shift; print $Line . "\n"; - my $Regex = qr/(.*?)\ :\ (.*?)\ ->\ Total\ CFGBlocks:\ (\d+)\ \|\ Unreachable + my $Regex = qr/(.*?)\ ->\ Total\ CFGBlocks:\ (\d+)\ \|\ Unreachable \ CFGBlocks:\ (\d+)\ \|\ Exhausted\ Block:\ (yes|no)\ \|\ Empty\ WorkList: \ (yes|no)/x; @@ -301,12 +308,12 @@ sub AddStatLine { # Create a hash of the interesting fields my $Row = { - Filename => $1, - Function => $2, - Total => $3, - Unreachable => $4, - Aborted => $5, - Empty => $6 + Filename => $File, + Function => $1, + Total => $2, + Unreachable => $3, + Aborted => $4, + Empty => $5 }; # Add them to the stats array @@ -387,7 +394,7 @@ sub ScanFile { # Don't add internal statistics to the bug reports if ($BugCategory =~ /statistics/i) { - AddStatLine($BugDescription, $Stats); + AddStatLine($BugDescription, $Stats, $BugFile); return; } @@ -475,11 +482,24 @@ sub CalcStats { # Postprocess - Postprocess the results of an analysis scan. ##----------------------------------------------------------------------------## +my @filesFound; +my $baseDir; +sub FileWanted { + my $baseDirRegEx = quotemeta $baseDir; + my $file = $File::Find::name; + if ($file =~ /report-.*\.html$/) { + my $relative_file = $file; + $relative_file =~ s/$baseDirRegEx//g; + push @filesFound, $relative_file; + } +} + sub Postprocess { my $Dir = shift; my $BaseDir = shift; my $AnalyzerStats = shift; + my $KeepEmpty = shift; die "No directory specified." if (!defined $Dir); @@ -487,21 +507,23 @@ sub Postprocess { Diag("No bugs found.\n"); return 0; } - - opendir(DIR, $Dir); - my @files = grep { /^report-.*\.html$/ } readdir(DIR); - closedir(DIR); - if (scalar(@files) == 0 and ! -e "$Dir/failures") { - Diag("Removing directory '$Dir' because it contains no reports.\n"); - system ("rm", "-fR", $Dir); + $baseDir = $Dir . "/"; + find({ wanted => \&FileWanted, follow => 0}, $Dir); + + if (scalar(@filesFound) == 0 and ! -e "$Dir/failures") { + if (! $KeepEmpty) { + Diag("Removing directory '$Dir' because it contains no reports.\n"); + system ("rm", "-fR", $Dir); + } + Diag("No bugs found.\n"); return 0; } # Scan each report file and build an index. my @Index; my @Stats; - foreach my $file (@files) { ScanFile(\@Index, $Dir, $file, \@Stats); } + foreach my $file (@filesFound) { ScanFile(\@Index, $Dir, $file, \@Stats); } # Scan the failures directory and use the information in the .info files # to update the common prefix directory. @@ -603,7 +625,7 @@ print OUT <<ENDTEXT; </table> ENDTEXT - if (scalar(@files)) { + if (scalar(@filesFound)) { # Print out the summary table. my %Totals; @@ -884,6 +906,39 @@ sub RunXcodebuild { if ($IgnoreErrors) { AddIfNotPresent($Args,"-PBXBuildsContinueAfterErrors=YES"); } + + # Detect the version of Xcode. If Xcode 4.6 or higher, use new + # in situ support for analyzer interposition without needed to override + # the compiler. + open(DETECT_XCODE, "xcodebuild -version |") or + die "error: cannot detect version of xcodebuild\n"; + + my $oldBehavior = 1; + + while(<DETECT_XCODE>) { + if (/^Xcode (.+)$/) { + my $ver = $1; + if ($ver =~ /^([0-9]+[.][0-9]+)[^0-9]?/) { + if ($1 >= 4.6) { + $oldBehavior = 0; + last; + } + } + } + } + close(DETECT_XCODE); + + if ($oldBehavior == 0) { + my $OutputDir = $Options->{"OUTPUT_DIR"}; + my $CLANG = $Options->{"CLANG"}; + push @$Args, + "RUN_CLANG_STATIC_ANALYZER=YES", + "CLANG_ANALYZER_OUTPUT=plist-html", + "CLANG_ANALYZER_EXEC=$CLANG", + "CLANG_ANALYZER_OUTPUT_DIR=$OutputDir"; + + return (system(@$Args) >> 8); + } # Default to old behavior where we insert a bogus compiler. SetEnv($Options); @@ -1086,7 +1141,11 @@ ADVANCED OPTIONS: scan-build uses the 'clang' executable relative to itself for static analysis. One can override this behavior with this option by using the 'clang' packaged with Xcode (on OS X) or from the PATH. - + + --keep-empty + + Don't remove the build results directory even if no issues were reported. + CONTROLLING CHECKERS: A default group of checkers are always run unless explicitly disabled. @@ -1253,6 +1312,7 @@ my $HtmlDir; # Parent directory to store HTML files. my $IgnoreErrors = 0; # Ignore build errors. my $ViewResults = 0; # View results when the build terminates. my $ExitStatusFoundBugs = 0; # Exit status reflects whether bugs were found +my $KeepEmpty = 0; # Don't remove output directory even with 0 results. my @AnalysesToRun; my $StoreModel; my $ConstraintsModel; @@ -1260,16 +1320,14 @@ my $InternalStats; my $OutputFormat = "html"; my $AnalyzerStats = 0; my $MaxLoop = 0; +my $RequestDisplayHelp = 0; +my $ForceDisplayHelp = 0; +my $AnalyzerDiscoveryMethod; if (!@ARGV) { - DisplayHelp(); - exit 1; + $ForceDisplayHelp = 1 } - -my $displayHelp = 0; -my $AnalyzerDiscoveryMethod; - while (@ARGV) { # Scan for options we recognize. @@ -1277,7 +1335,7 @@ while (@ARGV) { my $arg = $ARGV[0]; if ($arg eq "-h" or $arg eq "--help") { - $displayHelp = 1; + $RequestDisplayHelp = 1; shift @ARGV; next; } @@ -1446,15 +1504,20 @@ while (@ARGV) { $AnalyzerDiscoveryMethod = $1; next; } + if ($arg eq "--keep-empty") { + shift @ARGV; + $KeepEmpty = 1; + next; + } DieDiag("unrecognized option '$arg'\n") if ($arg =~ /^-/); last; } -if (!@ARGV and $displayHelp == 0) { - Diag("No build command specified.\n\n"); - $displayHelp = 1; +if (!@ARGV and !$RequestDisplayHelp) { + ErrorDiag("No build command specified.\n\n"); + $ForceDisplayHelp = 1; } # Find 'clang' @@ -1464,7 +1527,7 @@ if (!defined $AnalyzerDiscoveryMethod) { $Clang = Cwd::realpath("$RealBin/clang"); } if (!defined $Clang || ! -x $Clang) { - if (!$displayHelp) { + if (!$RequestDisplayHelp && !$ForceDisplayHelp) { DieDiag("error: Cannot find an executable 'clang' relative to scan-build." . " Consider using --use-analyzer to pick a version of 'clang' to use for static analysis.\n"); } @@ -1485,21 +1548,22 @@ else { } else { $Clang = Cwd::realpath($AnalyzerDiscoveryMethod); - if (! -x $Clang) { - DieDiag("Cannot find an executable clang at '$Clang'\n"); + if (!defined $Clang or not -x $Clang) { + DieDiag("Cannot find an executable clang at '$AnalyzerDiscoveryMethod'\n"); } } } -if ($displayHelp) { +if ($ForceDisplayHelp || $RequestDisplayHelp) { DisplayHelp(); - exit 1; + exit $ForceDisplayHelp; } $ClangCXX = $Clang; $ClangCXX =~ s/\-\d+\.\d+$//; $ClangCXX .= "++"; -$ClangVersion = HtmlEscape(`$Clang --version`); +# Make sure to use "" to handle paths with spaces. +$ClangVersion = HtmlEscape(`"$Clang" --version`); # Determine where results go. $CmdArgs = HtmlEscape(join(' ', map(ShellEscape($_), @ARGV))); @@ -1568,9 +1632,9 @@ if (defined $OutputFormat) { Diag "Analysis run complete.\n"; Diag "Analysis results (plist files) deposited in '$HtmlDir'\n"; } - elsif ($OutputFormat =~ /html/) { + if ($OutputFormat =~ /html/) { # Postprocess the HTML directory. - my $NumBugs = Postprocess($HtmlDir, $BaseDir, $AnalyzerStats); + my $NumBugs = Postprocess($HtmlDir, $BaseDir, $AnalyzerStats, $KeepEmpty); if ($ViewResults and -r "$HtmlDir/index.html") { Diag "Analysis run complete.\n"; diff --git a/tools/scan-build/set-xcode-analyzer b/tools/scan-build/set-xcode-analyzer index 93824af..3076b39 100755 --- a/tools/scan-build/set-xcode-analyzer +++ b/tools/scan-build/set-xcode-analyzer @@ -4,9 +4,13 @@ # want to the use the system version of Python on Mac OS X. # This one has the scripting bridge enabled. +import sys +if sys.version_info < (2, 7): + print "set-xcode-analyzer requires Python 2.7 or later" + sys.exit(1) + import os import subprocess -import sys import re import tempfile import shutil @@ -41,6 +45,8 @@ def ModifySpec(path, isBuiltinAnalyzer, pathToChecker): m = re.search('^(\s*ExecPath\s*=\s*")', line) if m: line = "".join([m.group(0), pathToChecker, '";\n']) + # Do not modify further ExecPath's later in the xcspec. + foundAnalyzer = False t.write(line) t.close() print "(+) processing:", path @@ -70,7 +76,7 @@ def main(): for x in NSWorkspace.sharedWorkspace().runningApplications(): if x.localizedName().find("Xcode") >= 0: print "(-) You must quit Xcode first before modifying its configuration files." - return + sys.exit(1) isBuiltinAnalyzer = False if options.path: |