Subversion Repositories QNX 8.QNX8 LLVM/Clang compiler suite

Rev

Details | Last modification | View Log | RSS feed

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