diff options
Diffstat (limited to 'clang/utils/scan-build')
-rwxr-xr-x | clang/utils/scan-build | 1101 |
1 files changed, 0 insertions, 1101 deletions
diff --git a/clang/utils/scan-build b/clang/utils/scan-build deleted file mode 100755 index 160b00c..0000000 --- a/clang/utils/scan-build +++ /dev/null @@ -1,1101 +0,0 @@ -#!/usr/bin/env perl -# -# The LLVM Compiler Infrastructure -# -# This file is distributed under the University of Illinois Open Source -# License. See LICENSE.TXT for details. -# -##===----------------------------------------------------------------------===## -# -# A script designed to wrap a build so that all calls to gcc are intercepted -# and piped to the static analyzer. -# -##===----------------------------------------------------------------------===## - -use strict; -use warnings; -use FindBin qw($RealBin); -use Digest::MD5; -use File::Basename; -use Term::ANSIColor; -use Term::ANSIColor qw(:constants); -use Cwd; -use Sys::Hostname; -use File::Basename; - -my $Verbose = 0; # Verbose output from this script. -my $Prog = "scan-build"; -my $BuildName; -my $BuildDate; -my $CXX; # Leave undefined initially. - -my $TERM = $ENV{'TERM'}; -my $UseColor = (defined $TERM and $TERM eq 'xterm-color' and -t STDOUT - and defined $ENV{'SCAN_BUILD_COLOR'}); - -my $UserName = HtmlEscape(getpwuid($<) || 'unknown'); -my $HostName = HtmlEscape(hostname() || 'unknown'); -my $CurrentDir = HtmlEscape(getcwd()); -my $CurrentDirSuffix = basename($CurrentDir); - -my $CmdArgs; - -my $HtmlTitle; - -my $Date = localtime(); - -##----------------------------------------------------------------------------## -# Diagnostics -##----------------------------------------------------------------------------## - -sub Diag { - if ($UseColor) { - print BOLD, MAGENTA "$Prog: @_"; - print RESET; - } - else { - print "$Prog: @_"; - } -} - -sub DiagCrashes { - my $Dir = shift; - Diag ("The analyzer crashed on some source files.\n"); - Diag ("Preprocessed versions of crashed files were deposited in '$Dir/crashes'.\n"); - Diag ("Please consider submitting a bug report using these files:\n"); - Diag (" http://clang.llvm.org/StaticAnalysisUsage.html#filingbugs\n") -} - -sub DieDiag { - if ($UseColor) { - print BOLD, RED "$Prog: "; - print RESET, RED @_; - print RESET; - } - else { - print "$Prog: ", @_; - } - exit(0); -} - -##----------------------------------------------------------------------------## -# Some initial preprocessing of Clang options. -##----------------------------------------------------------------------------## - -my $ClangSB = "$RealBin/clang"; -my $Clang = $ClangSB; - -if (! -x $ClangSB) { - $Clang = "clang"; -} - -my %AvailableAnalyses; - -# Query clang for analysis options. -open(PIPE, "-|", $Clang, "--help") or - DieDiag("Cannot execute '$Clang'"); - -my $FoundAnalysis = 0; - -while(<PIPE>) { - if ($FoundAnalysis == 0) { - if (/Available Source Code Analyses/) { - $FoundAnalysis = 1; - } - - next; - } - - if (/^\s\s\s\s([^\s]+)\s(.+)$/) { - next if ($1 =~ /-dump/ or $1 =~ /-view/ - or $1 =~ /-checker-simple/ or $1 =~ /-warn-uninit/); - - $AvailableAnalyses{$1} = $2; - next; - } - - last; -} - -close (PIPE); - -my %AnalysesDefaultEnabled = ( - '-warn-dead-stores' => 1, - '-checker-cfref' => 1, - '-warn-objc-methodsigs' => 1, - '-warn-objc-missing-dealloc' => 1, - '-warn-objc-unused-ivars' => 1, -); - -##----------------------------------------------------------------------------## -# GetHTMLRunDir - Construct an HTML directory name for the current sub-run. -##----------------------------------------------------------------------------## - -sub GetHTMLRunDir { - - die "Not enough arguments." if (@_ == 0); - my $Dir = shift @_; - - my $TmpMode = 0; - if (!defined $Dir) { - $Dir = "/tmp"; - $TmpMode = 1; - } - - # Get current date and time. - - my @CurrentTime = localtime(); - - my $year = $CurrentTime[5] + 1900; - my $day = $CurrentTime[3]; - my $month = $CurrentTime[4] + 1; - - my $DateString = sprintf("%d-%02d-%02d", $year, $month, $day); - - # Determine the run number. - - my $RunNumber; - - if (-d $Dir) { - - if (! -r $Dir) { - DieDiag("directory '$Dir' exists but is not readable.\n"); - } - - # Iterate over all files in the specified directory. - - my $max = 0; - - opendir(DIR, $Dir); - my @FILES = grep { -d "$Dir/$_" } readdir(DIR); - closedir(DIR); - - foreach my $f (@FILES) { - - # Strip the prefix '$Prog-' if we are dumping files to /tmp. - if ($TmpMode) { - next if (!($f =~ /^$Prog-(.+)/)); - $f = $1; - } - - - my @x = split/-/, $f; - next if (scalar(@x) != 4); - next if ($x[0] != $year); - next if ($x[1] != $month); - next if ($x[2] != $day); - - if ($x[3] > $max) { - $max = $x[3]; - } - } - - $RunNumber = $max + 1; - } - else { - - if (-x $Dir) { - DieDiag("'$Dir' exists but is not a directory.\n"); - } - - if ($TmpMode) { - DieDiag("The directory '/tmp' does not exist or cannot be accessed."); - } - - # $Dir does not exist. It will be automatically created by the - # clang driver. Set the run number to 1. - - $RunNumber = 1; - } - - die "RunNumber must be defined!" if (!defined $RunNumber); - - # Append the run number. - my $NewDir; - if ($TmpMode) { - $NewDir = "$Dir/$Prog-$DateString-$RunNumber"; - } - else { - $NewDir = "$Dir/$DateString-$RunNumber"; - } - system 'mkdir','-p',$NewDir; - return $NewDir; -} - -sub SetHtmlEnv { - - die "Wrong number of arguments." if (scalar(@_) != 2); - - my $Args = shift; - my $Dir = shift; - - die "No build command." if (scalar(@$Args) == 0); - - my $Cmd = $$Args[0]; - - if ($Cmd =~ /configure/) { - return; - } - - if ($Verbose) { - Diag("Emitting reports for this run to '$Dir'.\n"); - } - - $ENV{'CCC_ANALYZER_HTML'} = $Dir; -} - -##----------------------------------------------------------------------------## -# ComputeDigest - Compute a digest of the specified file. -##----------------------------------------------------------------------------## - -sub ComputeDigest { - my $FName = shift; - DieDiag("Cannot read $FName to compute Digest.\n") if (! -r $FName); - - # Use Digest::MD5. We don't have to be cryptographically secure. We're - # just looking for duplicate files that come from a non-malicious source. - # We use Digest::MD5 because it is a standard Perl module that should - # come bundled on most systems. - open(FILE, $FName) or DieDiag("Cannot open $FName when computing Digest.\n"); - binmode FILE; - my $Result = Digest::MD5->new->addfile(*FILE)->hexdigest; - close(FILE); - - # Return the digest. - return $Result; -} - -##----------------------------------------------------------------------------## -# UpdatePrefix - Compute the common prefix of files. -##----------------------------------------------------------------------------## - -my $Prefix; - -sub UpdatePrefix { - my $x = shift; - my $y = basename($x); - $x =~ s/\Q$y\E$//; - - # Ignore /usr, /Library, /System, /Developer - return if ( $x =~ /^\/usr/ or $x =~ /^\/Library/ - or $x =~ /^\/System/ or $x =~ /^\/Developer/); - - if (!defined $Prefix) { - $Prefix = $x; - return; - } - - chop $Prefix while (!($x =~ /^\Q$Prefix/)); -} - -sub GetPrefix { - return $Prefix; -} - -##----------------------------------------------------------------------------## -# UpdateInFilePath - Update the path in the report file. -##----------------------------------------------------------------------------## - -sub UpdateInFilePath { - my $fname = shift; - my $regex = shift; - my $newtext = shift; - - open (RIN, $fname) or die "cannot open $fname"; - open (ROUT, ">", "$fname.tmp") or die "cannot open $fname.tmp"; - - while (<RIN>) { - s/$regex/$newtext/; - print ROUT $_; - } - - close (ROUT); - close (RIN); - system("mv", "$fname.tmp", $fname); -} - -##----------------------------------------------------------------------------## -# ScanFile - Scan a report file for various identifying attributes. -##----------------------------------------------------------------------------## - -# Sometimes a source file is scanned more than once, and thus produces -# multiple error reports. We use a cache to solve this problem. - -my %AlreadyScanned; - -sub ScanFile { - - my $Index = shift; - my $Dir = shift; - my $FName = shift; - - # Compute a digest for the report file. Determine if we have already - # scanned a file that looks just like it. - - my $digest = ComputeDigest("$Dir/$FName"); - - if (defined $AlreadyScanned{$digest}) { - # Redundant file. Remove it. - system ("rm", "-f", "$Dir/$FName"); - return; - } - - $AlreadyScanned{$digest} = 1; - - # At this point the report file is not world readable. Make it happen. - system ("chmod", "644", "$Dir/$FName"); - - # Scan the report file for tags. - open(IN, "$Dir/$FName") or DieDiag("Cannot open '$Dir/$FName'\n"); - - my $BugDesc = ""; - my $BugFile = ""; - my $BugCategory; - my $BugPathLength = 1; - my $BugLine = 0; - my $found = 0; - - while (<IN>) { - - last if ($found == 5); - - if (/<!-- BUGDESC (.*) -->$/) { - $BugDesc = $1; - ++$found; - } - elsif (/<!-- BUGFILE (.*) -->$/) { - $BugFile = $1; - UpdatePrefix($BugFile); - ++$found; - } - elsif (/<!-- BUGPATHLENGTH (.*) -->$/) { - $BugPathLength = $1; - ++$found; - } - elsif (/<!-- BUGLINE (.*) -->$/) { - $BugLine = $1; - ++$found; - } - elsif (/<!-- BUGCATEGORY (.*) -->$/) { - $BugCategory = $1; - ++$found; - } - } - - close(IN); - - if (!defined $BugCategory) { - $BugCategory = "Other"; - } - - push @$Index,[ $FName, $BugCategory, $BugDesc, $BugFile, $BugLine, - $BugPathLength ]; -} - -##----------------------------------------------------------------------------## -# CopyFiles - Copy resource files to target directory. -##----------------------------------------------------------------------------## - -sub CopyFiles { - - my $Dir = shift; - - DieDiag("Cannot find 'sorttable.js'.\n") - if (! -r "$RealBin/sorttable.js"); - - system ("cp", "$RealBin/sorttable.js", "$Dir"); - - DieDiag("Could not copy 'sorttable.js' to '$Dir'.\n") - if (! -r "$Dir/sorttable.js"); - - DieDiag("Cannot find 'scanview.css'.\n") - if (! -r "$RealBin/scanview.css"); - - system ("cp", "$RealBin/scanview.css", "$Dir"); - - DieDiag("Could not copy 'scanview.css' to '$Dir'.\n") - if (! -r "$Dir/scanview.css"); -} - -##----------------------------------------------------------------------------## -# Postprocess - Postprocess the results of an analysis scan. -##----------------------------------------------------------------------------## - -sub Postprocess { - - my $Dir = shift; - my $BaseDir = shift; - - die "No directory specified." if (!defined $Dir); - - if (! -d $Dir) { - Diag("No bugs found.\n"); - return 0; - } - - opendir(DIR, $Dir); - my $Crashes = 0; - my @files = grep { if ($_ eq "crashes") { $Crashes++; } - /^report-.*\.html$/; } readdir(DIR); - closedir(DIR); - - if (scalar(@files) == 0 and $Crashes == 0) { - Diag("Removing directory '$Dir' because it contains no reports.\n"); - system ("rm", "-fR", $Dir); - return 0; - } - - # Scan each report file and build an index. - my @Index; - foreach my $file (@files) { ScanFile(\@Index, $Dir, $file); } - - # Scan the crashes directory and use the information in the .info files - # to update the common prefix directory. - if (-d "$Dir/crashes") { - opendir(DIR, "$Dir/crashes"); - my @files = grep { /[.]info.txt$/; } readdir(DIR); - closedir(DIR); - foreach my $file (@files) { - open IN, "$Dir/crashes/$file" or DieDiag("cannot open $file\n"); - my $Path = <IN>; - if (defined $Path) { UpdatePrefix($Path); } - close IN; - } - } - - # Generate an index.html file. - my $FName = "$Dir/index.html"; - open(OUT, ">", $FName) or DieDiag("Cannot create file '$FName'\n"); - - # Print out the header. - -print OUT <<ENDTEXT; -<html> -<head> -<title>${HtmlTitle}</title> -<link type="text/css" rel="stylesheet" href="scanview.css"/> -<script src="sorttable.js"></script> -<script language='javascript' type="text/javascript"> -function SetDisplay(RowClass, DisplayVal) -{ - var Rows = document.getElementsByTagName("tr"); - for ( var i = 0 ; i < Rows.length; ++i ) { - if (Rows[i].className == RowClass) { - Rows[i].style.display = DisplayVal; - } - } -} - -function ToggleDisplay(CheckButton, ClassName) { - if (CheckButton.checked) { - SetDisplay(ClassName, ""); - } - else { - SetDisplay(ClassName, "none"); - } -} -</script> -<!-- SUMMARYENDHEAD --> -</head> -<body> -<h1>${HtmlTitle}</h1> - -<table> -<tr><th>User:</th><td>${UserName}\@${HostName}</td></tr> -<tr><th>Working Directory:</th><td>${CurrentDir}</td></tr> -<tr><th>Command Line:</th><td>${CmdArgs}</td></tr> -<tr><th>Date:</th><td>${Date}</td></tr> -ENDTEXT - -print OUT "<tr><th>Version:</th><td>${BuildName} (${BuildDate})</td></tr>\n" - if (defined($BuildName) && defined($BuildDate)); - -print OUT <<ENDTEXT; -</table> -ENDTEXT - - if (scalar(@files)) { - # Print out the summary table. - my %Totals; - - for my $row ( @Index ) { - my $bug_type = ($row->[2]); - my $bug_category = ($row->[1]); - my $key = "$bug_category:$bug_type"; - - if (!defined $Totals{$key}) { $Totals{$key} = [1,$bug_category,$bug_type]; } - else { $Totals{$key}->[0]++; } - } - - print OUT "<h2>Bug Summary</h2>"; - - if (defined $BuildName) { - print OUT "\n<p>Results in this analysis run are based on analyzer build <b>$BuildName</b>.</p>\n" - } - -print OUT <<ENDTEXT; -<table> -<thead><tr><td>Bug Type</td><td>Quantity</td><td class="sorttable_nosort">Display?</td></tr></thead> -ENDTEXT - - my $last_category; - - for my $key ( - sort { - my $x = $Totals{$a}; - my $y = $Totals{$b}; - my $res = $x->[1] cmp $y->[1]; - $res = $x->[2] cmp $y->[2] if ($res == 0); - $res - } keys %Totals ) - { - my $val = $Totals{$key}; - my $category = $val->[1]; - if (!defined $last_category or $last_category ne $category) { - $last_category = $category; - print OUT "<tr><th>$category</th><th colspan=2></th></tr>\n"; - } - my $x = lc $key; - $x =~ s/[ ,'":\/()]+/_/g; - print OUT "<tr><td class=\"SUMM_DESC\">"; - print OUT $val->[2]; - print OUT "</td><td>"; - print OUT $val->[0]; - print OUT "</td><td><center><input type=\"checkbox\" onClick=\"ToggleDisplay(this,'bt_$x');\" checked/></center></td></tr>\n"; - } - - # Print out the table of errors. - -print OUT <<ENDTEXT; -</table> -<h2>Reports</h2> - -<table class="sortable" style="table-layout:automatic"> -<thead><tr> - <td>Bug Group</td> - <td class="sorttable_sorted">Bug Type<span id="sorttable_sortfwdind"> ▾</span></td> - <td>File</td> - <td class="Q">Line</td> - <td class="Q">Path Length</td> - <td class="sorttable_nosort"></td> - <!-- REPORTBUGCOL --> -</tr></thead> -<tbody> -ENDTEXT - - my $prefix = GetPrefix(); - my $regex; - my $InFileRegex; - my $InFilePrefix = "File:</td><td>"; - - if (defined $prefix) { - $regex = qr/^\Q$prefix\E/is; - $InFileRegex = qr/\Q$InFilePrefix$prefix\E/is; - } - - for my $row ( sort { $a->[2] cmp $b->[2] } @Index ) { - my $x = "$row->[1]:$row->[2]"; - $x = lc $x; - $x =~ s/[ ,'":\/()]+/_/g; - - my $ReportFile = $row->[0]; - - print OUT "<tr class=\"bt_$x\">"; - print OUT "<td class=\"DESC\">"; - print OUT $row->[1]; - print OUT "</td>"; - print OUT "<td class=\"DESC\">"; - print OUT $row->[2]; - print OUT "</td>"; - - # Update the file prefix. - my $fname = $row->[3]; - - if (defined $regex) { - $fname =~ s/$regex//; - UpdateInFilePath("$Dir/$ReportFile", $InFileRegex, $InFilePrefix) - } - - print OUT "<td>"; - my @fname = split /\//,$fname; - if ($#fname > 0) { - while ($#fname >= 0) { - my $x = shift @fname; - print OUT $x; - if ($#fname >= 0) { - print OUT "<span class=\"W\"> </span>/"; - } - } - } - else { - print OUT $fname; - } - print OUT "</td>"; - - # Print out the quantities. - for my $j ( 4 .. 5 ) { - print OUT "<td class=\"Q\">$row->[$j]</td>"; - } - - # Print the rest of the columns. - for (my $j = 6; $j <= $#{$row}; ++$j) { - print OUT "<td>$row->[$j]</td>" - } - - # Emit the "View" link. - print OUT "<td><a href=\"$ReportFile#EndPath\">View Report</a></td>"; - - # Emit REPORTBUG markers. - print OUT "\n<!-- REPORTBUG id=\"$ReportFile\" -->\n"; - - # End the row. - print OUT "</tr>\n"; - } - - print OUT "</tbody>\n</table>\n\n"; - } - - if ($Crashes) { - # Read the crash directory for files. - opendir(DIR, "$Dir/crashes"); - my @files = grep { /[.]info.txt$/ } readdir(DIR); - closedir(DIR); - - if (scalar(@files)) { - print OUT <<ENDTEXT; -<h2>Analyzer Failures</h2> - -<p>The analyzer had problems processing the following files:</p> - -<table> -<thead><tr><td>Problem</td><td>Source File</td><td>Preprocessed File</td><td>STDERR Output</td></tr></thead> -ENDTEXT - - foreach my $file (sort @files) { - $file =~ /(.+).info.txt$/; - # Get the preprocessed file. - my $ppfile = $1; - # Open the info file and get the name of the source file. - open (INFO, "$Dir/crashes/$file") or - die "Cannot open $Dir/crashes/$file\n"; - my $srcfile = <INFO>; - chomp $srcfile; - my $problem = <INFO>; - chomp $problem; - close (INFO); - # Print the information in the table. - my $prefix = GetPrefix(); - if (defined $prefix) { $srcfile =~ s/^\Q$prefix//; } - print OUT "<tr><td>$problem</td><td>$srcfile</td><td><a href=\"crashes/$ppfile\">$ppfile</a></td><td><a href=\"crashes/$ppfile.stderr.txt\">$ppfile.stderr.txt</a></td></tr>\n"; - my $ppfile_clang = $ppfile; - $ppfile_clang =~ s/[.](.+)$/.clang.$1/; - print OUT " <!-- REPORTPROBLEM src=\"$srcfile\" file=\"crashes/$ppfile\" clangfile=\"crashes/$ppfile_clang\" stderr=\"crashes/$ppfile.stderr.txt\" info=\"crashes/$ppfile.info.txt\" -->\n"; - } - - print OUT <<ENDTEXT; -</table> -<p>Please consider submitting preprocessed files as <a href="http://clang.llvm.org/StaticAnalysisUsage.html#filingbugs">bug reports</a>. <!-- REPORTCRASHES --> </p> -ENDTEXT - } - } - - print OUT "</body></html>\n"; - close(OUT); - CopyFiles($Dir); - - # Make sure $Dir and $BaseDir are world readable/executable. - system("chmod", "755", $Dir); - if (defined $BaseDir) { system("chmod", "755", $BaseDir); } - - my $Num = scalar(@Index); - Diag("$Num bugs found.\n"); - if ($Num > 0 && -r "$Dir/index.html") { - Diag("Run 'scan-view $Dir' to examine bug reports.\n"); - } - - DiagCrashes($Dir) if ($Crashes); - - return $Num; -} - -##----------------------------------------------------------------------------## -# RunBuildCommand - Run the build command. -##----------------------------------------------------------------------------## - -sub AddIfNotPresent { - my $Args = shift; - my $Arg = shift; - my $found = 0; - - foreach my $k (@$Args) { - if ($k eq $Arg) { - $found = 1; - last; - } - } - - if ($found == 0) { - push @$Args, $Arg; - } -} - -sub RunBuildCommand { - - my $Args = shift; - my $IgnoreErrors = shift; - my $Cmd = $Args->[0]; - my $CCAnalyzer = shift; - - # Get only the part of the command after the last '/'. - if ($Cmd =~ /\/([^\/]+)$/) { - $Cmd = $1; - } - - if ($Cmd eq "gcc" or $Cmd eq "cc" or $Cmd eq "llvm-gcc" - or $Cmd eq "ccc-analyzer") { - shift @$Args; - unshift @$Args, $CCAnalyzer; - } - elsif ($IgnoreErrors) { - if ($Cmd eq "make" or $Cmd eq "gmake") { - AddIfNotPresent($Args,"-k"); - AddIfNotPresent($Args,"-i"); - } - elsif ($Cmd eq "xcodebuild") { - AddIfNotPresent($Args,"-PBXBuildsContinueAfterErrors=YES"); - } - } - - if ($Cmd eq "xcodebuild") { - # Disable distributed builds for xcodebuild. - AddIfNotPresent($Args,"-nodistribute"); - - # Disable PCH files until clang supports them. - AddIfNotPresent($Args,"GCC_PRECOMPILE_PREFIX_HEADER=NO"); - - # When 'CC' is set, xcodebuild uses it to do all linking, even if we are - # linking C++ object files. Set 'LDPLUSPLUS' so that xcodebuild uses 'g++' - # when linking such files. - die if (!defined $CXX); - my $LDPLUSPLUS = `which $CXX`; - $LDPLUSPLUS =~ s/\015?\012//; # strip newlines - $ENV{'LDPLUSPLUS'} = $LDPLUSPLUS; - } - - return (system(@$Args) >> 8); -} - -##----------------------------------------------------------------------------## -# DisplayHelp - Utility function to display all help options. -##----------------------------------------------------------------------------## - -sub DisplayHelp { - -print <<ENDTEXT; -USAGE: $Prog [options] <build command> [build options] - -ENDTEXT - - if (defined $BuildName) { - print "ANALYZER BUILD: $BuildName ($BuildDate)\n\n"; - } - -print <<ENDTEXT; -OPTIONS: - - -o - Target directory for HTML report files. Subdirectories - will be created as needed to represent separate "runs" of - the analyzer. If this option is not specified, a directory - is created in /tmp to store the reports. - - -h - Display this message. - --help - - -k - Add a "keep on going" option to the specified build command. - --keep-going This option currently supports make and xcodebuild. - This is a convenience option; one can specify this - behavior directly using build options. - - --html-title [title] - Specify the title used on generated HTML pages. - --html-title=[title] If not specified, a default title will be used. - - --status-bugs - By default, the exit status of $Prog is the same as the - executed build command. Specifying this option causes the - exit status of $Prog to be 1 if it found potential bugs - and 0 otherwise. - - --use-cc [compiler path] - By default, $Prog uses 'gcc' to compile and link - --use-cc=[compiler path] your C and Objective-C code. Use this option - to specify an alternate compiler. - - --use-c++ [compiler path] - By default, $Prog uses 'g++' to compile and link - --use-c++=[compiler path] your C++ and Objective-C++ code. Use this option - to specify an alternate compiler. - - -v - Verbose output from $Prog and the analyzer. - A second and third '-v' increases verbosity. - - -V - View analysis results in a web browser when the build - --view completes. - - -AVAILABLE ANALYSES (multiple analyses may be specified): - -ENDTEXT - - foreach my $Analysis (sort keys %AvailableAnalyses) { - if (defined $AnalysesDefaultEnabled{$Analysis}) { - print " (+)"; - } - else { - print " "; - } - - print " $Analysis $AvailableAnalyses{$Analysis}\n"; - } - -print <<ENDTEXT - - NOTE: "(+)" indicates that an analysis is enabled by default unless one - or more analysis options are specified - -BUILD OPTIONS - - You can specify any build option acceptable to the build command. - -EXAMPLE - - $Prog -o /tmp/myhtmldir make -j4 - - The above example causes analysis reports to be deposited into - a subdirectory of "/tmp/myhtmldir" and to run "make" with the "-j4" option. - A different subdirectory is created each time $Prog analyzes a project. - The analyzer should support most parallel builds, but not distributed builds. - -ENDTEXT -} - -##----------------------------------------------------------------------------## -# HtmlEscape - HTML entity encode characters that are special in HTML -##----------------------------------------------------------------------------## - -sub HtmlEscape { - # copy argument to new variable so we don't clobber the original - my $arg = shift || ''; - my $tmp = $arg; - - $tmp =~ s/([\<\>\'\"])/sprintf("&#%02x;", chr($1))/ge; - - return $tmp; -} - -##----------------------------------------------------------------------------## -# ShellEscape - backslash escape characters that are special to the shell -##----------------------------------------------------------------------------## - -sub ShellEscape { - # copy argument to new variable so we don't clobber the original - my $arg = shift || ''; - my $tmp = $arg; - - $tmp =~ s/([\!\;\\\'\"\`\<\>\|\s\(\)\[\]\?\#\$\^\&\*\=])/\\$1/g; - - return $tmp; -} - -##----------------------------------------------------------------------------## -# Process command-line arguments. -##----------------------------------------------------------------------------## - -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 @AnalysesToRun; - -if (!@ARGV) { - DisplayHelp(); - exit 1; -} - -while (@ARGV) { - - # Scan for options we recognize. - - my $arg = $ARGV[0]; - - if ($arg eq "-h" or $arg eq "--help") { - DisplayHelp(); - exit 0; - } - - if (defined $AvailableAnalyses{$arg}) { - shift @ARGV; - push @AnalysesToRun, $arg; - next; - } - - if ($arg eq "-o") { - shift @ARGV; - - if (!@ARGV) { - DieDiag("'-o' option requires a target directory name.\n"); - } - - $HtmlDir = shift @ARGV; - next; - } - - if ($arg =~ /^--html-title(=(.+))?$/) { - shift @ARGV; - - if ($2 eq '') { - if (!@ARGV) { - DieDiag("'--html-title' option requires a string.\n"); - } - - $HtmlTitle = shift @ARGV; - } else { - $HtmlTitle = $2; - } - - next; - } - - if ($arg eq "-k" or $arg eq "--keep-going") { - shift @ARGV; - $IgnoreErrors = 1; - next; - } - - if ($arg =~ /^--use-cc(=(.+))?$/) { - shift @ARGV; - my $cc; - - if ($2 eq "") { - if (!@ARGV) { - DieDiag("'--use-cc' option requires a compiler executable name.\n"); - } - $cc = shift @ARGV; - } - else { - $cc = $2; - } - - $ENV{"CCC_CC"} = $cc; - next; - } - - if ($arg =~ /^--use-c\+\+(=(.+))?$/) { - shift @ARGV; - - if ($2 eq "") { - if (!@ARGV) { - DieDiag("'--use-c++' option requires a compiler executable name.\n"); - } - $CXX = shift @ARGV; - } - else { - $CXX = $2; - } - next; - } - - if ($arg eq "-v") { - shift @ARGV; - $Verbose++; - next; - } - - if ($arg eq "-V" or $arg eq "--view") { - shift @ARGV; - $ViewResults = 1; - next; - } - - if ($arg eq "--status-bugs") { - shift @ARGV; - $ExitStatusFoundBugs = 1; - next; - } - - DieDiag("unrecognized option '$arg'\n") if ($arg =~ /^-/); - - last; -} - -if (!@ARGV) { - Diag("No build command specified.\n\n"); - DisplayHelp(); - exit 1; -} - -$CmdArgs = HtmlEscape(join(' ', map(ShellEscape($_), @ARGV))); -$HtmlTitle = "${CurrentDirSuffix} - scan-build results" - unless (defined($HtmlTitle)); - -# Determine the output directory for the HTML reports. -my $BaseDir = $HtmlDir; -$HtmlDir = GetHTMLRunDir($HtmlDir); - -# Set the appropriate environment variables. -SetHtmlEnv(\@ARGV, $HtmlDir); - -my $Cmd = "$RealBin/ccc-analyzer"; - -DieDiag("Executable 'ccc-analyzer' does not exist at '$Cmd'\n") - if (! -x $Cmd); - -if (! -x $ClangSB) { - Diag("'clang' executable not found in '$RealBin'.\n"); - Diag("Using 'clang' from path.\n"); -} - -if (defined $CXX) { - $ENV{'CXX'} = $CXX; -} -else { - $CXX = 'g++'; # This variable is used by other parts of scan-build - # that need to know a default C++ compiler to fall back to. -} - -$ENV{'CC'} = $Cmd; -$ENV{'CLANG'} = $Clang; - -if ($Verbose >= 2) { - $ENV{'CCC_ANALYZER_VERBOSE'} = 1; -} - -if ($Verbose >= 3) { - $ENV{'CCC_ANALYZER_LOG'} = 1; -} - -if (scalar(@AnalysesToRun) == 0) { - foreach my $key (keys %AnalysesDefaultEnabled) { - push @AnalysesToRun,$key; - } -} - -$ENV{'CCC_ANALYZER_ANALYSIS'} = join ' ',@AnalysesToRun; - -# Run the build. -my $ExitStatus = RunBuildCommand(\@ARGV, $IgnoreErrors, $Cmd); - -# Postprocess the HTML directory. -my $NumBugs = Postprocess($HtmlDir, $BaseDir); - -if ($ViewResults and -r "$HtmlDir/index.html") { - Diag "Analysis run complete.\n"; - Diag "Viewing analysis results in '$HtmlDir' using scan-view.\n"; - my $ScanView = "$RealBin/scan-view"; - if (! -x $ScanView) { $ScanView = "scan-view"; } - exec $ScanView, "$HtmlDir"; -} - -if ($ExitStatusFoundBugs) { - exit 1 if ($NumBugs > 0); - exit 0; -} - -exit $ExitStatus; - |