xref: /aosp_15_r20/external/clang/tools/scan-build/bin/scan-build (revision 67e74705e28f6214e480b399dd47ea732279e315)
1*67e74705SXin Li#!/usr/bin/env perl
2*67e74705SXin Li#
3*67e74705SXin Li#                     The LLVM Compiler Infrastructure
4*67e74705SXin Li#
5*67e74705SXin Li# This file is distributed under the University of Illinois Open Source
6*67e74705SXin Li# License. See LICENSE.TXT for details.
7*67e74705SXin Li#
8*67e74705SXin Li##===----------------------------------------------------------------------===##
9*67e74705SXin Li#
10*67e74705SXin Li# A script designed to wrap a build so that all calls to gcc are intercepted
11*67e74705SXin Li# and piped to the static analyzer.
12*67e74705SXin Li#
13*67e74705SXin Li##===----------------------------------------------------------------------===##
14*67e74705SXin Li
15*67e74705SXin Liuse strict;
16*67e74705SXin Liuse warnings;
17*67e74705SXin Liuse FindBin qw($RealBin);
18*67e74705SXin Liuse Digest::MD5;
19*67e74705SXin Liuse File::Basename;
20*67e74705SXin Liuse File::Find;
21*67e74705SXin Liuse File::Copy qw(copy);
22*67e74705SXin Liuse File::Path qw( rmtree mkpath );
23*67e74705SXin Liuse Term::ANSIColor;
24*67e74705SXin Liuse Term::ANSIColor qw(:constants);
25*67e74705SXin Liuse Cwd qw/ getcwd abs_path /;
26*67e74705SXin Liuse Sys::Hostname;
27*67e74705SXin Liuse Hash::Util qw(lock_keys);
28*67e74705SXin Li
29*67e74705SXin Limy $Prog = "scan-build";
30*67e74705SXin Limy $BuildName;
31*67e74705SXin Limy $BuildDate;
32*67e74705SXin Li
33*67e74705SXin Limy $TERM = $ENV{'TERM'};
34*67e74705SXin Limy $UseColor = (defined $TERM and $TERM =~ 'xterm-.*color' and -t STDOUT
35*67e74705SXin Li                and defined $ENV{'SCAN_BUILD_COLOR'});
36*67e74705SXin Li
37*67e74705SXin Li# Portability: getpwuid is not implemented for Win32 (see Perl language
38*67e74705SXin Li# reference, perlport), use getlogin instead.
39*67e74705SXin Limy $UserName = HtmlEscape(getlogin() || getpwuid($<) || 'unknown');
40*67e74705SXin Limy $HostName = HtmlEscape(hostname() || 'unknown');
41*67e74705SXin Limy $CurrentDir = HtmlEscape(getcwd());
42*67e74705SXin Li
43*67e74705SXin Limy $CmdArgs;
44*67e74705SXin Li
45*67e74705SXin Limy $Date = localtime();
46*67e74705SXin Li
47*67e74705SXin Li# Command-line/config arguments.
48*67e74705SXin Limy %Options = (
49*67e74705SXin Li  Verbose => 0,              # Verbose output from this script.
50*67e74705SXin Li  AnalyzeHeaders => 0,
51*67e74705SXin Li  OutputDir => undef,        # Parent directory to store HTML files.
52*67e74705SXin Li  HtmlTitle => basename($CurrentDir)." - scan-build results",
53*67e74705SXin Li  IgnoreErrors => 0,         # Ignore build errors.
54*67e74705SXin Li  ViewResults => 0,          # View results when the build terminates.
55*67e74705SXin Li  ExitStatusFoundBugs => 0,  # Exit status reflects whether bugs were found
56*67e74705SXin Li  KeepEmpty => 0,            # Don't remove output directory even with 0 results.
57*67e74705SXin Li  EnableCheckers => {},
58*67e74705SXin Li  DisableCheckers => {},
59*67e74705SXin Li  UseCC => undef,            # C compiler to use for compilation.
60*67e74705SXin Li  UseCXX => undef,           # C++ compiler to use for compilation.
61*67e74705SXin Li  AnalyzerTarget => undef,
62*67e74705SXin Li  StoreModel => undef,
63*67e74705SXin Li  ConstraintsModel => undef,
64*67e74705SXin Li  InternalStats => undef,
65*67e74705SXin Li  OutputFormat => "html",
66*67e74705SXin Li  ConfigOptions => [],       # Options to pass through to the analyzer's -analyzer-config flag.
67*67e74705SXin Li  ReportFailures => undef,
68*67e74705SXin Li  AnalyzerStats => 0,
69*67e74705SXin Li  MaxLoop => 0,
70*67e74705SXin Li  PluginsToLoad => [],
71*67e74705SXin Li  AnalyzerDiscoveryMethod => undef,
72*67e74705SXin Li  OverrideCompiler => 0,      # The flag corresponding to the --override-compiler command line option.
73*67e74705SXin Li  ForceAnalyzeDebugCode => 0
74*67e74705SXin Li);
75*67e74705SXin Lilock_keys(%Options);
76*67e74705SXin Li
77*67e74705SXin Li##----------------------------------------------------------------------------##
78*67e74705SXin Li# Diagnostics
79*67e74705SXin Li##----------------------------------------------------------------------------##
80*67e74705SXin Li
81*67e74705SXin Lisub Diag {
82*67e74705SXin Li  if ($UseColor) {
83*67e74705SXin Li    print BOLD, MAGENTA "$Prog: @_";
84*67e74705SXin Li    print RESET;
85*67e74705SXin Li  }
86*67e74705SXin Li  else {
87*67e74705SXin Li    print "$Prog: @_";
88*67e74705SXin Li  }
89*67e74705SXin Li}
90*67e74705SXin Li
91*67e74705SXin Lisub ErrorDiag {
92*67e74705SXin Li  if ($UseColor) {
93*67e74705SXin Li    print STDERR BOLD, RED "$Prog: ";
94*67e74705SXin Li    print STDERR RESET, RED @_;
95*67e74705SXin Li    print STDERR RESET;
96*67e74705SXin Li  } else {
97*67e74705SXin Li    print STDERR "$Prog: @_";
98*67e74705SXin Li  }
99*67e74705SXin Li}
100*67e74705SXin Li
101*67e74705SXin Lisub DiagCrashes {
102*67e74705SXin Li  my $Dir = shift;
103*67e74705SXin Li  Diag ("The analyzer encountered problems on some source files.\n");
104*67e74705SXin Li  Diag ("Preprocessed versions of these sources were deposited in '$Dir/failures'.\n");
105*67e74705SXin Li  Diag ("Please consider submitting a bug report using these files:\n");
106*67e74705SXin Li  Diag ("  http://clang-analyzer.llvm.org/filing_bugs.html\n")
107*67e74705SXin Li}
108*67e74705SXin Li
109*67e74705SXin Lisub DieDiag {
110*67e74705SXin Li  if ($UseColor) {
111*67e74705SXin Li    print STDERR BOLD, RED "$Prog: ";
112*67e74705SXin Li    print STDERR RESET, RED @_;
113*67e74705SXin Li    print STDERR RESET;
114*67e74705SXin Li  }
115*67e74705SXin Li  else {
116*67e74705SXin Li    print STDERR "$Prog: ", @_;
117*67e74705SXin Li  }
118*67e74705SXin Li  exit 1;
119*67e74705SXin Li}
120*67e74705SXin Li
121*67e74705SXin Li##----------------------------------------------------------------------------##
122*67e74705SXin Li# Print default checker names
123*67e74705SXin Li##----------------------------------------------------------------------------##
124*67e74705SXin Li
125*67e74705SXin Liif (grep /^--help-checkers$/, @ARGV) {
126*67e74705SXin Li    my @options = qx($0 -h);
127*67e74705SXin Li    foreach (@options) {
128*67e74705SXin Li    next unless /^ \+/;
129*67e74705SXin Li    s/^\s*//;
130*67e74705SXin Li    my ($sign, $name, @text) = split ' ', $_;
131*67e74705SXin Li    print $name, $/ if $sign eq '+';
132*67e74705SXin Li    }
133*67e74705SXin Li    exit 0;
134*67e74705SXin Li}
135*67e74705SXin Li
136*67e74705SXin Li##----------------------------------------------------------------------------##
137*67e74705SXin Li# Declaration of Clang options.  Populated later.
138*67e74705SXin Li##----------------------------------------------------------------------------##
139*67e74705SXin Li
140*67e74705SXin Limy $Clang;
141*67e74705SXin Limy $ClangSB;
142*67e74705SXin Limy $ClangCXX;
143*67e74705SXin Limy $ClangVersion;
144*67e74705SXin Li
145*67e74705SXin Li##----------------------------------------------------------------------------##
146*67e74705SXin Li# GetHTMLRunDir - Construct an HTML directory name for the current sub-run.
147*67e74705SXin Li##----------------------------------------------------------------------------##
148*67e74705SXin Li
149*67e74705SXin Lisub GetHTMLRunDir {
150*67e74705SXin Li  die "Not enough arguments." if (@_ == 0);
151*67e74705SXin Li  my $Dir = shift @_;
152*67e74705SXin Li  my $TmpMode = 0;
153*67e74705SXin Li  if (!defined $Dir) {
154*67e74705SXin Li    $Dir = $ENV{'TMPDIR'} || $ENV{'TEMP'} || $ENV{'TMP'} || "/tmp";
155*67e74705SXin Li    $TmpMode = 1;
156*67e74705SXin Li  }
157*67e74705SXin Li
158*67e74705SXin Li  # Chop off any trailing '/' characters.
159*67e74705SXin Li  while ($Dir =~ /\/$/) { chop $Dir; }
160*67e74705SXin Li
161*67e74705SXin Li  # Get current date and time.
162*67e74705SXin Li  my @CurrentTime = localtime();
163*67e74705SXin Li  my $year  = $CurrentTime[5] + 1900;
164*67e74705SXin Li  my $day   = $CurrentTime[3];
165*67e74705SXin Li  my $month = $CurrentTime[4] + 1;
166*67e74705SXin Li  my $hour =  $CurrentTime[2];
167*67e74705SXin Li  my $min =   $CurrentTime[1];
168*67e74705SXin Li  my $sec =   $CurrentTime[0];
169*67e74705SXin Li
170*67e74705SXin Li  my $TimeString = sprintf("%02d%02d%02d", $hour, $min, $sec);
171*67e74705SXin Li  my $DateString = sprintf("%d-%02d-%02d-%s-$$",
172*67e74705SXin Li                           $year, $month, $day, $TimeString);
173*67e74705SXin Li
174*67e74705SXin Li  # Determine the run number.
175*67e74705SXin Li  my $RunNumber;
176*67e74705SXin Li
177*67e74705SXin Li  if (-d $Dir) {
178*67e74705SXin Li    if (! -r $Dir) {
179*67e74705SXin Li      DieDiag("directory '$Dir' exists but is not readable.\n");
180*67e74705SXin Li    }
181*67e74705SXin Li    # Iterate over all files in the specified directory.
182*67e74705SXin Li    my $max = 0;
183*67e74705SXin Li    opendir(DIR, $Dir);
184*67e74705SXin Li    my @FILES = grep { -d "$Dir/$_" } readdir(DIR);
185*67e74705SXin Li    closedir(DIR);
186*67e74705SXin Li
187*67e74705SXin Li    foreach my $f (@FILES) {
188*67e74705SXin Li      # Strip the prefix '$Prog-' if we are dumping files to /tmp.
189*67e74705SXin Li      if ($TmpMode) {
190*67e74705SXin Li        next if (!($f =~ /^$Prog-(.+)/));
191*67e74705SXin Li        $f = $1;
192*67e74705SXin Li      }
193*67e74705SXin Li
194*67e74705SXin Li      my @x = split/-/, $f;
195*67e74705SXin Li      next if (scalar(@x) != 4);
196*67e74705SXin Li      next if ($x[0] != $year);
197*67e74705SXin Li      next if ($x[1] != $month);
198*67e74705SXin Li      next if ($x[2] != $day);
199*67e74705SXin Li      next if ($x[3] != $TimeString);
200*67e74705SXin Li      next if ($x[4] != $$);
201*67e74705SXin Li
202*67e74705SXin Li      if ($x[5] > $max) {
203*67e74705SXin Li        $max = $x[5];
204*67e74705SXin Li      }
205*67e74705SXin Li    }
206*67e74705SXin Li
207*67e74705SXin Li    $RunNumber = $max + 1;
208*67e74705SXin Li  }
209*67e74705SXin Li  else {
210*67e74705SXin Li
211*67e74705SXin Li    if (-x $Dir) {
212*67e74705SXin Li      DieDiag("'$Dir' exists but is not a directory.\n");
213*67e74705SXin Li    }
214*67e74705SXin Li
215*67e74705SXin Li    if ($TmpMode) {
216*67e74705SXin Li      DieDiag("The directory '/tmp' does not exist or cannot be accessed.\n");
217*67e74705SXin Li    }
218*67e74705SXin Li
219*67e74705SXin Li    # $Dir does not exist.  It will be automatically created by the
220*67e74705SXin Li    # clang driver.  Set the run number to 1.
221*67e74705SXin Li
222*67e74705SXin Li    $RunNumber = 1;
223*67e74705SXin Li  }
224*67e74705SXin Li
225*67e74705SXin Li  die "RunNumber must be defined!" if (!defined $RunNumber);
226*67e74705SXin Li
227*67e74705SXin Li  # Append the run number.
228*67e74705SXin Li  my $NewDir;
229*67e74705SXin Li  if ($TmpMode) {
230*67e74705SXin Li    $NewDir = "$Dir/$Prog-$DateString-$RunNumber";
231*67e74705SXin Li  }
232*67e74705SXin Li  else {
233*67e74705SXin Li    $NewDir = "$Dir/$DateString-$RunNumber";
234*67e74705SXin Li  }
235*67e74705SXin Li
236*67e74705SXin Li  # Make sure that the directory does not exist in order to avoid hijack.
237*67e74705SXin Li  if (-e $NewDir) {
238*67e74705SXin Li      DieDiag("The directory '$NewDir' already exists.\n");
239*67e74705SXin Li  }
240*67e74705SXin Li
241*67e74705SXin Li  mkpath($NewDir);
242*67e74705SXin Li  return $NewDir;
243*67e74705SXin Li}
244*67e74705SXin Li
245*67e74705SXin Lisub SetHtmlEnv {
246*67e74705SXin Li
247*67e74705SXin Li  die "Wrong number of arguments." if (scalar(@_) != 2);
248*67e74705SXin Li
249*67e74705SXin Li  my $Args = shift;
250*67e74705SXin Li  my $Dir = shift;
251*67e74705SXin Li
252*67e74705SXin Li  die "No build command." if (scalar(@$Args) == 0);
253*67e74705SXin Li
254*67e74705SXin Li  my $Cmd = $$Args[0];
255*67e74705SXin Li
256*67e74705SXin Li  if ($Cmd =~ /configure/ || $Cmd =~ /autogen/) {
257*67e74705SXin Li    return;
258*67e74705SXin Li  }
259*67e74705SXin Li
260*67e74705SXin Li  if ($Options{Verbose}) {
261*67e74705SXin Li    Diag("Emitting reports for this run to '$Dir'.\n");
262*67e74705SXin Li  }
263*67e74705SXin Li
264*67e74705SXin Li  $ENV{'CCC_ANALYZER_HTML'} = $Dir;
265*67e74705SXin Li}
266*67e74705SXin Li
267*67e74705SXin Li##----------------------------------------------------------------------------##
268*67e74705SXin Li# ComputeDigest - Compute a digest of the specified file.
269*67e74705SXin Li##----------------------------------------------------------------------------##
270*67e74705SXin Li
271*67e74705SXin Lisub ComputeDigest {
272*67e74705SXin Li  my $FName = shift;
273*67e74705SXin Li  DieDiag("Cannot read $FName to compute Digest.\n") if (! -r $FName);
274*67e74705SXin Li
275*67e74705SXin Li  # Use Digest::MD5.  We don't have to be cryptographically secure.  We're
276*67e74705SXin Li  # just looking for duplicate files that come from a non-malicious source.
277*67e74705SXin Li  # We use Digest::MD5 because it is a standard Perl module that should
278*67e74705SXin Li  # come bundled on most systems.
279*67e74705SXin Li  open(FILE, $FName) or DieDiag("Cannot open $FName when computing Digest.\n");
280*67e74705SXin Li  binmode FILE;
281*67e74705SXin Li  my $Result = Digest::MD5->new->addfile(*FILE)->hexdigest;
282*67e74705SXin Li  close(FILE);
283*67e74705SXin Li
284*67e74705SXin Li  # Return the digest.
285*67e74705SXin Li  return $Result;
286*67e74705SXin Li}
287*67e74705SXin Li
288*67e74705SXin Li##----------------------------------------------------------------------------##
289*67e74705SXin Li#  UpdatePrefix - Compute the common prefix of files.
290*67e74705SXin Li##----------------------------------------------------------------------------##
291*67e74705SXin Li
292*67e74705SXin Limy $Prefix;
293*67e74705SXin Li
294*67e74705SXin Lisub UpdatePrefix {
295*67e74705SXin Li  my $x = shift;
296*67e74705SXin Li  my $y = basename($x);
297*67e74705SXin Li  $x =~ s/\Q$y\E$//;
298*67e74705SXin Li
299*67e74705SXin Li  if (!defined $Prefix) {
300*67e74705SXin Li    $Prefix = $x;
301*67e74705SXin Li    return;
302*67e74705SXin Li  }
303*67e74705SXin Li
304*67e74705SXin Li  chop $Prefix while (!($x =~ /^\Q$Prefix/));
305*67e74705SXin Li}
306*67e74705SXin Li
307*67e74705SXin Lisub GetPrefix {
308*67e74705SXin Li  return $Prefix;
309*67e74705SXin Li}
310*67e74705SXin Li
311*67e74705SXin Li##----------------------------------------------------------------------------##
312*67e74705SXin Li#  UpdateInFilePath - Update the path in the report file.
313*67e74705SXin Li##----------------------------------------------------------------------------##
314*67e74705SXin Li
315*67e74705SXin Lisub UpdateInFilePath {
316*67e74705SXin Li  my $fname = shift;
317*67e74705SXin Li  my $regex = shift;
318*67e74705SXin Li  my $newtext = shift;
319*67e74705SXin Li
320*67e74705SXin Li  open (RIN, $fname) or die "cannot open $fname";
321*67e74705SXin Li  open (ROUT, ">", "$fname.tmp") or die "cannot open $fname.tmp";
322*67e74705SXin Li
323*67e74705SXin Li  while (<RIN>) {
324*67e74705SXin Li    s/$regex/$newtext/;
325*67e74705SXin Li    print ROUT $_;
326*67e74705SXin Li  }
327*67e74705SXin Li
328*67e74705SXin Li  close (ROUT);
329*67e74705SXin Li  close (RIN);
330*67e74705SXin Li  rename("$fname.tmp", $fname)
331*67e74705SXin Li}
332*67e74705SXin Li
333*67e74705SXin Li##----------------------------------------------------------------------------##
334*67e74705SXin Li# AddStatLine - Decode and insert a statistics line into the database.
335*67e74705SXin Li##----------------------------------------------------------------------------##
336*67e74705SXin Li
337*67e74705SXin Lisub AddStatLine {
338*67e74705SXin Li  my $Line  = shift;
339*67e74705SXin Li  my $Stats = shift;
340*67e74705SXin Li  my $File  = shift;
341*67e74705SXin Li
342*67e74705SXin Li  print $Line . "\n";
343*67e74705SXin Li
344*67e74705SXin Li  my $Regex = qr/(.*?)\ ->\ Total\ CFGBlocks:\ (\d+)\ \|\ Unreachable
345*67e74705SXin Li      \ CFGBlocks:\ (\d+)\ \|\ Exhausted\ Block:\ (yes|no)\ \|\ Empty\ WorkList:
346*67e74705SXin Li      \ (yes|no)/x;
347*67e74705SXin Li
348*67e74705SXin Li  if ($Line !~ $Regex) {
349*67e74705SXin Li    return;
350*67e74705SXin Li  }
351*67e74705SXin Li
352*67e74705SXin Li  # Create a hash of the interesting fields
353*67e74705SXin Li  my $Row = {
354*67e74705SXin Li    Filename    => $File,
355*67e74705SXin Li    Function    => $1,
356*67e74705SXin Li    Total       => $2,
357*67e74705SXin Li    Unreachable => $3,
358*67e74705SXin Li    Aborted     => $4,
359*67e74705SXin Li    Empty       => $5
360*67e74705SXin Li  };
361*67e74705SXin Li
362*67e74705SXin Li  # Add them to the stats array
363*67e74705SXin Li  push @$Stats, $Row;
364*67e74705SXin Li}
365*67e74705SXin Li
366*67e74705SXin Li##----------------------------------------------------------------------------##
367*67e74705SXin Li# ScanFile - Scan a report file for various identifying attributes.
368*67e74705SXin Li##----------------------------------------------------------------------------##
369*67e74705SXin Li
370*67e74705SXin Li# Sometimes a source file is scanned more than once, and thus produces
371*67e74705SXin Li# multiple error reports.  We use a cache to solve this problem.
372*67e74705SXin Li
373*67e74705SXin Limy %AlreadyScanned;
374*67e74705SXin Li
375*67e74705SXin Lisub ScanFile {
376*67e74705SXin Li
377*67e74705SXin Li  my $Index = shift;
378*67e74705SXin Li  my $Dir = shift;
379*67e74705SXin Li  my $FName = shift;
380*67e74705SXin Li  my $Stats = shift;
381*67e74705SXin Li
382*67e74705SXin Li  # Compute a digest for the report file.  Determine if we have already
383*67e74705SXin Li  # scanned a file that looks just like it.
384*67e74705SXin Li
385*67e74705SXin Li  my $digest = ComputeDigest("$Dir/$FName");
386*67e74705SXin Li
387*67e74705SXin Li  if (defined $AlreadyScanned{$digest}) {
388*67e74705SXin Li    # Redundant file.  Remove it.
389*67e74705SXin Li    unlink("$Dir/$FName");
390*67e74705SXin Li    return;
391*67e74705SXin Li  }
392*67e74705SXin Li
393*67e74705SXin Li  $AlreadyScanned{$digest} = 1;
394*67e74705SXin Li
395*67e74705SXin Li  # At this point the report file is not world readable.  Make it happen.
396*67e74705SXin Li  chmod(0644, "$Dir/$FName");
397*67e74705SXin Li
398*67e74705SXin Li  # Scan the report file for tags.
399*67e74705SXin Li  open(IN, "$Dir/$FName") or DieDiag("Cannot open '$Dir/$FName'\n");
400*67e74705SXin Li
401*67e74705SXin Li  my $BugType        = "";
402*67e74705SXin Li  my $BugFile        = "";
403*67e74705SXin Li  my $BugFunction    = "";
404*67e74705SXin Li  my $BugCategory    = "";
405*67e74705SXin Li  my $BugDescription = "";
406*67e74705SXin Li  my $BugPathLength  = 1;
407*67e74705SXin Li  my $BugLine        = 0;
408*67e74705SXin Li
409*67e74705SXin Li  while (<IN>) {
410*67e74705SXin Li    last if (/<!-- BUGMETAEND -->/);
411*67e74705SXin Li
412*67e74705SXin Li    if (/<!-- BUGTYPE (.*) -->$/) {
413*67e74705SXin Li      $BugType = $1;
414*67e74705SXin Li    }
415*67e74705SXin Li    elsif (/<!-- BUGFILE (.*) -->$/) {
416*67e74705SXin Li      $BugFile = abs_path($1);
417*67e74705SXin Li      if (!defined $BugFile) {
418*67e74705SXin Li         # The file no longer exists: use the original path.
419*67e74705SXin Li         $BugFile = $1;
420*67e74705SXin Li      }
421*67e74705SXin Li      UpdatePrefix($BugFile);
422*67e74705SXin Li    }
423*67e74705SXin Li    elsif (/<!-- BUGPATHLENGTH (.*) -->$/) {
424*67e74705SXin Li      $BugPathLength = $1;
425*67e74705SXin Li    }
426*67e74705SXin Li    elsif (/<!-- BUGLINE (.*) -->$/) {
427*67e74705SXin Li      $BugLine = $1;
428*67e74705SXin Li    }
429*67e74705SXin Li    elsif (/<!-- BUGCATEGORY (.*) -->$/) {
430*67e74705SXin Li      $BugCategory = $1;
431*67e74705SXin Li    }
432*67e74705SXin Li    elsif (/<!-- BUGDESC (.*) -->$/) {
433*67e74705SXin Li      $BugDescription = $1;
434*67e74705SXin Li    }
435*67e74705SXin Li    elsif (/<!-- FUNCTIONNAME (.*) -->$/) {
436*67e74705SXin Li      $BugFunction = $1;
437*67e74705SXin Li    }
438*67e74705SXin Li
439*67e74705SXin Li  }
440*67e74705SXin Li
441*67e74705SXin Li
442*67e74705SXin Li  close(IN);
443*67e74705SXin Li
444*67e74705SXin Li  if (!defined $BugCategory) {
445*67e74705SXin Li    $BugCategory = "Other";
446*67e74705SXin Li  }
447*67e74705SXin Li
448*67e74705SXin Li  # Don't add internal statistics to the bug reports
449*67e74705SXin Li  if ($BugCategory =~ /statistics/i) {
450*67e74705SXin Li    AddStatLine($BugDescription, $Stats, $BugFile);
451*67e74705SXin Li    return;
452*67e74705SXin Li  }
453*67e74705SXin Li
454*67e74705SXin Li  push @$Index,[ $FName, $BugCategory, $BugType, $BugFile, $BugFunction, $BugLine,
455*67e74705SXin Li                 $BugPathLength ];
456*67e74705SXin Li}
457*67e74705SXin Li
458*67e74705SXin Li##----------------------------------------------------------------------------##
459*67e74705SXin Li# CopyFiles - Copy resource files to target directory.
460*67e74705SXin Li##----------------------------------------------------------------------------##
461*67e74705SXin Li
462*67e74705SXin Lisub CopyFiles {
463*67e74705SXin Li
464*67e74705SXin Li  my $Dir = shift;
465*67e74705SXin Li
466*67e74705SXin Li  my $JS = Cwd::realpath("$RealBin/../share/scan-build/sorttable.js");
467*67e74705SXin Li
468*67e74705SXin Li  DieDiag("Cannot find 'sorttable.js'.\n")
469*67e74705SXin Li    if (! -r $JS);
470*67e74705SXin Li
471*67e74705SXin Li  copy($JS, "$Dir");
472*67e74705SXin Li
473*67e74705SXin Li  DieDiag("Could not copy 'sorttable.js' to '$Dir'.\n")
474*67e74705SXin Li    if (! -r "$Dir/sorttable.js");
475*67e74705SXin Li
476*67e74705SXin Li  my $CSS = Cwd::realpath("$RealBin/../share/scan-build/scanview.css");
477*67e74705SXin Li
478*67e74705SXin Li  DieDiag("Cannot find 'scanview.css'.\n")
479*67e74705SXin Li    if (! -r $CSS);
480*67e74705SXin Li
481*67e74705SXin Li  copy($CSS, "$Dir");
482*67e74705SXin Li
483*67e74705SXin Li  DieDiag("Could not copy 'scanview.css' to '$Dir'.\n")
484*67e74705SXin Li    if (! -r $CSS);
485*67e74705SXin Li}
486*67e74705SXin Li
487*67e74705SXin Li##----------------------------------------------------------------------------##
488*67e74705SXin Li# CalcStats - Calculates visitation statistics and returns the string.
489*67e74705SXin Li##----------------------------------------------------------------------------##
490*67e74705SXin Li
491*67e74705SXin Lisub CalcStats {
492*67e74705SXin Li  my $Stats = shift;
493*67e74705SXin Li
494*67e74705SXin Li  my $TotalBlocks = 0;
495*67e74705SXin Li  my $UnreachedBlocks = 0;
496*67e74705SXin Li  my $TotalFunctions = scalar(@$Stats);
497*67e74705SXin Li  my $BlockAborted = 0;
498*67e74705SXin Li  my $WorkListAborted = 0;
499*67e74705SXin Li  my $Aborted = 0;
500*67e74705SXin Li
501*67e74705SXin Li  # Calculate the unique files
502*67e74705SXin Li  my $FilesHash = {};
503*67e74705SXin Li
504*67e74705SXin Li  foreach my $Row (@$Stats) {
505*67e74705SXin Li    $FilesHash->{$Row->{Filename}} = 1;
506*67e74705SXin Li    $TotalBlocks += $Row->{Total};
507*67e74705SXin Li    $UnreachedBlocks += $Row->{Unreachable};
508*67e74705SXin Li    $BlockAborted++ if $Row->{Aborted} eq 'yes';
509*67e74705SXin Li    $WorkListAborted++ if $Row->{Empty} eq 'no';
510*67e74705SXin Li    $Aborted++ if $Row->{Aborted} eq 'yes' || $Row->{Empty} eq 'no';
511*67e74705SXin Li  }
512*67e74705SXin Li
513*67e74705SXin Li  my $TotalFiles = scalar(keys(%$FilesHash));
514*67e74705SXin Li
515*67e74705SXin Li  # Calculations
516*67e74705SXin Li  my $PercentAborted = sprintf("%.2f", $Aborted / $TotalFunctions * 100);
517*67e74705SXin Li  my $PercentBlockAborted = sprintf("%.2f", $BlockAborted / $TotalFunctions
518*67e74705SXin Li      * 100);
519*67e74705SXin Li  my $PercentWorkListAborted = sprintf("%.2f", $WorkListAborted /
520*67e74705SXin Li      $TotalFunctions * 100);
521*67e74705SXin Li  my $PercentBlocksUnreached = sprintf("%.2f", $UnreachedBlocks / $TotalBlocks
522*67e74705SXin Li      * 100);
523*67e74705SXin Li
524*67e74705SXin Li  my $StatsString = "Analyzed $TotalBlocks blocks in $TotalFunctions functions"
525*67e74705SXin Li    . " in $TotalFiles files\n"
526*67e74705SXin Li    . "$Aborted functions aborted early ($PercentAborted%)\n"
527*67e74705SXin Li    . "$BlockAborted had aborted blocks ($PercentBlockAborted%)\n"
528*67e74705SXin Li    . "$WorkListAborted had unfinished worklists ($PercentWorkListAborted%)\n"
529*67e74705SXin Li    . "$UnreachedBlocks blocks were never reached ($PercentBlocksUnreached%)\n";
530*67e74705SXin Li
531*67e74705SXin Li  return $StatsString;
532*67e74705SXin Li}
533*67e74705SXin Li
534*67e74705SXin Li##----------------------------------------------------------------------------##
535*67e74705SXin Li# Postprocess - Postprocess the results of an analysis scan.
536*67e74705SXin Li##----------------------------------------------------------------------------##
537*67e74705SXin Li
538*67e74705SXin Limy @filesFound;
539*67e74705SXin Limy $baseDir;
540*67e74705SXin Lisub FileWanted {
541*67e74705SXin Li    my $baseDirRegEx = quotemeta $baseDir;
542*67e74705SXin Li    my $file = $File::Find::name;
543*67e74705SXin Li
544*67e74705SXin Li    # The name of the file is generated by clang binary (HTMLDiagnostics.cpp)
545*67e74705SXin Li    if ($file =~ /report-.*\.html$/) {
546*67e74705SXin Li       my $relative_file = $file;
547*67e74705SXin Li       $relative_file =~ s/$baseDirRegEx//g;
548*67e74705SXin Li       push @filesFound, $relative_file;
549*67e74705SXin Li    }
550*67e74705SXin Li}
551*67e74705SXin Li
552*67e74705SXin Lisub Postprocess {
553*67e74705SXin Li
554*67e74705SXin Li  my $Dir           = shift;
555*67e74705SXin Li  my $BaseDir       = shift;
556*67e74705SXin Li  my $AnalyzerStats = shift;
557*67e74705SXin Li  my $KeepEmpty     = shift;
558*67e74705SXin Li
559*67e74705SXin Li  die "No directory specified." if (!defined $Dir);
560*67e74705SXin Li
561*67e74705SXin Li  if (! -d $Dir) {
562*67e74705SXin Li    Diag("No bugs found.\n");
563*67e74705SXin Li    return 0;
564*67e74705SXin Li  }
565*67e74705SXin Li
566*67e74705SXin Li  $baseDir = $Dir . "/";
567*67e74705SXin Li  find({ wanted => \&FileWanted, follow => 0}, $Dir);
568*67e74705SXin Li
569*67e74705SXin Li  if (scalar(@filesFound) == 0 and ! -e "$Dir/failures") {
570*67e74705SXin Li    if (! $KeepEmpty) {
571*67e74705SXin Li      Diag("Removing directory '$Dir' because it contains no reports.\n");
572*67e74705SXin Li      rmtree($Dir) or die "Cannot rmtree '$Dir' : $!";
573*67e74705SXin Li    }
574*67e74705SXin Li    Diag("No bugs found.\n");
575*67e74705SXin Li    return 0;
576*67e74705SXin Li  }
577*67e74705SXin Li
578*67e74705SXin Li  # Scan each report file and build an index.
579*67e74705SXin Li  my @Index;
580*67e74705SXin Li  my @Stats;
581*67e74705SXin Li  foreach my $file (@filesFound) { ScanFile(\@Index, $Dir, $file, \@Stats); }
582*67e74705SXin Li
583*67e74705SXin Li  # Scan the failures directory and use the information in the .info files
584*67e74705SXin Li  # to update the common prefix directory.
585*67e74705SXin Li  my @failures;
586*67e74705SXin Li  my @attributes_ignored;
587*67e74705SXin Li  if (-d "$Dir/failures") {
588*67e74705SXin Li    opendir(DIR, "$Dir/failures");
589*67e74705SXin Li    @failures = grep { /[.]info.txt$/ && !/attribute_ignored/; } readdir(DIR);
590*67e74705SXin Li    closedir(DIR);
591*67e74705SXin Li    opendir(DIR, "$Dir/failures");
592*67e74705SXin Li    @attributes_ignored = grep { /^attribute_ignored/; } readdir(DIR);
593*67e74705SXin Li    closedir(DIR);
594*67e74705SXin Li    foreach my $file (@failures) {
595*67e74705SXin Li      open IN, "$Dir/failures/$file" or DieDiag("cannot open $file\n");
596*67e74705SXin Li      my $Path = <IN>;
597*67e74705SXin Li      if (defined $Path) { UpdatePrefix($Path); }
598*67e74705SXin Li      close IN;
599*67e74705SXin Li    }
600*67e74705SXin Li  }
601*67e74705SXin Li
602*67e74705SXin Li  # Generate an index.html file.
603*67e74705SXin Li  my $FName = "$Dir/index.html";
604*67e74705SXin Li  open(OUT, ">", $FName) or DieDiag("Cannot create file '$FName'\n");
605*67e74705SXin Li
606*67e74705SXin Li  # Print out the header.
607*67e74705SXin Li
608*67e74705SXin Liprint OUT <<ENDTEXT;
609*67e74705SXin Li<html>
610*67e74705SXin Li<head>
611*67e74705SXin Li<title>${Options{HtmlTitle}}</title>
612*67e74705SXin Li<link type="text/css" rel="stylesheet" href="scanview.css"/>
613*67e74705SXin Li<script src="sorttable.js"></script>
614*67e74705SXin Li<script language='javascript' type="text/javascript">
615*67e74705SXin Lifunction SetDisplay(RowClass, DisplayVal)
616*67e74705SXin Li{
617*67e74705SXin Li  var Rows = document.getElementsByTagName("tr");
618*67e74705SXin Li  for ( var i = 0 ; i < Rows.length; ++i ) {
619*67e74705SXin Li    if (Rows[i].className == RowClass) {
620*67e74705SXin Li      Rows[i].style.display = DisplayVal;
621*67e74705SXin Li    }
622*67e74705SXin Li  }
623*67e74705SXin Li}
624*67e74705SXin Li
625*67e74705SXin Lifunction CopyCheckedStateToCheckButtons(SummaryCheckButton) {
626*67e74705SXin Li  var Inputs = document.getElementsByTagName("input");
627*67e74705SXin Li  for ( var i = 0 ; i < Inputs.length; ++i ) {
628*67e74705SXin Li    if (Inputs[i].type == "checkbox") {
629*67e74705SXin Li      if(Inputs[i] != SummaryCheckButton) {
630*67e74705SXin Li        Inputs[i].checked = SummaryCheckButton.checked;
631*67e74705SXin Li        Inputs[i].onclick();
632*67e74705SXin Li      }
633*67e74705SXin Li    }
634*67e74705SXin Li  }
635*67e74705SXin Li}
636*67e74705SXin Li
637*67e74705SXin Lifunction returnObjById( id ) {
638*67e74705SXin Li    if (document.getElementById)
639*67e74705SXin Li        var returnVar = document.getElementById(id);
640*67e74705SXin Li    else if (document.all)
641*67e74705SXin Li        var returnVar = document.all[id];
642*67e74705SXin Li    else if (document.layers)
643*67e74705SXin Li        var returnVar = document.layers[id];
644*67e74705SXin Li    return returnVar;
645*67e74705SXin Li}
646*67e74705SXin Li
647*67e74705SXin Livar NumUnchecked = 0;
648*67e74705SXin Li
649*67e74705SXin Lifunction ToggleDisplay(CheckButton, ClassName) {
650*67e74705SXin Li  if (CheckButton.checked) {
651*67e74705SXin Li    SetDisplay(ClassName, "");
652*67e74705SXin Li    if (--NumUnchecked == 0) {
653*67e74705SXin Li      returnObjById("AllBugsCheck").checked = true;
654*67e74705SXin Li    }
655*67e74705SXin Li  }
656*67e74705SXin Li  else {
657*67e74705SXin Li    SetDisplay(ClassName, "none");
658*67e74705SXin Li    NumUnchecked++;
659*67e74705SXin Li    returnObjById("AllBugsCheck").checked = false;
660*67e74705SXin Li  }
661*67e74705SXin Li}
662*67e74705SXin Li</script>
663*67e74705SXin Li<!-- SUMMARYENDHEAD -->
664*67e74705SXin Li</head>
665*67e74705SXin Li<body>
666*67e74705SXin Li<h1>${Options{HtmlTitle}}</h1>
667*67e74705SXin Li
668*67e74705SXin Li<table>
669*67e74705SXin Li<tr><th>User:</th><td>${UserName}\@${HostName}</td></tr>
670*67e74705SXin Li<tr><th>Working Directory:</th><td>${CurrentDir}</td></tr>
671*67e74705SXin Li<tr><th>Command Line:</th><td>${CmdArgs}</td></tr>
672*67e74705SXin Li<tr><th>Clang Version:</th><td>${ClangVersion}</td></tr>
673*67e74705SXin Li<tr><th>Date:</th><td>${Date}</td></tr>
674*67e74705SXin LiENDTEXT
675*67e74705SXin Li
676*67e74705SXin Liprint OUT "<tr><th>Version:</th><td>${BuildName} (${BuildDate})</td></tr>\n"
677*67e74705SXin Li  if (defined($BuildName) && defined($BuildDate));
678*67e74705SXin Li
679*67e74705SXin Liprint OUT <<ENDTEXT;
680*67e74705SXin Li</table>
681*67e74705SXin LiENDTEXT
682*67e74705SXin Li
683*67e74705SXin Li  if (scalar(@filesFound)) {
684*67e74705SXin Li    # Print out the summary table.
685*67e74705SXin Li    my %Totals;
686*67e74705SXin Li
687*67e74705SXin Li    for my $row ( @Index ) {
688*67e74705SXin Li      my $bug_type = ($row->[2]);
689*67e74705SXin Li      my $bug_category = ($row->[1]);
690*67e74705SXin Li      my $key = "$bug_category:$bug_type";
691*67e74705SXin Li
692*67e74705SXin Li      if (!defined $Totals{$key}) { $Totals{$key} = [1,$bug_category,$bug_type]; }
693*67e74705SXin Li      else { $Totals{$key}->[0]++; }
694*67e74705SXin Li    }
695*67e74705SXin Li
696*67e74705SXin Li    print OUT "<h2>Bug Summary</h2>";
697*67e74705SXin Li
698*67e74705SXin Li    if (defined $BuildName) {
699*67e74705SXin Li      print OUT "\n<p>Results in this analysis run are based on analyzer build <b>$BuildName</b>.</p>\n"
700*67e74705SXin Li    }
701*67e74705SXin Li
702*67e74705SXin Li  my $TotalBugs = scalar(@Index);
703*67e74705SXin Liprint OUT <<ENDTEXT;
704*67e74705SXin Li<table>
705*67e74705SXin Li<thead><tr><td>Bug Type</td><td>Quantity</td><td class="sorttable_nosort">Display?</td></tr></thead>
706*67e74705SXin Li<tr style="font-weight:bold"><td class="SUMM_DESC">All Bugs</td><td class="Q">$TotalBugs</td><td><center><input type="checkbox" id="AllBugsCheck" onClick="CopyCheckedStateToCheckButtons(this);" checked/></center></td></tr>
707*67e74705SXin LiENDTEXT
708*67e74705SXin Li
709*67e74705SXin Li    my $last_category;
710*67e74705SXin Li
711*67e74705SXin Li    for my $key (
712*67e74705SXin Li      sort {
713*67e74705SXin Li        my $x = $Totals{$a};
714*67e74705SXin Li        my $y = $Totals{$b};
715*67e74705SXin Li        my $res = $x->[1] cmp $y->[1];
716*67e74705SXin Li        $res = $x->[2] cmp $y->[2] if ($res == 0);
717*67e74705SXin Li        $res
718*67e74705SXin Li      } keys %Totals )
719*67e74705SXin Li    {
720*67e74705SXin Li      my $val = $Totals{$key};
721*67e74705SXin Li      my $category = $val->[1];
722*67e74705SXin Li      if (!defined $last_category or $last_category ne $category) {
723*67e74705SXin Li        $last_category = $category;
724*67e74705SXin Li        print OUT "<tr><th>$category</th><th colspan=2></th></tr>\n";
725*67e74705SXin Li      }
726*67e74705SXin Li      my $x = lc $key;
727*67e74705SXin Li      $x =~ s/[ ,'":\/()]+/_/g;
728*67e74705SXin Li      print OUT "<tr><td class=\"SUMM_DESC\">";
729*67e74705SXin Li      print OUT $val->[2];
730*67e74705SXin Li      print OUT "</td><td class=\"Q\">";
731*67e74705SXin Li      print OUT $val->[0];
732*67e74705SXin Li      print OUT "</td><td><center><input type=\"checkbox\" onClick=\"ToggleDisplay(this,'bt_$x');\" checked/></center></td></tr>\n";
733*67e74705SXin Li    }
734*67e74705SXin Li
735*67e74705SXin Li  # Print out the table of errors.
736*67e74705SXin Li
737*67e74705SXin Liprint OUT <<ENDTEXT;
738*67e74705SXin Li</table>
739*67e74705SXin Li<h2>Reports</h2>
740*67e74705SXin Li
741*67e74705SXin Li<table class="sortable" style="table-layout:automatic">
742*67e74705SXin Li<thead><tr>
743*67e74705SXin Li  <td>Bug Group</td>
744*67e74705SXin Li  <td class="sorttable_sorted">Bug Type<span id="sorttable_sortfwdind">&nbsp;&#x25BE;</span></td>
745*67e74705SXin Li  <td>File</td>
746*67e74705SXin Li  <td>Function/Method</td>
747*67e74705SXin Li  <td class="Q">Line</td>
748*67e74705SXin Li  <td class="Q">Path Length</td>
749*67e74705SXin Li  <td class="sorttable_nosort"></td>
750*67e74705SXin Li  <!-- REPORTBUGCOL -->
751*67e74705SXin Li</tr></thead>
752*67e74705SXin Li<tbody>
753*67e74705SXin LiENDTEXT
754*67e74705SXin Li
755*67e74705SXin Li    my $prefix = GetPrefix();
756*67e74705SXin Li    my $regex;
757*67e74705SXin Li    my $InFileRegex;
758*67e74705SXin Li    my $InFilePrefix = "File:</td><td>";
759*67e74705SXin Li
760*67e74705SXin Li    if (defined $prefix) {
761*67e74705SXin Li      $regex = qr/^\Q$prefix\E/is;
762*67e74705SXin Li      $InFileRegex = qr/\Q$InFilePrefix$prefix\E/is;
763*67e74705SXin Li    }
764*67e74705SXin Li
765*67e74705SXin Li    for my $row ( sort { $a->[2] cmp $b->[2] } @Index ) {
766*67e74705SXin Li      my $x = "$row->[1]:$row->[2]";
767*67e74705SXin Li      $x = lc $x;
768*67e74705SXin Li      $x =~ s/[ ,'":\/()]+/_/g;
769*67e74705SXin Li
770*67e74705SXin Li      my $ReportFile = $row->[0];
771*67e74705SXin Li
772*67e74705SXin Li      print OUT "<tr class=\"bt_$x\">";
773*67e74705SXin Li      print OUT "<td class=\"DESC\">";
774*67e74705SXin Li      print OUT $row->[1];
775*67e74705SXin Li      print OUT "</td>";
776*67e74705SXin Li      print OUT "<td class=\"DESC\">";
777*67e74705SXin Li      print OUT $row->[2];
778*67e74705SXin Li      print OUT "</td>";
779*67e74705SXin Li
780*67e74705SXin Li      # Update the file prefix.
781*67e74705SXin Li      my $fname = $row->[3];
782*67e74705SXin Li
783*67e74705SXin Li      if (defined $regex) {
784*67e74705SXin Li        $fname =~ s/$regex//;
785*67e74705SXin Li        UpdateInFilePath("$Dir/$ReportFile", $InFileRegex, $InFilePrefix)
786*67e74705SXin Li      }
787*67e74705SXin Li
788*67e74705SXin Li      print OUT "<td>";
789*67e74705SXin Li      my @fname = split /\//,$fname;
790*67e74705SXin Li      if ($#fname > 0) {
791*67e74705SXin Li        while ($#fname >= 0) {
792*67e74705SXin Li          my $x = shift @fname;
793*67e74705SXin Li          print OUT $x;
794*67e74705SXin Li          if ($#fname >= 0) {
795*67e74705SXin Li            print OUT "/";
796*67e74705SXin Li          }
797*67e74705SXin Li        }
798*67e74705SXin Li      }
799*67e74705SXin Li      else {
800*67e74705SXin Li        print OUT $fname;
801*67e74705SXin Li      }
802*67e74705SXin Li      print OUT "</td>";
803*67e74705SXin Li
804*67e74705SXin Li      print OUT "<td class=\"DESC\">";
805*67e74705SXin Li      print OUT $row->[4];
806*67e74705SXin Li      print OUT "</td>";
807*67e74705SXin Li
808*67e74705SXin Li      # Print out the quantities.
809*67e74705SXin Li      for my $j ( 5 .. 6 ) {
810*67e74705SXin Li        print OUT "<td class=\"Q\">$row->[$j]</td>";
811*67e74705SXin Li      }
812*67e74705SXin Li
813*67e74705SXin Li      # Print the rest of the columns.
814*67e74705SXin Li      for (my $j = 7; $j <= $#{$row}; ++$j) {
815*67e74705SXin Li        print OUT "<td>$row->[$j]</td>"
816*67e74705SXin Li      }
817*67e74705SXin Li
818*67e74705SXin Li      # Emit the "View" link.
819*67e74705SXin Li      print OUT "<td><a href=\"$ReportFile#EndPath\">View Report</a></td>";
820*67e74705SXin Li
821*67e74705SXin Li      # Emit REPORTBUG markers.
822*67e74705SXin Li      print OUT "\n<!-- REPORTBUG id=\"$ReportFile\" -->\n";
823*67e74705SXin Li
824*67e74705SXin Li      # End the row.
825*67e74705SXin Li      print OUT "</tr>\n";
826*67e74705SXin Li    }
827*67e74705SXin Li
828*67e74705SXin Li    print OUT "</tbody>\n</table>\n\n";
829*67e74705SXin Li  }
830*67e74705SXin Li
831*67e74705SXin Li  if (scalar (@failures) || scalar(@attributes_ignored)) {
832*67e74705SXin Li    print OUT "<h2>Analyzer Failures</h2>\n";
833*67e74705SXin Li
834*67e74705SXin Li    if (scalar @attributes_ignored) {
835*67e74705SXin Li      print OUT "The analyzer's parser ignored the following attributes:<p>\n";
836*67e74705SXin Li      print OUT "<table>\n";
837*67e74705SXin Li      print OUT "<thead><tr><td>Attribute</td><td>Source File</td><td>Preprocessed File</td><td>STDERR Output</td></tr></thead>\n";
838*67e74705SXin Li      foreach my $file (sort @attributes_ignored) {
839*67e74705SXin Li        die "cannot demangle attribute name\n" if (! ($file =~ /^attribute_ignored_(.+).txt/));
840*67e74705SXin Li        my $attribute = $1;
841*67e74705SXin Li        # Open the attribute file to get the first file that failed.
842*67e74705SXin Li        next if (!open (ATTR, "$Dir/failures/$file"));
843*67e74705SXin Li        my $ppfile = <ATTR>;
844*67e74705SXin Li        chomp $ppfile;
845*67e74705SXin Li        close ATTR;
846*67e74705SXin Li        next if (! -e "$Dir/failures/$ppfile");
847*67e74705SXin Li        # Open the info file and get the name of the source file.
848*67e74705SXin Li        open (INFO, "$Dir/failures/$ppfile.info.txt") or
849*67e74705SXin Li          die "Cannot open $Dir/failures/$ppfile.info.txt\n";
850*67e74705SXin Li        my $srcfile = <INFO>;
851*67e74705SXin Li        chomp $srcfile;
852*67e74705SXin Li        close (INFO);
853*67e74705SXin Li        # Print the information in the table.
854*67e74705SXin Li        my $prefix = GetPrefix();
855*67e74705SXin Li        if (defined $prefix) { $srcfile =~ s/^\Q$prefix//; }
856*67e74705SXin Li        print OUT "<tr><td>$attribute</td><td>$srcfile</td><td><a href=\"failures/$ppfile\">$ppfile</a></td><td><a href=\"failures/$ppfile.stderr.txt\">$ppfile.stderr.txt</a></td></tr>\n";
857*67e74705SXin Li        my $ppfile_clang = $ppfile;
858*67e74705SXin Li        $ppfile_clang =~ s/[.](.+)$/.clang.$1/;
859*67e74705SXin Li        print OUT "  <!-- REPORTPROBLEM src=\"$srcfile\" file=\"failures/$ppfile\" clangfile=\"failures/$ppfile_clang\" stderr=\"failures/$ppfile.stderr.txt\" info=\"failures/$ppfile.info.txt\" -->\n";
860*67e74705SXin Li      }
861*67e74705SXin Li      print OUT "</table>\n";
862*67e74705SXin Li    }
863*67e74705SXin Li
864*67e74705SXin Li    if (scalar @failures) {
865*67e74705SXin Li      print OUT "<p>The analyzer had problems processing the following files:</p>\n";
866*67e74705SXin Li      print OUT "<table>\n";
867*67e74705SXin Li      print OUT "<thead><tr><td>Problem</td><td>Source File</td><td>Preprocessed File</td><td>STDERR Output</td></tr></thead>\n";
868*67e74705SXin Li      foreach my $file (sort @failures) {
869*67e74705SXin Li        $file =~ /(.+).info.txt$/;
870*67e74705SXin Li        # Get the preprocessed file.
871*67e74705SXin Li        my $ppfile = $1;
872*67e74705SXin Li        # Open the info file and get the name of the source file.
873*67e74705SXin Li        open (INFO, "$Dir/failures/$file") or
874*67e74705SXin Li          die "Cannot open $Dir/failures/$file\n";
875*67e74705SXin Li        my $srcfile = <INFO>;
876*67e74705SXin Li        chomp $srcfile;
877*67e74705SXin Li        my $problem = <INFO>;
878*67e74705SXin Li        chomp $problem;
879*67e74705SXin Li        close (INFO);
880*67e74705SXin Li        # Print the information in the table.
881*67e74705SXin Li        my $prefix = GetPrefix();
882*67e74705SXin Li        if (defined $prefix) { $srcfile =~ s/^\Q$prefix//; }
883*67e74705SXin Li        print OUT "<tr><td>$problem</td><td>$srcfile</td><td><a href=\"failures/$ppfile\">$ppfile</a></td><td><a href=\"failures/$ppfile.stderr.txt\">$ppfile.stderr.txt</a></td></tr>\n";
884*67e74705SXin Li        my $ppfile_clang = $ppfile;
885*67e74705SXin Li        $ppfile_clang =~ s/[.](.+)$/.clang.$1/;
886*67e74705SXin Li        print OUT "  <!-- REPORTPROBLEM src=\"$srcfile\" file=\"failures/$ppfile\" clangfile=\"failures/$ppfile_clang\" stderr=\"failures/$ppfile.stderr.txt\" info=\"failures/$ppfile.info.txt\" -->\n";
887*67e74705SXin Li      }
888*67e74705SXin Li      print OUT "</table>\n";
889*67e74705SXin Li    }
890*67e74705SXin Li    print OUT "<p>Please consider submitting preprocessed files as <a href=\"http://clang-analyzer.llvm.org/filing_bugs.html\">bug reports</a>. <!-- REPORTCRASHES --> </p>\n";
891*67e74705SXin Li  }
892*67e74705SXin Li
893*67e74705SXin Li  print OUT "</body></html>\n";
894*67e74705SXin Li  close(OUT);
895*67e74705SXin Li  CopyFiles($Dir);
896*67e74705SXin Li
897*67e74705SXin Li  # Make sure $Dir and $BaseDir are world readable/executable.
898*67e74705SXin Li  chmod(0755, $Dir);
899*67e74705SXin Li  if (defined $BaseDir) { chmod(0755, $BaseDir); }
900*67e74705SXin Li
901*67e74705SXin Li  # Print statistics
902*67e74705SXin Li  print CalcStats(\@Stats) if $AnalyzerStats;
903*67e74705SXin Li
904*67e74705SXin Li  my $Num = scalar(@Index);
905*67e74705SXin Li  if ($Num == 1) {
906*67e74705SXin Li    Diag("$Num bug found.\n");
907*67e74705SXin Li  } else {
908*67e74705SXin Li    Diag("$Num bugs found.\n");
909*67e74705SXin Li  }
910*67e74705SXin Li  if ($Num > 0 && -r "$Dir/index.html") {
911*67e74705SXin Li    Diag("Run 'scan-view $Dir' to examine bug reports.\n");
912*67e74705SXin Li  }
913*67e74705SXin Li
914*67e74705SXin Li  DiagCrashes($Dir) if (scalar @failures || scalar @attributes_ignored);
915*67e74705SXin Li
916*67e74705SXin Li  return $Num;
917*67e74705SXin Li}
918*67e74705SXin Li
919*67e74705SXin Li##----------------------------------------------------------------------------##
920*67e74705SXin Li# RunBuildCommand - Run the build command.
921*67e74705SXin Li##----------------------------------------------------------------------------##
922*67e74705SXin Li
923*67e74705SXin Lisub AddIfNotPresent {
924*67e74705SXin Li  my $Args = shift;
925*67e74705SXin Li  my $Arg = shift;
926*67e74705SXin Li  my $found = 0;
927*67e74705SXin Li
928*67e74705SXin Li  foreach my $k (@$Args) {
929*67e74705SXin Li    if ($k eq $Arg) {
930*67e74705SXin Li      $found = 1;
931*67e74705SXin Li      last;
932*67e74705SXin Li    }
933*67e74705SXin Li  }
934*67e74705SXin Li
935*67e74705SXin Li  if ($found == 0) {
936*67e74705SXin Li    push @$Args, $Arg;
937*67e74705SXin Li  }
938*67e74705SXin Li}
939*67e74705SXin Li
940*67e74705SXin Lisub SetEnv {
941*67e74705SXin Li  my $EnvVars = shift @_;
942*67e74705SXin Li  foreach my $var ('CC', 'CXX', 'CLANG', 'CLANG_CXX',
943*67e74705SXin Li                   'CCC_ANALYZER_ANALYSIS', 'CCC_ANALYZER_PLUGINS',
944*67e74705SXin Li                   'CCC_ANALYZER_CONFIG') {
945*67e74705SXin Li    die "$var is undefined\n" if (!defined $var);
946*67e74705SXin Li    $ENV{$var} = $EnvVars->{$var};
947*67e74705SXin Li  }
948*67e74705SXin Li  foreach my $var ('CCC_ANALYZER_STORE_MODEL',
949*67e74705SXin Li                   'CCC_ANALYZER_CONSTRAINTS_MODEL',
950*67e74705SXin Li                   'CCC_ANALYZER_INTERNAL_STATS',
951*67e74705SXin Li                   'CCC_ANALYZER_OUTPUT_FORMAT',
952*67e74705SXin Li                   'CCC_CC',
953*67e74705SXin Li                   'CCC_CXX',
954*67e74705SXin Li                   'CCC_REPORT_FAILURES',
955*67e74705SXin Li                   'CLANG_ANALYZER_TARGET',
956*67e74705SXin Li                   'CCC_ANALYZER_FORCE_ANALYZE_DEBUG_CODE') {
957*67e74705SXin Li    my $x = $EnvVars->{$var};
958*67e74705SXin Li    if (defined $x) { $ENV{$var} = $x }
959*67e74705SXin Li  }
960*67e74705SXin Li  my $Verbose = $EnvVars->{'VERBOSE'};
961*67e74705SXin Li  if ($Verbose >= 2) {
962*67e74705SXin Li    $ENV{'CCC_ANALYZER_VERBOSE'} = 1;
963*67e74705SXin Li  }
964*67e74705SXin Li  if ($Verbose >= 3) {
965*67e74705SXin Li    $ENV{'CCC_ANALYZER_LOG'} = 1;
966*67e74705SXin Li  }
967*67e74705SXin Li}
968*67e74705SXin Li
969*67e74705SXin Lisub RunXcodebuild {
970*67e74705SXin Li  my $Args = shift;
971*67e74705SXin Li  my $IgnoreErrors = shift;
972*67e74705SXin Li  my $CCAnalyzer = shift;
973*67e74705SXin Li  my $CXXAnalyzer = shift;
974*67e74705SXin Li  my $EnvVars = shift;
975*67e74705SXin Li
976*67e74705SXin Li  if ($IgnoreErrors) {
977*67e74705SXin Li    AddIfNotPresent($Args,"-PBXBuildsContinueAfterErrors=YES");
978*67e74705SXin Li  }
979*67e74705SXin Li
980*67e74705SXin Li  # Detect the version of Xcode.  If Xcode 4.6 or higher, use new
981*67e74705SXin Li  # in situ support for analyzer interposition without needed to override
982*67e74705SXin Li  # the compiler.
983*67e74705SXin Li  open(DETECT_XCODE, "-|", $Args->[0], "-version") or
984*67e74705SXin Li    die "error: cannot detect version of xcodebuild\n";
985*67e74705SXin Li
986*67e74705SXin Li  my $oldBehavior = 1;
987*67e74705SXin Li
988*67e74705SXin Li  while(<DETECT_XCODE>) {
989*67e74705SXin Li    if (/^Xcode (.+)$/) {
990*67e74705SXin Li      my $ver = $1;
991*67e74705SXin Li      if ($ver =~ /^([0-9]+[.][0-9]+)[^0-9]?/) {
992*67e74705SXin Li        if ($1 >= 4.6) {
993*67e74705SXin Li          $oldBehavior = 0;
994*67e74705SXin Li          last;
995*67e74705SXin Li        }
996*67e74705SXin Li      }
997*67e74705SXin Li    }
998*67e74705SXin Li  }
999*67e74705SXin Li  close(DETECT_XCODE);
1000*67e74705SXin Li
1001*67e74705SXin Li  # If --override-compiler is explicitely requested, resort to the old
1002*67e74705SXin Li  # behavior regardless of Xcode version.
1003*67e74705SXin Li  if ($Options{OverrideCompiler}) {
1004*67e74705SXin Li    $oldBehavior = 1;
1005*67e74705SXin Li  }
1006*67e74705SXin Li
1007*67e74705SXin Li  if ($oldBehavior == 0) {
1008*67e74705SXin Li    my $OutputDir = $EnvVars->{"OUTPUT_DIR"};
1009*67e74705SXin Li    my $CLANG = $EnvVars->{"CLANG"};
1010*67e74705SXin Li    my $OtherFlags = $EnvVars->{"CCC_ANALYZER_ANALYSIS"};
1011*67e74705SXin Li    push @$Args,
1012*67e74705SXin Li        "RUN_CLANG_STATIC_ANALYZER=YES",
1013*67e74705SXin Li        "CLANG_ANALYZER_OUTPUT=plist-html",
1014*67e74705SXin Li        "CLANG_ANALYZER_EXEC=$CLANG",
1015*67e74705SXin Li        "CLANG_ANALYZER_OUTPUT_DIR=$OutputDir",
1016*67e74705SXin Li        "CLANG_ANALYZER_OTHER_FLAGS=$OtherFlags";
1017*67e74705SXin Li
1018*67e74705SXin Li    return (system(@$Args) >> 8);
1019*67e74705SXin Li  }
1020*67e74705SXin Li
1021*67e74705SXin Li  # Default to old behavior where we insert a bogus compiler.
1022*67e74705SXin Li  SetEnv($EnvVars);
1023*67e74705SXin Li
1024*67e74705SXin Li  # Check if using iPhone SDK 3.0 (simulator).  If so the compiler being
1025*67e74705SXin Li  # used should be gcc-4.2.
1026*67e74705SXin Li  if (!defined $ENV{"CCC_CC"}) {
1027*67e74705SXin Li    for (my $i = 0 ; $i < scalar(@$Args); ++$i) {
1028*67e74705SXin Li      if ($Args->[$i] eq "-sdk" && $i + 1 < scalar(@$Args)) {
1029*67e74705SXin Li        if (@$Args[$i+1] =~ /^iphonesimulator3/) {
1030*67e74705SXin Li          $ENV{"CCC_CC"} = "gcc-4.2";
1031*67e74705SXin Li          $ENV{"CCC_CXX"} = "g++-4.2";
1032*67e74705SXin Li        }
1033*67e74705SXin Li      }
1034*67e74705SXin Li    }
1035*67e74705SXin Li  }
1036*67e74705SXin Li
1037*67e74705SXin Li  # Disable PCH files until clang supports them.
1038*67e74705SXin Li  AddIfNotPresent($Args,"GCC_PRECOMPILE_PREFIX_HEADER=NO");
1039*67e74705SXin Li
1040*67e74705SXin Li  # When 'CC' is set, xcodebuild uses it to do all linking, even if we are
1041*67e74705SXin Li  # linking C++ object files.  Set 'LDPLUSPLUS' so that xcodebuild uses 'g++'
1042*67e74705SXin Li  # (via c++-analyzer) when linking such files.
1043*67e74705SXin Li  $ENV{"LDPLUSPLUS"} = $CXXAnalyzer;
1044*67e74705SXin Li
1045*67e74705SXin Li  return (system(@$Args) >> 8);
1046*67e74705SXin Li}
1047*67e74705SXin Li
1048*67e74705SXin Lisub RunBuildCommand {
1049*67e74705SXin Li  my $Args = shift;
1050*67e74705SXin Li  my $IgnoreErrors = shift;
1051*67e74705SXin Li  my $Cmd = $Args->[0];
1052*67e74705SXin Li  my $CCAnalyzer = shift;
1053*67e74705SXin Li  my $CXXAnalyzer = shift;
1054*67e74705SXin Li  my $EnvVars = shift;
1055*67e74705SXin Li
1056*67e74705SXin Li  if ($Cmd =~ /\bxcodebuild$/) {
1057*67e74705SXin Li    return RunXcodebuild($Args, $IgnoreErrors, $CCAnalyzer, $CXXAnalyzer, $EnvVars);
1058*67e74705SXin Li  }
1059*67e74705SXin Li
1060*67e74705SXin Li  # Setup the environment.
1061*67e74705SXin Li  SetEnv($EnvVars);
1062*67e74705SXin Li
1063*67e74705SXin Li  if ($Cmd =~ /(.*\/?gcc[^\/]*$)/ or
1064*67e74705SXin Li      $Cmd =~ /(.*\/?cc[^\/]*$)/ or
1065*67e74705SXin Li      $Cmd =~ /(.*\/?llvm-gcc[^\/]*$)/ or
1066*67e74705SXin Li      $Cmd =~ /(.*\/?clang$)/ or
1067*67e74705SXin Li      $Cmd =~ /(.*\/?ccc-analyzer[^\/]*$)/) {
1068*67e74705SXin Li
1069*67e74705SXin Li    if (!($Cmd =~ /ccc-analyzer/) and !defined $ENV{"CCC_CC"}) {
1070*67e74705SXin Li      $ENV{"CCC_CC"} = $1;
1071*67e74705SXin Li    }
1072*67e74705SXin Li
1073*67e74705SXin Li    shift @$Args;
1074*67e74705SXin Li    unshift @$Args, $CCAnalyzer;
1075*67e74705SXin Li  }
1076*67e74705SXin Li  elsif ($Cmd =~ /(.*\/?g\+\+[^\/]*$)/ or
1077*67e74705SXin Li        $Cmd =~ /(.*\/?c\+\+[^\/]*$)/ or
1078*67e74705SXin Li        $Cmd =~ /(.*\/?llvm-g\+\+[^\/]*$)/ or
1079*67e74705SXin Li        $Cmd =~ /(.*\/?clang\+\+$)/ or
1080*67e74705SXin Li        $Cmd =~ /(.*\/?c\+\+-analyzer[^\/]*$)/) {
1081*67e74705SXin Li    if (!($Cmd =~ /c\+\+-analyzer/) and !defined $ENV{"CCC_CXX"}) {
1082*67e74705SXin Li      $ENV{"CCC_CXX"} = $1;
1083*67e74705SXin Li    }
1084*67e74705SXin Li    shift @$Args;
1085*67e74705SXin Li    unshift @$Args, $CXXAnalyzer;
1086*67e74705SXin Li  }
1087*67e74705SXin Li  elsif ($Cmd eq "make" or $Cmd eq "gmake" or $Cmd eq "mingw32-make") {
1088*67e74705SXin Li    AddIfNotPresent($Args, "CC=$CCAnalyzer");
1089*67e74705SXin Li    AddIfNotPresent($Args, "CXX=$CXXAnalyzer");
1090*67e74705SXin Li    if ($IgnoreErrors) {
1091*67e74705SXin Li      AddIfNotPresent($Args,"-k");
1092*67e74705SXin Li      AddIfNotPresent($Args,"-i");
1093*67e74705SXin Li    }
1094*67e74705SXin Li  }
1095*67e74705SXin Li
1096*67e74705SXin Li  return (system(@$Args) >> 8);
1097*67e74705SXin Li}
1098*67e74705SXin Li
1099*67e74705SXin Li##----------------------------------------------------------------------------##
1100*67e74705SXin Li# DisplayHelp - Utility function to display all help options.
1101*67e74705SXin Li##----------------------------------------------------------------------------##
1102*67e74705SXin Li
1103*67e74705SXin Lisub DisplayHelp {
1104*67e74705SXin Li
1105*67e74705SXin Li  my $ArgClangNotFoundErrMsg = shift;
1106*67e74705SXin Liprint <<ENDTEXT;
1107*67e74705SXin LiUSAGE: $Prog [options] <build command> [build options]
1108*67e74705SXin Li
1109*67e74705SXin LiENDTEXT
1110*67e74705SXin Li
1111*67e74705SXin Li  if (defined $BuildName) {
1112*67e74705SXin Li    print "ANALYZER BUILD: $BuildName ($BuildDate)\n\n";
1113*67e74705SXin Li  }
1114*67e74705SXin Li
1115*67e74705SXin Liprint <<ENDTEXT;
1116*67e74705SXin LiOPTIONS:
1117*67e74705SXin Li
1118*67e74705SXin Li -analyze-headers
1119*67e74705SXin Li
1120*67e74705SXin Li   Also analyze functions in #included files.  By default, such functions
1121*67e74705SXin Li   are skipped unless they are called by functions within the main source file.
1122*67e74705SXin Li
1123*67e74705SXin Li --force-analyze-debug-code
1124*67e74705SXin Li
1125*67e74705SXin Li   Tells analyzer to enable assertions in code even if they were disabled
1126*67e74705SXin Li   during compilation to enable more precise results.
1127*67e74705SXin Li
1128*67e74705SXin Li -o <output location>
1129*67e74705SXin Li
1130*67e74705SXin Li   Specifies the output directory for analyzer reports. Subdirectories will be
1131*67e74705SXin Li   created as needed to represent separate "runs" of the analyzer. If this
1132*67e74705SXin Li   option is not specified, a directory is created in /tmp (TMPDIR on Mac OS X)
1133*67e74705SXin Li   to store the reports.
1134*67e74705SXin Li
1135*67e74705SXin Li -h
1136*67e74705SXin Li --help
1137*67e74705SXin Li
1138*67e74705SXin Li   Display this message.
1139*67e74705SXin Li
1140*67e74705SXin Li -k
1141*67e74705SXin Li --keep-going
1142*67e74705SXin Li
1143*67e74705SXin Li   Add a "keep on going" option to the specified build command. This option
1144*67e74705SXin Li   currently supports make and xcodebuild. This is a convenience option; one
1145*67e74705SXin Li   can specify this behavior directly using build options.
1146*67e74705SXin Li
1147*67e74705SXin Li --html-title [title]
1148*67e74705SXin Li --html-title=[title]
1149*67e74705SXin Li
1150*67e74705SXin Li   Specify the title used on generated HTML pages. If not specified, a default
1151*67e74705SXin Li   title will be used.
1152*67e74705SXin Li
1153*67e74705SXin Li -plist
1154*67e74705SXin Li
1155*67e74705SXin Li   By default the output of scan-build is a set of HTML files. This option
1156*67e74705SXin Li   outputs the results as a set of .plist files.
1157*67e74705SXin Li
1158*67e74705SXin Li -plist-html
1159*67e74705SXin Li
1160*67e74705SXin Li   By default the output of scan-build is a set of HTML files. This option
1161*67e74705SXin Li   outputs the results as a set of HTML and .plist files.
1162*67e74705SXin Li
1163*67e74705SXin Li --status-bugs
1164*67e74705SXin Li
1165*67e74705SXin Li   By default, the exit status of scan-build is the same as the executed build
1166*67e74705SXin Li   command. Specifying this option causes the exit status of scan-build to be 1
1167*67e74705SXin Li   if it found potential bugs and 0 otherwise.
1168*67e74705SXin Li
1169*67e74705SXin Li --use-cc [compiler path]
1170*67e74705SXin Li --use-cc=[compiler path]
1171*67e74705SXin Li
1172*67e74705SXin Li   scan-build analyzes a project by interposing a "fake compiler", which
1173*67e74705SXin Li   executes a real compiler for compilation and the static analyzer for analysis.
1174*67e74705SXin Li   Because of the current implementation of interposition, scan-build does not
1175*67e74705SXin Li   know what compiler your project normally uses.  Instead, it simply overrides
1176*67e74705SXin Li   the CC environment variable, and guesses your default compiler.
1177*67e74705SXin Li
1178*67e74705SXin Li   In the future, this interposition mechanism to be improved, but if you need
1179*67e74705SXin Li   scan-build to use a specific compiler for *compilation* then you can use
1180*67e74705SXin Li   this option to specify a path to that compiler.
1181*67e74705SXin Li
1182*67e74705SXin Li   If the given compiler is a cross compiler, you may also need to provide
1183*67e74705SXin Li   --analyzer-target option to properly analyze the source code because static
1184*67e74705SXin Li   analyzer runs as if the code is compiled for the host machine by default.
1185*67e74705SXin Li
1186*67e74705SXin Li --use-c++ [compiler path]
1187*67e74705SXin Li --use-c++=[compiler path]
1188*67e74705SXin Li
1189*67e74705SXin Li   This is the same as "--use-cc" but for C++ code.
1190*67e74705SXin Li
1191*67e74705SXin Li --analyzer-target [target triple name for analysis]
1192*67e74705SXin Li --analyzer-target=[target triple name for analysis]
1193*67e74705SXin Li
1194*67e74705SXin Li   This provides target triple information to clang static analyzer.
1195*67e74705SXin Li   It only changes the target for analysis but doesn't change the target of a
1196*67e74705SXin Li   real compiler given by --use-cc and --use-c++ options.
1197*67e74705SXin Li
1198*67e74705SXin Li -v
1199*67e74705SXin Li
1200*67e74705SXin Li   Enable verbose output from scan-build. A second and third '-v' increases
1201*67e74705SXin Li   verbosity.
1202*67e74705SXin Li
1203*67e74705SXin Li -V
1204*67e74705SXin Li --view
1205*67e74705SXin Li
1206*67e74705SXin Li   View analysis results in a web browser when the build completes.
1207*67e74705SXin Li
1208*67e74705SXin LiADVANCED OPTIONS:
1209*67e74705SXin Li
1210*67e74705SXin Li -no-failure-reports
1211*67e74705SXin Li
1212*67e74705SXin Li   Do not create a 'failures' subdirectory that includes analyzer crash reports
1213*67e74705SXin Li   and preprocessed source files.
1214*67e74705SXin Li
1215*67e74705SXin Li -stats
1216*67e74705SXin Li
1217*67e74705SXin Li   Generates visitation statistics for the project being analyzed.
1218*67e74705SXin Li
1219*67e74705SXin Li -maxloop <loop count>
1220*67e74705SXin Li
1221*67e74705SXin Li   Specifiy the number of times a block can be visited before giving up.
1222*67e74705SXin Li   Default is 4. Increase for more comprehensive coverage at a cost of speed.
1223*67e74705SXin Li
1224*67e74705SXin Li -internal-stats
1225*67e74705SXin Li
1226*67e74705SXin Li   Generate internal analyzer statistics.
1227*67e74705SXin Li
1228*67e74705SXin Li --use-analyzer [Xcode|path to clang]
1229*67e74705SXin Li --use-analyzer=[Xcode|path to clang]
1230*67e74705SXin Li
1231*67e74705SXin Li   scan-build uses the 'clang' executable relative to itself for static
1232*67e74705SXin Li   analysis. One can override this behavior with this option by using the
1233*67e74705SXin Li   'clang' packaged with Xcode (on OS X) or from the PATH.
1234*67e74705SXin Li
1235*67e74705SXin Li --keep-empty
1236*67e74705SXin Li
1237*67e74705SXin Li   Don't remove the build results directory even if no issues were reported.
1238*67e74705SXin Li
1239*67e74705SXin Li --override-compiler
1240*67e74705SXin Li   Always resort to the ccc-analyzer even when better interposition methods
1241*67e74705SXin Li   are available.
1242*67e74705SXin Li
1243*67e74705SXin Li -analyzer-config <options>
1244*67e74705SXin Li
1245*67e74705SXin Li   Provide options to pass through to the analyzer's -analyzer-config flag.
1246*67e74705SXin Li   Several options are separated with comma: 'key1=val1,key2=val2'
1247*67e74705SXin Li
1248*67e74705SXin Li   Available options:
1249*67e74705SXin Li     * stable-report-filename=true or false (default)
1250*67e74705SXin Li       Switch the page naming to:
1251*67e74705SXin Li       report-<filename>-<function/method name>-<id>.html
1252*67e74705SXin Li       instead of report-XXXXXX.html
1253*67e74705SXin Li
1254*67e74705SXin LiCONTROLLING CHECKERS:
1255*67e74705SXin Li
1256*67e74705SXin Li A default group of checkers are always run unless explicitly disabled.
1257*67e74705SXin Li Checkers may be enabled/disabled using the following options:
1258*67e74705SXin Li
1259*67e74705SXin Li -enable-checker [checker name]
1260*67e74705SXin Li -disable-checker [checker name]
1261*67e74705SXin Li
1262*67e74705SXin LiLOADING CHECKERS:
1263*67e74705SXin Li
1264*67e74705SXin Li Loading external checkers using the clang plugin interface:
1265*67e74705SXin Li
1266*67e74705SXin Li -load-plugin [plugin library]
1267*67e74705SXin LiENDTEXT
1268*67e74705SXin Li
1269*67e74705SXin Li  if (defined $Clang && -x $Clang) {
1270*67e74705SXin Li    # Query clang for list of checkers that are enabled.
1271*67e74705SXin Li
1272*67e74705SXin Li    # create a list to load the plugins via the 'Xclang' command line
1273*67e74705SXin Li    # argument
1274*67e74705SXin Li    my @PluginLoadCommandline_xclang;
1275*67e74705SXin Li    foreach my $param ( @{$Options{PluginsToLoad}} ) {
1276*67e74705SXin Li      push ( @PluginLoadCommandline_xclang, "-Xclang" );
1277*67e74705SXin Li      push ( @PluginLoadCommandline_xclang, "-load" );
1278*67e74705SXin Li      push ( @PluginLoadCommandline_xclang, "-Xclang" );
1279*67e74705SXin Li      push ( @PluginLoadCommandline_xclang, $param );
1280*67e74705SXin Li    }
1281*67e74705SXin Li
1282*67e74705SXin Li    my %EnabledCheckers;
1283*67e74705SXin Li    foreach my $lang ("c", "objective-c", "objective-c++", "c++") {
1284*67e74705SXin Li      my $ExecLine = join(' ', qq/"$Clang"/, @PluginLoadCommandline_xclang, "--analyze", "-x", $lang, "-", "-###", "2>&1", "|");
1285*67e74705SXin Li      open(PS, $ExecLine);
1286*67e74705SXin Li      while (<PS>) {
1287*67e74705SXin Li        foreach my $val (split /\s+/) {
1288*67e74705SXin Li          $val =~ s/\"//g;
1289*67e74705SXin Li          if ($val =~ /-analyzer-checker\=([^\s]+)/) {
1290*67e74705SXin Li            $EnabledCheckers{$1} = 1;
1291*67e74705SXin Li          }
1292*67e74705SXin Li        }
1293*67e74705SXin Li      }
1294*67e74705SXin Li    }
1295*67e74705SXin Li
1296*67e74705SXin Li    # Query clang for complete list of checkers.
1297*67e74705SXin Li    my @PluginLoadCommandline;
1298*67e74705SXin Li    foreach my $param ( @{$Options{PluginsToLoad}} ) {
1299*67e74705SXin Li      push ( @PluginLoadCommandline, "-load" );
1300*67e74705SXin Li      push ( @PluginLoadCommandline, $param );
1301*67e74705SXin Li    }
1302*67e74705SXin Li
1303*67e74705SXin Li    my $ExecLine = join(' ', qq/"$Clang"/, "-cc1", @PluginLoadCommandline, "-analyzer-checker-help", "2>&1", "|");
1304*67e74705SXin Li    open(PS, $ExecLine);
1305*67e74705SXin Li    my $foundCheckers = 0;
1306*67e74705SXin Li    while (<PS>) {
1307*67e74705SXin Li      if (/CHECKERS:/) {
1308*67e74705SXin Li        $foundCheckers = 1;
1309*67e74705SXin Li        last;
1310*67e74705SXin Li      }
1311*67e74705SXin Li    }
1312*67e74705SXin Li    if (!$foundCheckers) {
1313*67e74705SXin Li      print "  *** Could not query Clang for the list of available checkers.";
1314*67e74705SXin Li    }
1315*67e74705SXin Li    else {
1316*67e74705SXin Li      print("\nAVAILABLE CHECKERS:\n\n");
1317*67e74705SXin Li      my $skip = 0;
1318*67e74705SXin Li       while(<PS>) {
1319*67e74705SXin Li        if (/experimental/) {
1320*67e74705SXin Li          $skip = 1;
1321*67e74705SXin Li          next;
1322*67e74705SXin Li        }
1323*67e74705SXin Li        if ($skip) {
1324*67e74705SXin Li          next if (!/^\s\s[^\s]/);
1325*67e74705SXin Li          $skip = 0;
1326*67e74705SXin Li        }
1327*67e74705SXin Li        s/^\s\s//;
1328*67e74705SXin Li        if (/^([^\s]+)/) {
1329*67e74705SXin Li          # Is the checker enabled?
1330*67e74705SXin Li          my $checker = $1;
1331*67e74705SXin Li          my $enabled = 0;
1332*67e74705SXin Li          my $aggregate = "";
1333*67e74705SXin Li          foreach my $domain (split /\./, $checker) {
1334*67e74705SXin Li            $aggregate .= $domain;
1335*67e74705SXin Li            if ($EnabledCheckers{$aggregate}) {
1336*67e74705SXin Li              $enabled =1;
1337*67e74705SXin Li              last;
1338*67e74705SXin Li            }
1339*67e74705SXin Li            # append a dot, if an additional domain is added in the next iteration
1340*67e74705SXin Li            $aggregate .= ".";
1341*67e74705SXin Li          }
1342*67e74705SXin Li
1343*67e74705SXin Li          if ($enabled) {
1344*67e74705SXin Li            print " + ";
1345*67e74705SXin Li          }
1346*67e74705SXin Li          else {
1347*67e74705SXin Li            print "   ";
1348*67e74705SXin Li          }
1349*67e74705SXin Li        }
1350*67e74705SXin Li        else {
1351*67e74705SXin Li          print "   ";
1352*67e74705SXin Li        }
1353*67e74705SXin Li        print $_;
1354*67e74705SXin Li      }
1355*67e74705SXin Li      print "\nNOTE: \"+\" indicates that an analysis is enabled by default.\n";
1356*67e74705SXin Li    }
1357*67e74705SXin Li    close PS;
1358*67e74705SXin Li  }
1359*67e74705SXin Li  else {
1360*67e74705SXin Li    print "  *** Could not query Clang for the list of available checkers.\n";
1361*67e74705SXin Li    if (defined  $ArgClangNotFoundErrMsg) {
1362*67e74705SXin Li      print "  *** Reason: $ArgClangNotFoundErrMsg\n";
1363*67e74705SXin Li    }
1364*67e74705SXin Li  }
1365*67e74705SXin Li
1366*67e74705SXin Liprint <<ENDTEXT
1367*67e74705SXin Li
1368*67e74705SXin LiBUILD OPTIONS
1369*67e74705SXin Li
1370*67e74705SXin Li You can specify any build option acceptable to the build command.
1371*67e74705SXin Li
1372*67e74705SXin LiEXAMPLE
1373*67e74705SXin Li
1374*67e74705SXin Li scan-build -o /tmp/myhtmldir make -j4
1375*67e74705SXin Li
1376*67e74705SXin LiThe above example causes analysis reports to be deposited into a subdirectory
1377*67e74705SXin Liof "/tmp/myhtmldir" and to run "make" with the "-j4" option. A different
1378*67e74705SXin Lisubdirectory is created each time scan-build analyzes a project. The analyzer
1379*67e74705SXin Lishould support most parallel builds, but not distributed builds.
1380*67e74705SXin Li
1381*67e74705SXin LiENDTEXT
1382*67e74705SXin Li}
1383*67e74705SXin Li
1384*67e74705SXin Li##----------------------------------------------------------------------------##
1385*67e74705SXin Li# HtmlEscape - HTML entity encode characters that are special in HTML
1386*67e74705SXin Li##----------------------------------------------------------------------------##
1387*67e74705SXin Li
1388*67e74705SXin Lisub HtmlEscape {
1389*67e74705SXin Li  # copy argument to new variable so we don't clobber the original
1390*67e74705SXin Li  my $arg = shift || '';
1391*67e74705SXin Li  my $tmp = $arg;
1392*67e74705SXin Li  $tmp =~ s/&/&amp;/g;
1393*67e74705SXin Li  $tmp =~ s/</&lt;/g;
1394*67e74705SXin Li  $tmp =~ s/>/&gt;/g;
1395*67e74705SXin Li  return $tmp;
1396*67e74705SXin Li}
1397*67e74705SXin Li
1398*67e74705SXin Li##----------------------------------------------------------------------------##
1399*67e74705SXin Li# ShellEscape - backslash escape characters that are special to the shell
1400*67e74705SXin Li##----------------------------------------------------------------------------##
1401*67e74705SXin Li
1402*67e74705SXin Lisub ShellEscape {
1403*67e74705SXin Li  # copy argument to new variable so we don't clobber the original
1404*67e74705SXin Li  my $arg = shift || '';
1405*67e74705SXin Li  if ($arg =~ /["\s]/) { return "'" . $arg . "'"; }
1406*67e74705SXin Li  return $arg;
1407*67e74705SXin Li}
1408*67e74705SXin Li
1409*67e74705SXin Li##----------------------------------------------------------------------------##
1410*67e74705SXin Li# FindClang - searches for 'clang' executable.
1411*67e74705SXin Li##----------------------------------------------------------------------------##
1412*67e74705SXin Li
1413*67e74705SXin Lisub FindClang {
1414*67e74705SXin Li  if (!defined $Options{AnalyzerDiscoveryMethod}) {
1415*67e74705SXin Li    $Clang = Cwd::realpath("$RealBin/bin/clang") if (-f "$RealBin/bin/clang");
1416*67e74705SXin Li    if (!defined $Clang || ! -x $Clang) {
1417*67e74705SXin Li      $Clang = Cwd::realpath("$RealBin/clang") if (-f "$RealBin/clang");
1418*67e74705SXin Li    }
1419*67e74705SXin Li    if (!defined $Clang || ! -x $Clang) {
1420*67e74705SXin Li      return "error: Cannot find an executable 'clang' relative to" .
1421*67e74705SXin Li             " scan-build. Consider using --use-analyzer to pick a version of" .
1422*67e74705SXin Li             " 'clang' to use for static analysis.\n";
1423*67e74705SXin Li    }
1424*67e74705SXin Li  }
1425*67e74705SXin Li  else {
1426*67e74705SXin Li    if ($Options{AnalyzerDiscoveryMethod} =~ /^[Xx]code$/) {
1427*67e74705SXin Li      my $xcrun = `which xcrun`;
1428*67e74705SXin Li      chomp $xcrun;
1429*67e74705SXin Li      if ($xcrun eq "") {
1430*67e74705SXin Li        return "Cannot find 'xcrun' to find 'clang' for analysis.\n";
1431*67e74705SXin Li      }
1432*67e74705SXin Li      $Clang = `$xcrun -toolchain XcodeDefault -find clang`;
1433*67e74705SXin Li      chomp $Clang;
1434*67e74705SXin Li      if ($Clang eq "") {
1435*67e74705SXin Li        return "No 'clang' executable found by 'xcrun'\n";
1436*67e74705SXin Li      }
1437*67e74705SXin Li    }
1438*67e74705SXin Li    else {
1439*67e74705SXin Li      $Clang = $Options{AnalyzerDiscoveryMethod};
1440*67e74705SXin Li      if (!defined $Clang or not -x $Clang) {
1441*67e74705SXin Li        return "Cannot find an executable clang at '$Options{AnalyzerDiscoveryMethod}'\n";
1442*67e74705SXin Li      }
1443*67e74705SXin Li    }
1444*67e74705SXin Li  }
1445*67e74705SXin Li  return undef;
1446*67e74705SXin Li}
1447*67e74705SXin Li
1448*67e74705SXin Li##----------------------------------------------------------------------------##
1449*67e74705SXin Li# Process command-line arguments.
1450*67e74705SXin Li##----------------------------------------------------------------------------##
1451*67e74705SXin Li
1452*67e74705SXin Limy $RequestDisplayHelp = 0;
1453*67e74705SXin Limy $ForceDisplayHelp = 0;
1454*67e74705SXin Li
1455*67e74705SXin Lisub ProcessArgs {
1456*67e74705SXin Li  my $Args = shift;
1457*67e74705SXin Li  my $NumArgs = 0;
1458*67e74705SXin Li
1459*67e74705SXin Li  while (@$Args) {
1460*67e74705SXin Li
1461*67e74705SXin Li    $NumArgs++;
1462*67e74705SXin Li
1463*67e74705SXin Li    # Scan for options we recognize.
1464*67e74705SXin Li
1465*67e74705SXin Li    my $arg = $Args->[0];
1466*67e74705SXin Li
1467*67e74705SXin Li    if ($arg eq "-h" or $arg eq "--help") {
1468*67e74705SXin Li      $RequestDisplayHelp = 1;
1469*67e74705SXin Li      shift @$Args;
1470*67e74705SXin Li      next;
1471*67e74705SXin Li    }
1472*67e74705SXin Li
1473*67e74705SXin Li    if ($arg eq '-analyze-headers') {
1474*67e74705SXin Li      shift @$Args;
1475*67e74705SXin Li      $Options{AnalyzeHeaders} = 1;
1476*67e74705SXin Li      next;
1477*67e74705SXin Li    }
1478*67e74705SXin Li
1479*67e74705SXin Li    if ($arg eq "-o") {
1480*67e74705SXin Li      shift @$Args;
1481*67e74705SXin Li
1482*67e74705SXin Li      if (!@$Args) {
1483*67e74705SXin Li        DieDiag("'-o' option requires a target directory name.\n");
1484*67e74705SXin Li      }
1485*67e74705SXin Li
1486*67e74705SXin Li      # Construct an absolute path.  Uses the current working directory
1487*67e74705SXin Li      # as a base if the original path was not absolute.
1488*67e74705SXin Li      my $OutDir = shift @$Args;
1489*67e74705SXin Li      mkpath($OutDir) unless (-e $OutDir);  # abs_path wants existing dir
1490*67e74705SXin Li      $Options{OutputDir} = abs_path($OutDir);
1491*67e74705SXin Li
1492*67e74705SXin Li      next;
1493*67e74705SXin Li    }
1494*67e74705SXin Li
1495*67e74705SXin Li    if ($arg =~ /^--html-title(=(.+))?$/) {
1496*67e74705SXin Li      shift @$Args;
1497*67e74705SXin Li
1498*67e74705SXin Li      if (!defined $2 || $2 eq '') {
1499*67e74705SXin Li        if (!@$Args) {
1500*67e74705SXin Li          DieDiag("'--html-title' option requires a string.\n");
1501*67e74705SXin Li        }
1502*67e74705SXin Li
1503*67e74705SXin Li        $Options{HtmlTitle} = shift @$Args;
1504*67e74705SXin Li      } else {
1505*67e74705SXin Li        $Options{HtmlTitle} = $2;
1506*67e74705SXin Li      }
1507*67e74705SXin Li
1508*67e74705SXin Li      next;
1509*67e74705SXin Li    }
1510*67e74705SXin Li
1511*67e74705SXin Li    if ($arg eq "-k" or $arg eq "--keep-going") {
1512*67e74705SXin Li      shift @$Args;
1513*67e74705SXin Li      $Options{IgnoreErrors} = 1;
1514*67e74705SXin Li      next;
1515*67e74705SXin Li    }
1516*67e74705SXin Li
1517*67e74705SXin Li    if ($arg =~ /^--use-cc(=(.+))?$/) {
1518*67e74705SXin Li      shift @$Args;
1519*67e74705SXin Li      my $cc;
1520*67e74705SXin Li
1521*67e74705SXin Li      if (!defined $2 || $2 eq "") {
1522*67e74705SXin Li        if (!@$Args) {
1523*67e74705SXin Li          DieDiag("'--use-cc' option requires a compiler executable name.\n");
1524*67e74705SXin Li        }
1525*67e74705SXin Li        $cc = shift @$Args;
1526*67e74705SXin Li      }
1527*67e74705SXin Li      else {
1528*67e74705SXin Li        $cc = $2;
1529*67e74705SXin Li      }
1530*67e74705SXin Li
1531*67e74705SXin Li      $Options{UseCC} = $cc;
1532*67e74705SXin Li      next;
1533*67e74705SXin Li    }
1534*67e74705SXin Li
1535*67e74705SXin Li    if ($arg =~ /^--use-c\+\+(=(.+))?$/) {
1536*67e74705SXin Li      shift @$Args;
1537*67e74705SXin Li      my $cxx;
1538*67e74705SXin Li
1539*67e74705SXin Li      if (!defined $2 || $2 eq "") {
1540*67e74705SXin Li        if (!@$Args) {
1541*67e74705SXin Li          DieDiag("'--use-c++' option requires a compiler executable name.\n");
1542*67e74705SXin Li        }
1543*67e74705SXin Li        $cxx = shift @$Args;
1544*67e74705SXin Li      }
1545*67e74705SXin Li      else {
1546*67e74705SXin Li        $cxx = $2;
1547*67e74705SXin Li      }
1548*67e74705SXin Li
1549*67e74705SXin Li      $Options{UseCXX} = $cxx;
1550*67e74705SXin Li      next;
1551*67e74705SXin Li    }
1552*67e74705SXin Li
1553*67e74705SXin Li    if ($arg =~ /^--analyzer-target(=(.+))?$/) {
1554*67e74705SXin Li      shift @ARGV;
1555*67e74705SXin Li      my $AnalyzerTarget;
1556*67e74705SXin Li
1557*67e74705SXin Li      if (!defined $2 || $2 eq "") {
1558*67e74705SXin Li        if (!@ARGV) {
1559*67e74705SXin Li          DieDiag("'--analyzer-target' option requires a target triple name.\n");
1560*67e74705SXin Li        }
1561*67e74705SXin Li        $AnalyzerTarget = shift @ARGV;
1562*67e74705SXin Li      }
1563*67e74705SXin Li      else {
1564*67e74705SXin Li        $AnalyzerTarget = $2;
1565*67e74705SXin Li      }
1566*67e74705SXin Li
1567*67e74705SXin Li      $Options{AnalyzerTarget} = $AnalyzerTarget;
1568*67e74705SXin Li      next;
1569*67e74705SXin Li    }
1570*67e74705SXin Li
1571*67e74705SXin Li    if ($arg eq "-v") {
1572*67e74705SXin Li      shift @$Args;
1573*67e74705SXin Li      $Options{Verbose}++;
1574*67e74705SXin Li      next;
1575*67e74705SXin Li    }
1576*67e74705SXin Li
1577*67e74705SXin Li    if ($arg eq "-V" or $arg eq "--view") {
1578*67e74705SXin Li      shift @$Args;
1579*67e74705SXin Li      $Options{ViewResults} = 1;
1580*67e74705SXin Li      next;
1581*67e74705SXin Li    }
1582*67e74705SXin Li
1583*67e74705SXin Li    if ($arg eq "--status-bugs") {
1584*67e74705SXin Li      shift @$Args;
1585*67e74705SXin Li      $Options{ExitStatusFoundBugs} = 1;
1586*67e74705SXin Li      next;
1587*67e74705SXin Li    }
1588*67e74705SXin Li
1589*67e74705SXin Li    if ($arg eq "-store") {
1590*67e74705SXin Li      shift @$Args;
1591*67e74705SXin Li      $Options{StoreModel} = shift @$Args;
1592*67e74705SXin Li      next;
1593*67e74705SXin Li    }
1594*67e74705SXin Li
1595*67e74705SXin Li    if ($arg eq "-constraints") {
1596*67e74705SXin Li      shift @$Args;
1597*67e74705SXin Li      $Options{ConstraintsModel} = shift @$Args;
1598*67e74705SXin Li      next;
1599*67e74705SXin Li    }
1600*67e74705SXin Li
1601*67e74705SXin Li    if ($arg eq "-internal-stats") {
1602*67e74705SXin Li      shift @$Args;
1603*67e74705SXin Li      $Options{InternalStats} = 1;
1604*67e74705SXin Li      next;
1605*67e74705SXin Li    }
1606*67e74705SXin Li
1607*67e74705SXin Li    if ($arg eq "-plist") {
1608*67e74705SXin Li      shift @$Args;
1609*67e74705SXin Li      $Options{OutputFormat} = "plist";
1610*67e74705SXin Li      next;
1611*67e74705SXin Li    }
1612*67e74705SXin Li
1613*67e74705SXin Li    if ($arg eq "-plist-html") {
1614*67e74705SXin Li      shift @$Args;
1615*67e74705SXin Li      $Options{OutputFormat} = "plist-html";
1616*67e74705SXin Li      next;
1617*67e74705SXin Li    }
1618*67e74705SXin Li
1619*67e74705SXin Li    if ($arg eq "-analyzer-config") {
1620*67e74705SXin Li      shift @$Args;
1621*67e74705SXin Li      push @{$Options{ConfigOptions}}, shift @$Args;
1622*67e74705SXin Li      next;
1623*67e74705SXin Li    }
1624*67e74705SXin Li
1625*67e74705SXin Li    if ($arg eq "-no-failure-reports") {
1626*67e74705SXin Li      shift @$Args;
1627*67e74705SXin Li      $Options{ReportFailures} = 0;
1628*67e74705SXin Li      next;
1629*67e74705SXin Li    }
1630*67e74705SXin Li
1631*67e74705SXin Li    if ($arg eq "-stats") {
1632*67e74705SXin Li      shift @$Args;
1633*67e74705SXin Li      $Options{AnalyzerStats} = 1;
1634*67e74705SXin Li      next;
1635*67e74705SXin Li    }
1636*67e74705SXin Li
1637*67e74705SXin Li    if ($arg eq "-maxloop") {
1638*67e74705SXin Li      shift @$Args;
1639*67e74705SXin Li      $Options{MaxLoop} = shift @$Args;
1640*67e74705SXin Li      next;
1641*67e74705SXin Li    }
1642*67e74705SXin Li
1643*67e74705SXin Li    if ($arg eq "-enable-checker") {
1644*67e74705SXin Li      shift @$Args;
1645*67e74705SXin Li      my $Checker = shift @$Args;
1646*67e74705SXin Li      # Store $NumArgs to preserve the order the checkers were enabled.
1647*67e74705SXin Li      $Options{EnableCheckers}{$Checker} = $NumArgs;
1648*67e74705SXin Li      delete $Options{DisableCheckers}{$Checker};
1649*67e74705SXin Li      next;
1650*67e74705SXin Li    }
1651*67e74705SXin Li
1652*67e74705SXin Li    if ($arg eq "-disable-checker") {
1653*67e74705SXin Li      shift @$Args;
1654*67e74705SXin Li      my $Checker = shift @$Args;
1655*67e74705SXin Li      # Store $NumArgs to preserve the order the checkers were disabled.
1656*67e74705SXin Li      $Options{DisableCheckers}{$Checker} = $NumArgs;
1657*67e74705SXin Li      delete $Options{EnableCheckers}{$Checker};
1658*67e74705SXin Li      next;
1659*67e74705SXin Li    }
1660*67e74705SXin Li
1661*67e74705SXin Li    if ($arg eq "-load-plugin") {
1662*67e74705SXin Li      shift @$Args;
1663*67e74705SXin Li      push @{$Options{PluginsToLoad}}, shift @$Args;
1664*67e74705SXin Li      next;
1665*67e74705SXin Li    }
1666*67e74705SXin Li
1667*67e74705SXin Li    if ($arg eq "--use-analyzer") {
1668*67e74705SXin Li      shift @$Args;
1669*67e74705SXin Li      $Options{AnalyzerDiscoveryMethod} = shift @$Args;
1670*67e74705SXin Li      next;
1671*67e74705SXin Li    }
1672*67e74705SXin Li
1673*67e74705SXin Li    if ($arg =~ /^--use-analyzer=(.+)$/) {
1674*67e74705SXin Li      shift @$Args;
1675*67e74705SXin Li      $Options{AnalyzerDiscoveryMethod} = $1;
1676*67e74705SXin Li      next;
1677*67e74705SXin Li    }
1678*67e74705SXin Li
1679*67e74705SXin Li    if ($arg eq "--keep-empty") {
1680*67e74705SXin Li      shift @$Args;
1681*67e74705SXin Li      $Options{KeepEmpty} = 1;
1682*67e74705SXin Li      next;
1683*67e74705SXin Li    }
1684*67e74705SXin Li
1685*67e74705SXin Li    if ($arg eq "--override-compiler") {
1686*67e74705SXin Li      shift @$Args;
1687*67e74705SXin Li      $Options{OverrideCompiler} = 1;
1688*67e74705SXin Li      next;
1689*67e74705SXin Li    }
1690*67e74705SXin Li
1691*67e74705SXin Li    if ($arg eq "--force-analyze-debug-code") {
1692*67e74705SXin Li      shift @$Args;
1693*67e74705SXin Li      $Options{ForceAnalyzeDebugCode} = 1;
1694*67e74705SXin Li      next;
1695*67e74705SXin Li    }
1696*67e74705SXin Li
1697*67e74705SXin Li    DieDiag("unrecognized option '$arg'\n") if ($arg =~ /^-/);
1698*67e74705SXin Li
1699*67e74705SXin Li    $NumArgs--;
1700*67e74705SXin Li    last;
1701*67e74705SXin Li  }
1702*67e74705SXin Li  return $NumArgs;
1703*67e74705SXin Li}
1704*67e74705SXin Li
1705*67e74705SXin Liif (!@ARGV) {
1706*67e74705SXin Li  $ForceDisplayHelp = 1
1707*67e74705SXin Li}
1708*67e74705SXin Li
1709*67e74705SXin LiProcessArgs(\@ARGV);
1710*67e74705SXin Li# All arguments are now shifted from @ARGV. The rest is a build command, if any.
1711*67e74705SXin Li
1712*67e74705SXin Liif (!@ARGV and !$RequestDisplayHelp) {
1713*67e74705SXin Li  ErrorDiag("No build command specified.\n\n");
1714*67e74705SXin Li  $ForceDisplayHelp = 1;
1715*67e74705SXin Li}
1716*67e74705SXin Li
1717*67e74705SXin Limy $ClangNotFoundErrMsg = FindClang();
1718*67e74705SXin Li
1719*67e74705SXin Liif ($ForceDisplayHelp || $RequestDisplayHelp) {
1720*67e74705SXin Li  DisplayHelp($ClangNotFoundErrMsg);
1721*67e74705SXin Li  exit $ForceDisplayHelp;
1722*67e74705SXin Li}
1723*67e74705SXin Li
1724*67e74705SXin LiDieDiag($ClangNotFoundErrMsg) if (defined $ClangNotFoundErrMsg);
1725*67e74705SXin Li
1726*67e74705SXin Li$ClangCXX = $Clang;
1727*67e74705SXin Liif ($Clang !~ /\+\+(\.exe)?$/) {
1728*67e74705SXin Li  # If $Clang holds the name of the clang++ executable then we leave
1729*67e74705SXin Li  # $ClangCXX and $Clang equal, otherwise construct the name of the clang++
1730*67e74705SXin Li  # executable from the clang executable name.
1731*67e74705SXin Li
1732*67e74705SXin Li  # Determine operating system under which this copy of Perl was built.
1733*67e74705SXin Li  my $IsWinBuild = ($^O =~/msys|cygwin|MSWin32/);
1734*67e74705SXin Li  if($IsWinBuild) {
1735*67e74705SXin Li    $ClangCXX =~ s/.exe$/++.exe/;
1736*67e74705SXin Li  }
1737*67e74705SXin Li  else {
1738*67e74705SXin Li    $ClangCXX =~ s/\-\d+\.\d+$//;
1739*67e74705SXin Li    $ClangCXX .= "++";
1740*67e74705SXin Li  }
1741*67e74705SXin Li}
1742*67e74705SXin Li
1743*67e74705SXin Li# Make sure to use "" to handle paths with spaces.
1744*67e74705SXin Li$ClangVersion = HtmlEscape(`"$Clang" --version`);
1745*67e74705SXin Li
1746*67e74705SXin Li# Determine where results go.
1747*67e74705SXin Li$CmdArgs = HtmlEscape(join(' ', map(ShellEscape($_), @ARGV)));
1748*67e74705SXin Li
1749*67e74705SXin Li# Determine the output directory for the HTML reports.
1750*67e74705SXin Limy $BaseDir = $Options{OutputDir};
1751*67e74705SXin Li$Options{OutputDir} = GetHTMLRunDir($Options{OutputDir});
1752*67e74705SXin Li
1753*67e74705SXin Li# Determine the location of ccc-analyzer.
1754*67e74705SXin Limy $AbsRealBin = Cwd::realpath($RealBin);
1755*67e74705SXin Limy $Cmd = "$AbsRealBin/../libexec/ccc-analyzer";
1756*67e74705SXin Limy $CmdCXX = "$AbsRealBin/../libexec/c++-analyzer";
1757*67e74705SXin Li
1758*67e74705SXin Li# Portability: use less strict but portable check -e (file exists) instead of
1759*67e74705SXin Li# non-portable -x (file is executable). On some windows ports -x just checks
1760*67e74705SXin Li# file extension to determine if a file is executable (see Perl language
1761*67e74705SXin Li# reference, perlport)
1762*67e74705SXin Liif (!defined $Cmd || ! -e $Cmd) {
1763*67e74705SXin Li  $Cmd = "$AbsRealBin/ccc-analyzer";
1764*67e74705SXin Li  DieDiag("'ccc-analyzer' does not exist at '$Cmd'\n") if(! -e $Cmd);
1765*67e74705SXin Li}
1766*67e74705SXin Liif (!defined $CmdCXX || ! -e $CmdCXX) {
1767*67e74705SXin Li  $CmdCXX = "$AbsRealBin/c++-analyzer";
1768*67e74705SXin Li  DieDiag("'c++-analyzer' does not exist at '$CmdCXX'\n") if(! -e $CmdCXX);
1769*67e74705SXin Li}
1770*67e74705SXin Li
1771*67e74705SXin LiDiag("Using '$Clang' for static analysis\n");
1772*67e74705SXin Li
1773*67e74705SXin LiSetHtmlEnv(\@ARGV, $Options{OutputDir});
1774*67e74705SXin Li
1775*67e74705SXin Limy @AnalysesToRun;
1776*67e74705SXin Liforeach (sort { $Options{EnableCheckers}{$a} <=> $Options{EnableCheckers}{$b} }
1777*67e74705SXin Li         keys %{$Options{EnableCheckers}}) {
1778*67e74705SXin Li  # Push checkers in order they were enabled.
1779*67e74705SXin Li  push @AnalysesToRun, "-analyzer-checker", $_;
1780*67e74705SXin Li}
1781*67e74705SXin Liforeach (sort { $Options{DisableCheckers}{$a} <=> $Options{DisableCheckers}{$b} }
1782*67e74705SXin Li         keys %{$Options{DisableCheckers}}) {
1783*67e74705SXin Li  # Push checkers in order they were disabled.
1784*67e74705SXin Li  push @AnalysesToRun, "-analyzer-disable-checker", $_;
1785*67e74705SXin Li}
1786*67e74705SXin Liif ($Options{AnalyzeHeaders}) { push @AnalysesToRun, "-analyzer-opt-analyze-headers"; }
1787*67e74705SXin Liif ($Options{AnalyzerStats}) { push @AnalysesToRun, '-analyzer-checker=debug.Stats'; }
1788*67e74705SXin Liif ($Options{MaxLoop} > 0) { push @AnalysesToRun, "-analyzer-max-loop $Options{MaxLoop}"; }
1789*67e74705SXin Li
1790*67e74705SXin Li# Delay setting up other environment variables in case we can do true
1791*67e74705SXin Li# interposition.
1792*67e74705SXin Limy $CCC_ANALYZER_ANALYSIS = join ' ', @AnalysesToRun;
1793*67e74705SXin Limy $CCC_ANALYZER_PLUGINS = join ' ', map { "-load ".$_ } @{$Options{PluginsToLoad}};
1794*67e74705SXin Limy $CCC_ANALYZER_CONFIG = join ' ', map { "-analyzer-config ".$_ } @{$Options{ConfigOptions}};
1795*67e74705SXin Limy %EnvVars = (
1796*67e74705SXin Li  'CC' => $Cmd,
1797*67e74705SXin Li  'CXX' => $CmdCXX,
1798*67e74705SXin Li  'CLANG' => $Clang,
1799*67e74705SXin Li  'CLANG_CXX' => $ClangCXX,
1800*67e74705SXin Li  'VERBOSE' => $Options{Verbose},
1801*67e74705SXin Li  'CCC_ANALYZER_ANALYSIS' => $CCC_ANALYZER_ANALYSIS,
1802*67e74705SXin Li  'CCC_ANALYZER_PLUGINS' => $CCC_ANALYZER_PLUGINS,
1803*67e74705SXin Li  'CCC_ANALYZER_CONFIG' => $CCC_ANALYZER_CONFIG,
1804*67e74705SXin Li  'OUTPUT_DIR' => $Options{OutputDir},
1805*67e74705SXin Li  'CCC_CC' => $Options{UseCC},
1806*67e74705SXin Li  'CCC_CXX' => $Options{UseCXX},
1807*67e74705SXin Li  'CCC_REPORT_FAILURES' => $Options{ReportFailures},
1808*67e74705SXin Li  'CCC_ANALYZER_STORE_MODEL' => $Options{StoreModel},
1809*67e74705SXin Li  'CCC_ANALYZER_CONSTRAINTS_MODEL' => $Options{ConstraintsModel},
1810*67e74705SXin Li  'CCC_ANALYZER_INTERNAL_STATS' => $Options{InternalStats},
1811*67e74705SXin Li  'CCC_ANALYZER_OUTPUT_FORMAT' => $Options{OutputFormat},
1812*67e74705SXin Li  'CLANG_ANALYZER_TARGET' => $Options{AnalyzerTarget},
1813*67e74705SXin Li  'CCC_ANALYZER_FORCE_ANALYZE_DEBUG_CODE' => $Options{ForceAnalyzeDebugCode}
1814*67e74705SXin Li);
1815*67e74705SXin Li
1816*67e74705SXin Li# Run the build.
1817*67e74705SXin Limy $ExitStatus = RunBuildCommand(\@ARGV, $Options{IgnoreErrors}, $Cmd, $CmdCXX,
1818*67e74705SXin Li                                \%EnvVars);
1819*67e74705SXin Li
1820*67e74705SXin Liif (defined $Options{OutputFormat}) {
1821*67e74705SXin Li  if ($Options{OutputFormat} =~ /plist/) {
1822*67e74705SXin Li    Diag "Analysis run complete.\n";
1823*67e74705SXin Li    Diag "Analysis results (plist files) deposited in '$Options{OutputDir}'\n";
1824*67e74705SXin Li  }
1825*67e74705SXin Li  if ($Options{OutputFormat} =~ /html/) {
1826*67e74705SXin Li    # Postprocess the HTML directory.
1827*67e74705SXin Li    my $NumBugs = Postprocess($Options{OutputDir}, $BaseDir,
1828*67e74705SXin Li                              $Options{AnalyzerStats}, $Options{KeepEmpty});
1829*67e74705SXin Li
1830*67e74705SXin Li    if ($Options{ViewResults} and -r "$Options{OutputDir}/index.html") {
1831*67e74705SXin Li      Diag "Analysis run complete.\n";
1832*67e74705SXin Li      Diag "Viewing analysis results in '$Options{OutputDir}' using scan-view.\n";
1833*67e74705SXin Li      my $ScanView = Cwd::realpath("$RealBin/scan-view");
1834*67e74705SXin Li      if (! -x $ScanView) { $ScanView = "scan-view"; }
1835*67e74705SXin Li      if (! -x $ScanView) { $ScanView = Cwd::realpath("$RealBin/../../scan-view/bin/scan-view"); }
1836*67e74705SXin Li      exec $ScanView, "$Options{OutputDir}";
1837*67e74705SXin Li    }
1838*67e74705SXin Li
1839*67e74705SXin Li    if ($Options{ExitStatusFoundBugs}) {
1840*67e74705SXin Li      exit 1 if ($NumBugs > 0);
1841*67e74705SXin Li      exit 0;
1842*67e74705SXin Li    }
1843*67e74705SXin Li  }
1844*67e74705SXin Li}
1845*67e74705SXin Li
1846*67e74705SXin Liexit $ExitStatus;
1847