xref: /minix3/external/bsd/llvm/dist/clang/tools/scan-build/ccc-analyzer (revision 0a6a1f1d05b60e214de2f05a7310ddd1f0e590e7)
1f4a2713aSLionel Sambuc#!/usr/bin/env perl
2f4a2713aSLionel Sambuc#
3f4a2713aSLionel Sambuc#                     The LLVM Compiler Infrastructure
4f4a2713aSLionel Sambuc#
5f4a2713aSLionel Sambuc# This file is distributed under the University of Illinois Open Source
6f4a2713aSLionel Sambuc# License. See LICENSE.TXT for details.
7f4a2713aSLionel Sambuc#
8f4a2713aSLionel Sambuc##===----------------------------------------------------------------------===##
9f4a2713aSLionel Sambuc#
10f4a2713aSLionel Sambuc#  A script designed to interpose between the build system and gcc.  It invokes
11f4a2713aSLionel Sambuc#  both gcc and the static analyzer.
12f4a2713aSLionel Sambuc#
13f4a2713aSLionel Sambuc##===----------------------------------------------------------------------===##
14f4a2713aSLionel Sambuc
15f4a2713aSLionel Sambucuse strict;
16f4a2713aSLionel Sambucuse warnings;
17f4a2713aSLionel Sambucuse FindBin;
18f4a2713aSLionel Sambucuse Cwd qw/ getcwd abs_path /;
19f4a2713aSLionel Sambucuse File::Temp qw/ tempfile /;
20f4a2713aSLionel Sambucuse File::Path qw / mkpath /;
21f4a2713aSLionel Sambucuse File::Basename;
22f4a2713aSLionel Sambucuse Text::ParseWords;
23f4a2713aSLionel Sambuc
24f4a2713aSLionel Sambuc##===----------------------------------------------------------------------===##
25f4a2713aSLionel Sambuc# Compiler command setup.
26f4a2713aSLionel Sambuc##===----------------------------------------------------------------------===##
27f4a2713aSLionel Sambuc
28*0a6a1f1dSLionel Sambuc# Search in the PATH if the compiler exists
29*0a6a1f1dSLionel Sambucsub SearchInPath {
30*0a6a1f1dSLionel Sambuc    my $file = shift;
31*0a6a1f1dSLionel Sambuc    foreach my $dir (split (':', $ENV{PATH})) {
32*0a6a1f1dSLionel Sambuc        if (-x "$dir/$file") {
33*0a6a1f1dSLionel Sambuc            return 1;
34*0a6a1f1dSLionel Sambuc        }
35*0a6a1f1dSLionel Sambuc    }
36*0a6a1f1dSLionel Sambuc    return 0;
37*0a6a1f1dSLionel Sambuc}
38*0a6a1f1dSLionel Sambuc
39f4a2713aSLionel Sambucmy $Compiler;
40f4a2713aSLionel Sambucmy $Clang;
41f4a2713aSLionel Sambucmy $DefaultCCompiler;
42f4a2713aSLionel Sambucmy $DefaultCXXCompiler;
43*0a6a1f1dSLionel Sambucmy $IsCXX;
44*0a6a1f1dSLionel Sambuc
45*0a6a1f1dSLionel Sambuc# If on OSX, use xcrun to determine the SDK root.
46*0a6a1f1dSLionel Sambucmy $UseXCRUN = 0;
47f4a2713aSLionel Sambuc
48f4a2713aSLionel Sambucif (`uname -a` =~ m/Darwin/) {
49f4a2713aSLionel Sambuc  $DefaultCCompiler = 'clang';
50f4a2713aSLionel Sambuc  $DefaultCXXCompiler = 'clang++';
51*0a6a1f1dSLionel Sambuc  # Older versions of OSX do not have xcrun to
52*0a6a1f1dSLionel Sambuc  # query the SDK location.
53*0a6a1f1dSLionel Sambuc  if (-x "/usr/bin/xcrun") {
54*0a6a1f1dSLionel Sambuc    $UseXCRUN = 1;
55*0a6a1f1dSLionel Sambuc  }
56f4a2713aSLionel Sambuc} else {
57f4a2713aSLionel Sambuc  $DefaultCCompiler = 'gcc';
58f4a2713aSLionel Sambuc  $DefaultCXXCompiler = 'g++';
59f4a2713aSLionel Sambuc}
60f4a2713aSLionel Sambuc
61f4a2713aSLionel Sambucif ($FindBin::Script =~ /c\+\+-analyzer/) {
62f4a2713aSLionel Sambuc  $Compiler = $ENV{'CCC_CXX'};
63*0a6a1f1dSLionel Sambuc  if (!defined $Compiler || (! -x $Compiler && ! SearchInPath($Compiler))) { $Compiler = $DefaultCXXCompiler; }
64f4a2713aSLionel Sambuc
65f4a2713aSLionel Sambuc  $Clang = $ENV{'CLANG_CXX'};
66*0a6a1f1dSLionel Sambuc  if (!defined $Clang || ! -x $Clang) { $Clang = 'clang++'; }
67*0a6a1f1dSLionel Sambuc
68*0a6a1f1dSLionel Sambuc  $IsCXX = 1
69f4a2713aSLionel Sambuc}
70f4a2713aSLionel Sambucelse {
71f4a2713aSLionel Sambuc  $Compiler = $ENV{'CCC_CC'};
72*0a6a1f1dSLionel Sambuc  if (!defined $Compiler || (! -x $Compiler && ! SearchInPath($Compiler))) { $Compiler = $DefaultCCompiler; }
73f4a2713aSLionel Sambuc
74f4a2713aSLionel Sambuc  $Clang = $ENV{'CLANG'};
75*0a6a1f1dSLionel Sambuc  if (!defined $Clang || ! -x $Clang) { $Clang = 'clang'; }
76*0a6a1f1dSLionel Sambuc
77*0a6a1f1dSLionel Sambuc  $IsCXX = 0
78f4a2713aSLionel Sambuc}
79f4a2713aSLionel Sambuc
80f4a2713aSLionel Sambuc##===----------------------------------------------------------------------===##
81f4a2713aSLionel Sambuc# Cleanup.
82f4a2713aSLionel Sambuc##===----------------------------------------------------------------------===##
83f4a2713aSLionel Sambuc
84f4a2713aSLionel Sambucmy $ReportFailures = $ENV{'CCC_REPORT_FAILURES'};
85f4a2713aSLionel Sambucif (!defined $ReportFailures) { $ReportFailures = 1; }
86f4a2713aSLionel Sambuc
87f4a2713aSLionel Sambucmy $CleanupFile;
88f4a2713aSLionel Sambucmy $ResultFile;
89f4a2713aSLionel Sambuc
90f4a2713aSLionel Sambuc# Remove any stale files at exit.
91f4a2713aSLionel SambucEND {
92f4a2713aSLionel Sambuc  if (defined $ResultFile && -z $ResultFile) {
93*0a6a1f1dSLionel Sambuc    unlink($ResultFile);
94f4a2713aSLionel Sambuc  }
95f4a2713aSLionel Sambuc  if (defined $CleanupFile) {
96*0a6a1f1dSLionel Sambuc    unlink($CleanupFile);
97f4a2713aSLionel Sambuc  }
98f4a2713aSLionel Sambuc}
99f4a2713aSLionel Sambuc
100f4a2713aSLionel Sambuc##----------------------------------------------------------------------------##
101f4a2713aSLionel Sambuc#  Process Clang Crashes.
102f4a2713aSLionel Sambuc##----------------------------------------------------------------------------##
103f4a2713aSLionel Sambuc
104f4a2713aSLionel Sambucsub GetPPExt {
105f4a2713aSLionel Sambuc  my $Lang = shift;
106f4a2713aSLionel Sambuc  if ($Lang =~ /objective-c\+\+/) { return ".mii" };
107f4a2713aSLionel Sambuc  if ($Lang =~ /objective-c/) { return ".mi"; }
108f4a2713aSLionel Sambuc  if ($Lang =~ /c\+\+/) { return ".ii"; }
109f4a2713aSLionel Sambuc  return ".i";
110f4a2713aSLionel Sambuc}
111f4a2713aSLionel Sambuc
112f4a2713aSLionel Sambuc# Set this to 1 if we want to include 'parser rejects' files.
113f4a2713aSLionel Sambucmy $IncludeParserRejects = 0;
114f4a2713aSLionel Sambucmy $ParserRejects = "Parser Rejects";
115f4a2713aSLionel Sambucmy $AttributeIgnored = "Attribute Ignored";
116f4a2713aSLionel Sambucmy $OtherError = "Other Error";
117f4a2713aSLionel Sambuc
118f4a2713aSLionel Sambucsub ProcessClangFailure {
119f4a2713aSLionel Sambuc  my ($Clang, $Lang, $file, $Args, $HtmlDir, $ErrorType, $ofile) = @_;
120f4a2713aSLionel Sambuc  my $Dir = "$HtmlDir/failures";
121f4a2713aSLionel Sambuc  mkpath $Dir;
122f4a2713aSLionel Sambuc
123f4a2713aSLionel Sambuc  my $prefix = "clang_crash";
124f4a2713aSLionel Sambuc  if ($ErrorType eq $ParserRejects) {
125f4a2713aSLionel Sambuc    $prefix = "clang_parser_rejects";
126f4a2713aSLionel Sambuc  }
127f4a2713aSLionel Sambuc  elsif ($ErrorType eq $AttributeIgnored) {
128f4a2713aSLionel Sambuc    $prefix = "clang_attribute_ignored";
129f4a2713aSLionel Sambuc  }
130f4a2713aSLionel Sambuc  elsif ($ErrorType eq $OtherError) {
131f4a2713aSLionel Sambuc    $prefix = "clang_other_error";
132f4a2713aSLionel Sambuc  }
133f4a2713aSLionel Sambuc
134f4a2713aSLionel Sambuc  # Generate the preprocessed file with Clang.
135f4a2713aSLionel Sambuc  my ($PPH, $PPFile) = tempfile( $prefix . "_XXXXXX",
136f4a2713aSLionel Sambuc                                 SUFFIX => GetPPExt($Lang),
137f4a2713aSLionel Sambuc                                 DIR => $Dir);
138f4a2713aSLionel Sambuc  system $Clang, @$Args, "-E", "-o", $PPFile;
139f4a2713aSLionel Sambuc  close ($PPH);
140f4a2713aSLionel Sambuc
141f4a2713aSLionel Sambuc  # Create the info file.
142f4a2713aSLionel Sambuc  open (OUT, ">", "$PPFile.info.txt") or die "Cannot open $PPFile.info.txt\n";
143f4a2713aSLionel Sambuc  print OUT abs_path($file), "\n";
144f4a2713aSLionel Sambuc  print OUT "$ErrorType\n";
145f4a2713aSLionel Sambuc  print OUT "@$Args\n";
146f4a2713aSLionel Sambuc  close OUT;
147f4a2713aSLionel Sambuc  `uname -a >> $PPFile.info.txt 2>&1`;
148f4a2713aSLionel Sambuc  `$Compiler -v >> $PPFile.info.txt 2>&1`;
149*0a6a1f1dSLionel Sambuc  rename($ofile, "$PPFile.stderr.txt");
150f4a2713aSLionel Sambuc  return (basename $PPFile);
151f4a2713aSLionel Sambuc}
152f4a2713aSLionel Sambuc
153f4a2713aSLionel Sambuc##----------------------------------------------------------------------------##
154f4a2713aSLionel Sambuc#  Running the analyzer.
155f4a2713aSLionel Sambuc##----------------------------------------------------------------------------##
156f4a2713aSLionel Sambuc
157f4a2713aSLionel Sambucsub GetCCArgs {
158f4a2713aSLionel Sambuc  my $mode = shift;
159f4a2713aSLionel Sambuc  my $Args = shift;
160f4a2713aSLionel Sambuc
161f4a2713aSLionel Sambuc  pipe (FROM_CHILD, TO_PARENT);
162f4a2713aSLionel Sambuc  my $pid = fork();
163f4a2713aSLionel Sambuc  if ($pid == 0) {
164f4a2713aSLionel Sambuc    close FROM_CHILD;
165f4a2713aSLionel Sambuc    open(STDOUT,">&", \*TO_PARENT);
166f4a2713aSLionel Sambuc    open(STDERR,">&", \*TO_PARENT);
167f4a2713aSLionel Sambuc    exec $Clang, "-###", $mode, @$Args;
168f4a2713aSLionel Sambuc  }
169f4a2713aSLionel Sambuc  close(TO_PARENT);
170f4a2713aSLionel Sambuc  my $line;
171f4a2713aSLionel Sambuc  while (<FROM_CHILD>) {
172*0a6a1f1dSLionel Sambuc    next if (!/\s"?-cc1"?\s/);
173f4a2713aSLionel Sambuc    $line = $_;
174f4a2713aSLionel Sambuc  }
175f4a2713aSLionel Sambuc
176f4a2713aSLionel Sambuc  waitpid($pid,0);
177f4a2713aSLionel Sambuc  close(FROM_CHILD);
178f4a2713aSLionel Sambuc
179f4a2713aSLionel Sambuc  die "could not find clang line\n" if (!defined $line);
180*0a6a1f1dSLionel Sambuc  # Strip leading and trailing whitespace characters.
181*0a6a1f1dSLionel Sambuc  $line =~ s/^\s+|\s+$//g;
182f4a2713aSLionel Sambuc  my @items = quotewords('\s+', 0, $line);
183f4a2713aSLionel Sambuc  my $cmd = shift @items;
184f4a2713aSLionel Sambuc  die "cannot find 'clang' in 'clang' command\n" if (!($cmd =~ /clang/));
185f4a2713aSLionel Sambuc  return \@items;
186f4a2713aSLionel Sambuc}
187f4a2713aSLionel Sambuc
188f4a2713aSLionel Sambucsub Analyze {
189f4a2713aSLionel Sambuc  my ($Clang, $OriginalArgs, $AnalyzeArgs, $Lang, $Output, $Verbose, $HtmlDir,
190f4a2713aSLionel Sambuc      $file) = @_;
191f4a2713aSLionel Sambuc
192f4a2713aSLionel Sambuc  my @Args = @$OriginalArgs;
193f4a2713aSLionel Sambuc  my $Cmd;
194f4a2713aSLionel Sambuc  my @CmdArgs;
195f4a2713aSLionel Sambuc  my @CmdArgsSansAnalyses;
196f4a2713aSLionel Sambuc
197f4a2713aSLionel Sambuc  if ($Lang =~ /header/) {
198f4a2713aSLionel Sambuc    exit 0 if (!defined ($Output));
199f4a2713aSLionel Sambuc    $Cmd = 'cp';
200f4a2713aSLionel Sambuc    push @CmdArgs, $file;
201f4a2713aSLionel Sambuc    # Remove the PCH extension.
202f4a2713aSLionel Sambuc    $Output =~ s/[.]gch$//;
203f4a2713aSLionel Sambuc    push @CmdArgs, $Output;
204f4a2713aSLionel Sambuc    @CmdArgsSansAnalyses = @CmdArgs;
205f4a2713aSLionel Sambuc  }
206f4a2713aSLionel Sambuc  else {
207f4a2713aSLionel Sambuc    $Cmd = $Clang;
208f4a2713aSLionel Sambuc
209f4a2713aSLionel Sambuc    # Create arguments for doing regular parsing.
210f4a2713aSLionel Sambuc    my $SyntaxArgs = GetCCArgs("-fsyntax-only", \@Args);
211f4a2713aSLionel Sambuc    @CmdArgsSansAnalyses = @$SyntaxArgs;
212f4a2713aSLionel Sambuc
213f4a2713aSLionel Sambuc    # Create arguments for doing static analysis.
214f4a2713aSLionel Sambuc    if (defined $ResultFile) {
215f4a2713aSLionel Sambuc      push @Args, '-o', $ResultFile;
216f4a2713aSLionel Sambuc    }
217f4a2713aSLionel Sambuc    elsif (defined $HtmlDir) {
218f4a2713aSLionel Sambuc      push @Args, '-o', $HtmlDir;
219f4a2713aSLionel Sambuc    }
220f4a2713aSLionel Sambuc    if ($Verbose) {
221f4a2713aSLionel Sambuc      push @Args, "-Xclang", "-analyzer-display-progress";
222f4a2713aSLionel Sambuc    }
223f4a2713aSLionel Sambuc
224f4a2713aSLionel Sambuc    foreach my $arg (@$AnalyzeArgs) {
225f4a2713aSLionel Sambuc      push @Args, "-Xclang", $arg;
226f4a2713aSLionel Sambuc    }
227f4a2713aSLionel Sambuc
228f4a2713aSLionel Sambuc    # Display Ubiviz graph?
229f4a2713aSLionel Sambuc    if (defined $ENV{'CCC_UBI'}) {
230f4a2713aSLionel Sambuc      push @Args, "-Xclang", "-analyzer-viz-egraph-ubigraph";
231f4a2713aSLionel Sambuc    }
232f4a2713aSLionel Sambuc
233f4a2713aSLionel Sambuc    my $AnalysisArgs = GetCCArgs("--analyze", \@Args);
234f4a2713aSLionel Sambuc    @CmdArgs = @$AnalysisArgs;
235f4a2713aSLionel Sambuc  }
236f4a2713aSLionel Sambuc
237f4a2713aSLionel Sambuc  my @PrintArgs;
238f4a2713aSLionel Sambuc  my $dir;
239f4a2713aSLionel Sambuc
240f4a2713aSLionel Sambuc  if ($Verbose) {
241f4a2713aSLionel Sambuc    $dir = getcwd();
242f4a2713aSLionel Sambuc    print STDERR "\n[LOCATION]: $dir\n";
243f4a2713aSLionel Sambuc    push @PrintArgs,"'$Cmd'";
244f4a2713aSLionel Sambuc    foreach my $arg (@CmdArgs) {
245f4a2713aSLionel Sambuc        push @PrintArgs,"\'$arg\'";
246f4a2713aSLionel Sambuc    }
247f4a2713aSLionel Sambuc  }
248f4a2713aSLionel Sambuc  if ($Verbose == 1) {
249f4a2713aSLionel Sambuc    # We MUST print to stderr.  Some clients use the stdout output of
250f4a2713aSLionel Sambuc    # gcc for various purposes.
251f4a2713aSLionel Sambuc    print STDERR join(' ', @PrintArgs);
252f4a2713aSLionel Sambuc    print STDERR "\n";
253f4a2713aSLionel Sambuc  }
254f4a2713aSLionel Sambuc  elsif ($Verbose == 2) {
255f4a2713aSLionel Sambuc    print STDERR "#SHELL (cd '$dir' && @PrintArgs)\n";
256f4a2713aSLionel Sambuc  }
257f4a2713aSLionel Sambuc
258f4a2713aSLionel Sambuc  # Capture the STDERR of clang and send it to a temporary file.
259f4a2713aSLionel Sambuc  # Capture the STDOUT of clang and reroute it to ccc-analyzer's STDERR.
260f4a2713aSLionel Sambuc  # We save the output file in the 'crashes' directory if clang encounters
261f4a2713aSLionel Sambuc  # any problems with the file.
262f4a2713aSLionel Sambuc  pipe (FROM_CHILD, TO_PARENT);
263f4a2713aSLionel Sambuc  my $pid = fork();
264f4a2713aSLionel Sambuc  if ($pid == 0) {
265f4a2713aSLionel Sambuc    close FROM_CHILD;
266f4a2713aSLionel Sambuc    open(STDOUT,">&", \*TO_PARENT);
267f4a2713aSLionel Sambuc    open(STDERR,">&", \*TO_PARENT);
268f4a2713aSLionel Sambuc    exec $Cmd, @CmdArgs;
269f4a2713aSLionel Sambuc  }
270f4a2713aSLionel Sambuc
271f4a2713aSLionel Sambuc  close TO_PARENT;
272f4a2713aSLionel Sambuc  my ($ofh, $ofile) = tempfile("clang_output_XXXXXX", DIR => $HtmlDir);
273f4a2713aSLionel Sambuc
274f4a2713aSLionel Sambuc  while (<FROM_CHILD>) {
275f4a2713aSLionel Sambuc    print $ofh $_;
276f4a2713aSLionel Sambuc    print STDERR $_;
277f4a2713aSLionel Sambuc  }
278f4a2713aSLionel Sambuc  close $ofh;
279f4a2713aSLionel Sambuc
280f4a2713aSLionel Sambuc  waitpid($pid,0);
281f4a2713aSLionel Sambuc  close(FROM_CHILD);
282f4a2713aSLionel Sambuc  my $Result = $?;
283f4a2713aSLionel Sambuc
284f4a2713aSLionel Sambuc  # Did the command die because of a signal?
285f4a2713aSLionel Sambuc  if ($ReportFailures) {
286f4a2713aSLionel Sambuc    if ($Result & 127 and $Cmd eq $Clang and defined $HtmlDir) {
287f4a2713aSLionel Sambuc      ProcessClangFailure($Clang, $Lang, $file, \@CmdArgsSansAnalyses,
288f4a2713aSLionel Sambuc                          $HtmlDir, "Crash", $ofile);
289f4a2713aSLionel Sambuc    }
290f4a2713aSLionel Sambuc    elsif ($Result) {
291f4a2713aSLionel Sambuc      if ($IncludeParserRejects && !($file =~/conftest/)) {
292f4a2713aSLionel Sambuc        ProcessClangFailure($Clang, $Lang, $file, \@CmdArgsSansAnalyses,
293f4a2713aSLionel Sambuc                            $HtmlDir, $ParserRejects, $ofile);
294f4a2713aSLionel Sambuc      } else {
295f4a2713aSLionel Sambuc        ProcessClangFailure($Clang, $Lang, $file, \@CmdArgsSansAnalyses,
296f4a2713aSLionel Sambuc                            $HtmlDir, $OtherError, $ofile);
297f4a2713aSLionel Sambuc      }
298f4a2713aSLionel Sambuc    }
299f4a2713aSLionel Sambuc    else {
300f4a2713aSLionel Sambuc      # Check if there were any unhandled attributes.
301f4a2713aSLionel Sambuc      if (open(CHILD, $ofile)) {
302f4a2713aSLionel Sambuc        my %attributes_not_handled;
303f4a2713aSLionel Sambuc
304f4a2713aSLionel Sambuc        # Don't flag warnings about the following attributes that we
305f4a2713aSLionel Sambuc        # know are currently not supported by Clang.
306f4a2713aSLionel Sambuc        $attributes_not_handled{"cdecl"} = 1;
307f4a2713aSLionel Sambuc
308f4a2713aSLionel Sambuc        my $ppfile;
309f4a2713aSLionel Sambuc        while (<CHILD>) {
310f4a2713aSLionel Sambuc          next if (! /warning: '([^\']+)' attribute ignored/);
311f4a2713aSLionel Sambuc
312f4a2713aSLionel Sambuc          # Have we already spotted this unhandled attribute?
313f4a2713aSLionel Sambuc          next if (defined $attributes_not_handled{$1});
314f4a2713aSLionel Sambuc          $attributes_not_handled{$1} = 1;
315f4a2713aSLionel Sambuc
316f4a2713aSLionel Sambuc          # Get the name of the attribute file.
317f4a2713aSLionel Sambuc          my $dir = "$HtmlDir/failures";
318f4a2713aSLionel Sambuc          my $afile = "$dir/attribute_ignored_$1.txt";
319f4a2713aSLionel Sambuc
320f4a2713aSLionel Sambuc          # Only create another preprocessed file if the attribute file
321f4a2713aSLionel Sambuc          # doesn't exist yet.
322f4a2713aSLionel Sambuc          next if (-e $afile);
323f4a2713aSLionel Sambuc
324f4a2713aSLionel Sambuc          # Add this file to the list of files that contained this attribute.
325f4a2713aSLionel Sambuc          # Generate a preprocessed file if we haven't already.
326f4a2713aSLionel Sambuc          if (!(defined $ppfile)) {
327f4a2713aSLionel Sambuc            $ppfile = ProcessClangFailure($Clang, $Lang, $file,
328f4a2713aSLionel Sambuc                                          \@CmdArgsSansAnalyses,
329f4a2713aSLionel Sambuc                                          $HtmlDir, $AttributeIgnored, $ofile);
330f4a2713aSLionel Sambuc          }
331f4a2713aSLionel Sambuc
332f4a2713aSLionel Sambuc          mkpath $dir;
333f4a2713aSLionel Sambuc          open(AFILE, ">$afile");
334f4a2713aSLionel Sambuc          print AFILE "$ppfile\n";
335f4a2713aSLionel Sambuc          close(AFILE);
336f4a2713aSLionel Sambuc        }
337f4a2713aSLionel Sambuc        close CHILD;
338f4a2713aSLionel Sambuc      }
339f4a2713aSLionel Sambuc    }
340f4a2713aSLionel Sambuc  }
341f4a2713aSLionel Sambuc
342f4a2713aSLionel Sambuc  unlink($ofile);
343f4a2713aSLionel Sambuc}
344f4a2713aSLionel Sambuc
345f4a2713aSLionel Sambuc##----------------------------------------------------------------------------##
346f4a2713aSLionel Sambuc#  Lookup tables.
347f4a2713aSLionel Sambuc##----------------------------------------------------------------------------##
348f4a2713aSLionel Sambuc
349f4a2713aSLionel Sambucmy %CompileOptionMap = (
350f4a2713aSLionel Sambuc  '-nostdinc' => 0,
351f4a2713aSLionel Sambuc  '-include' => 1,
352f4a2713aSLionel Sambuc  '-idirafter' => 1,
353f4a2713aSLionel Sambuc  '-imacros' => 1,
354f4a2713aSLionel Sambuc  '-iprefix' => 1,
355f4a2713aSLionel Sambuc  '-iquote' => 1,
356f4a2713aSLionel Sambuc  '-isystem' => 1,
357f4a2713aSLionel Sambuc  '-iwithprefix' => 1,
358f4a2713aSLionel Sambuc  '-iwithprefixbefore' => 1
359f4a2713aSLionel Sambuc);
360f4a2713aSLionel Sambuc
361f4a2713aSLionel Sambucmy %LinkerOptionMap = (
362f4a2713aSLionel Sambuc  '-framework' => 1,
363f4a2713aSLionel Sambuc  '-fobjc-link-runtime' => 0
364f4a2713aSLionel Sambuc);
365f4a2713aSLionel Sambuc
366f4a2713aSLionel Sambucmy %CompilerLinkerOptionMap = (
367f4a2713aSLionel Sambuc  '-Wwrite-strings' => 0,
368f4a2713aSLionel Sambuc  '-ftrapv-handler' => 1, # specifically call out separated -f flag
369f4a2713aSLionel Sambuc  '-mios-simulator-version-min' => 0, # This really has 1 argument, but always has '='
370f4a2713aSLionel Sambuc  '-isysroot' => 1,
371f4a2713aSLionel Sambuc  '-arch' => 1,
372f4a2713aSLionel Sambuc  '-m32' => 0,
373f4a2713aSLionel Sambuc  '-m64' => 0,
374f4a2713aSLionel Sambuc  '-stdlib' => 0, # This is really a 1 argument, but always has '='
375*0a6a1f1dSLionel Sambuc  '--sysroot' => 1,
376f4a2713aSLionel Sambuc  '-target' => 1,
377f4a2713aSLionel Sambuc  '-v' => 0,
378f4a2713aSLionel Sambuc  '-mmacosx-version-min' => 0, # This is really a 1 argument, but always has '='
379f4a2713aSLionel Sambuc  '-miphoneos-version-min' => 0 # This is really a 1 argument, but always has '='
380f4a2713aSLionel Sambuc);
381f4a2713aSLionel Sambuc
382f4a2713aSLionel Sambucmy %IgnoredOptionMap = (
383f4a2713aSLionel Sambuc  '-MT' => 1,  # Ignore these preprocessor options.
384f4a2713aSLionel Sambuc  '-MF' => 1,
385f4a2713aSLionel Sambuc
386f4a2713aSLionel Sambuc  '-fsyntax-only' => 0,
387f4a2713aSLionel Sambuc  '-save-temps' => 0,
388f4a2713aSLionel Sambuc  '-install_name' => 1,
389f4a2713aSLionel Sambuc  '-exported_symbols_list' => 1,
390f4a2713aSLionel Sambuc  '-current_version' => 1,
391f4a2713aSLionel Sambuc  '-compatibility_version' => 1,
392f4a2713aSLionel Sambuc  '-init' => 1,
393f4a2713aSLionel Sambuc  '-e' => 1,
394f4a2713aSLionel Sambuc  '-seg1addr' => 1,
395f4a2713aSLionel Sambuc  '-bundle_loader' => 1,
396f4a2713aSLionel Sambuc  '-multiply_defined' => 1,
397f4a2713aSLionel Sambuc  '-sectorder' => 3,
398f4a2713aSLionel Sambuc  '--param' => 1,
399f4a2713aSLionel Sambuc  '-u' => 1,
400f4a2713aSLionel Sambuc  '--serialize-diagnostics' => 1
401f4a2713aSLionel Sambuc);
402f4a2713aSLionel Sambuc
403f4a2713aSLionel Sambucmy %LangMap = (
404*0a6a1f1dSLionel Sambuc  'c'   => $IsCXX ? 'c++' : 'c',
405f4a2713aSLionel Sambuc  'cp'  => 'c++',
406f4a2713aSLionel Sambuc  'cpp' => 'c++',
407f4a2713aSLionel Sambuc  'cxx' => 'c++',
408f4a2713aSLionel Sambuc  'txx' => 'c++',
409f4a2713aSLionel Sambuc  'cc'  => 'c++',
410f4a2713aSLionel Sambuc  'C'   => 'c++',
411*0a6a1f1dSLionel Sambuc  'ii'  => 'c++-cpp-output',
412*0a6a1f1dSLionel Sambuc  'i'   => $IsCXX ? 'c++-cpp-output' : 'c-cpp-output',
413f4a2713aSLionel Sambuc  'm'   => 'objective-c',
414f4a2713aSLionel Sambuc  'mi'  => 'objective-c-cpp-output',
415*0a6a1f1dSLionel Sambuc  'mm'  => 'objective-c++',
416*0a6a1f1dSLionel Sambuc  'mii' => 'objective-c++-cpp-output',
417f4a2713aSLionel Sambuc);
418f4a2713aSLionel Sambuc
419f4a2713aSLionel Sambucmy %UniqueOptions = (
420f4a2713aSLionel Sambuc  '-isysroot' => 0
421f4a2713aSLionel Sambuc);
422f4a2713aSLionel Sambuc
423f4a2713aSLionel Sambuc##----------------------------------------------------------------------------##
424f4a2713aSLionel Sambuc# Languages accepted.
425f4a2713aSLionel Sambuc##----------------------------------------------------------------------------##
426f4a2713aSLionel Sambuc
427f4a2713aSLionel Sambucmy %LangsAccepted = (
428f4a2713aSLionel Sambuc  "objective-c" => 1,
429f4a2713aSLionel Sambuc  "c" => 1,
430f4a2713aSLionel Sambuc  "c++" => 1,
431*0a6a1f1dSLionel Sambuc  "objective-c++" => 1,
432*0a6a1f1dSLionel Sambuc  "c-cpp-output" => 1,
433*0a6a1f1dSLionel Sambuc  "objective-c-cpp-output" => 1,
434*0a6a1f1dSLionel Sambuc  "c++-cpp-output" => 1
435f4a2713aSLionel Sambuc);
436f4a2713aSLionel Sambuc
437f4a2713aSLionel Sambuc##----------------------------------------------------------------------------##
438f4a2713aSLionel Sambuc#  Main Logic.
439f4a2713aSLionel Sambuc##----------------------------------------------------------------------------##
440f4a2713aSLionel Sambuc
441f4a2713aSLionel Sambucmy $Action = 'link';
442f4a2713aSLionel Sambucmy @CompileOpts;
443f4a2713aSLionel Sambucmy @LinkOpts;
444f4a2713aSLionel Sambucmy @Files;
445f4a2713aSLionel Sambucmy $Lang;
446f4a2713aSLionel Sambucmy $Output;
447f4a2713aSLionel Sambucmy %Uniqued;
448f4a2713aSLionel Sambuc
449f4a2713aSLionel Sambuc# Forward arguments to gcc.
450f4a2713aSLionel Sambucmy $Status = system($Compiler,@ARGV);
451f4a2713aSLionel Sambucif (defined $ENV{'CCC_ANALYZER_LOG'}) {
452f4a2713aSLionel Sambuc  print STDERR "$Compiler @ARGV\n";
453f4a2713aSLionel Sambuc}
454f4a2713aSLionel Sambucif ($Status) { exit($Status >> 8); }
455f4a2713aSLionel Sambuc
456f4a2713aSLionel Sambuc# Get the analysis options.
457f4a2713aSLionel Sambucmy $Analyses = $ENV{'CCC_ANALYZER_ANALYSIS'};
458f4a2713aSLionel Sambuc
459f4a2713aSLionel Sambuc# Get the plugins to load.
460f4a2713aSLionel Sambucmy $Plugins = $ENV{'CCC_ANALYZER_PLUGINS'};
461f4a2713aSLionel Sambuc
462f4a2713aSLionel Sambuc# Get the store model.
463f4a2713aSLionel Sambucmy $StoreModel = $ENV{'CCC_ANALYZER_STORE_MODEL'};
464f4a2713aSLionel Sambuc
465f4a2713aSLionel Sambuc# Get the constraints engine.
466f4a2713aSLionel Sambucmy $ConstraintsModel = $ENV{'CCC_ANALYZER_CONSTRAINTS_MODEL'};
467f4a2713aSLionel Sambuc
468f4a2713aSLionel Sambuc#Get the internal stats setting.
469f4a2713aSLionel Sambucmy $InternalStats = $ENV{'CCC_ANALYZER_INTERNAL_STATS'};
470f4a2713aSLionel Sambuc
471f4a2713aSLionel Sambuc# Get the output format.
472f4a2713aSLionel Sambucmy $OutputFormat = $ENV{'CCC_ANALYZER_OUTPUT_FORMAT'};
473f4a2713aSLionel Sambucif (!defined $OutputFormat) { $OutputFormat = "html"; }
474f4a2713aSLionel Sambuc
475*0a6a1f1dSLionel Sambuc# Get the config options.
476*0a6a1f1dSLionel Sambucmy $ConfigOptions = $ENV{'CCC_ANALYZER_CONFIG'};
477*0a6a1f1dSLionel Sambuc
478f4a2713aSLionel Sambuc# Determine the level of verbosity.
479f4a2713aSLionel Sambucmy $Verbose = 0;
480f4a2713aSLionel Sambucif (defined $ENV{'CCC_ANALYZER_VERBOSE'}) { $Verbose = 1; }
481f4a2713aSLionel Sambucif (defined $ENV{'CCC_ANALYZER_LOG'}) { $Verbose = 2; }
482f4a2713aSLionel Sambuc
483f4a2713aSLionel Sambuc# Get the HTML output directory.
484f4a2713aSLionel Sambucmy $HtmlDir = $ENV{'CCC_ANALYZER_HTML'};
485f4a2713aSLionel Sambuc
486f4a2713aSLionel Sambucmy %DisabledArchs = ('ppc' => 1, 'ppc64' => 1);
487f4a2713aSLionel Sambucmy %ArchsSeen;
488f4a2713aSLionel Sambucmy $HadArch = 0;
489*0a6a1f1dSLionel Sambucmy $HasSDK = 0;
490f4a2713aSLionel Sambuc
491f4a2713aSLionel Sambuc# Process the arguments.
492f4a2713aSLionel Sambucforeach (my $i = 0; $i < scalar(@ARGV); ++$i) {
493f4a2713aSLionel Sambuc  my $Arg = $ARGV[$i];
494f4a2713aSLionel Sambuc  my ($ArgKey) = split /=/,$Arg,2;
495f4a2713aSLionel Sambuc
496f4a2713aSLionel Sambuc  # Modes ccc-analyzer supports
497f4a2713aSLionel Sambuc  if ($Arg =~ /^-(E|MM?)$/) { $Action = 'preprocess'; }
498f4a2713aSLionel Sambuc  elsif ($Arg eq '-c') { $Action = 'compile'; }
499f4a2713aSLionel Sambuc  elsif ($Arg =~ /^-print-prog-name/) { exit 0; }
500f4a2713aSLionel Sambuc
501f4a2713aSLionel Sambuc  # Specially handle duplicate cases of -arch
502f4a2713aSLionel Sambuc  if ($Arg eq "-arch") {
503f4a2713aSLionel Sambuc    my $arch = $ARGV[$i+1];
504f4a2713aSLionel Sambuc    # We don't want to process 'ppc' because of Clang's lack of support
505f4a2713aSLionel Sambuc    # for Altivec (also some #defines won't likely be defined correctly, etc.)
506f4a2713aSLionel Sambuc    if (!(defined $DisabledArchs{$arch})) { $ArchsSeen{$arch} = 1; }
507f4a2713aSLionel Sambuc    $HadArch = 1;
508f4a2713aSLionel Sambuc    ++$i;
509f4a2713aSLionel Sambuc    next;
510f4a2713aSLionel Sambuc  }
511f4a2713aSLionel Sambuc
512*0a6a1f1dSLionel Sambuc  # On OSX/iOS, record if an SDK path was specified.  This
513*0a6a1f1dSLionel Sambuc  # is innocuous for other platforms, so the check just happens.
514*0a6a1f1dSLionel Sambuc  if ($Arg =~ /^-isysroot/) {
515*0a6a1f1dSLionel Sambuc    $HasSDK = 1;
516*0a6a1f1dSLionel Sambuc  }
517*0a6a1f1dSLionel Sambuc
518f4a2713aSLionel Sambuc  # Options with possible arguments that should pass through to compiler.
519f4a2713aSLionel Sambuc  if (defined $CompileOptionMap{$ArgKey}) {
520f4a2713aSLionel Sambuc    my $Cnt = $CompileOptionMap{$ArgKey};
521f4a2713aSLionel Sambuc    push @CompileOpts,$Arg;
522f4a2713aSLionel Sambuc    while ($Cnt > 0) { ++$i; --$Cnt; push @CompileOpts, $ARGV[$i]; }
523f4a2713aSLionel Sambuc    next;
524f4a2713aSLionel Sambuc  }
525f4a2713aSLionel Sambuc  # Handle the case where there isn't a space after -iquote
526*0a6a1f1dSLionel Sambuc  if ($Arg =~ /^-iquote.*/) {
527f4a2713aSLionel Sambuc    push @CompileOpts,$Arg;
528f4a2713aSLionel Sambuc    next;
529f4a2713aSLionel Sambuc  }
530f4a2713aSLionel Sambuc
531f4a2713aSLionel Sambuc  # Options with possible arguments that should pass through to linker.
532f4a2713aSLionel Sambuc  if (defined $LinkerOptionMap{$ArgKey}) {
533f4a2713aSLionel Sambuc    my $Cnt = $LinkerOptionMap{$ArgKey};
534f4a2713aSLionel Sambuc    push @LinkOpts,$Arg;
535f4a2713aSLionel Sambuc    while ($Cnt > 0) { ++$i; --$Cnt; push @LinkOpts, $ARGV[$i]; }
536f4a2713aSLionel Sambuc    next;
537f4a2713aSLionel Sambuc  }
538f4a2713aSLionel Sambuc
539f4a2713aSLionel Sambuc  # Options with possible arguments that should pass through to both compiler
540f4a2713aSLionel Sambuc  # and the linker.
541f4a2713aSLionel Sambuc  if (defined $CompilerLinkerOptionMap{$ArgKey}) {
542f4a2713aSLionel Sambuc    my $Cnt = $CompilerLinkerOptionMap{$ArgKey};
543f4a2713aSLionel Sambuc
544f4a2713aSLionel Sambuc    # Check if this is an option that should have a unique value, and if so
545f4a2713aSLionel Sambuc    # determine if the value was checked before.
546f4a2713aSLionel Sambuc    if ($UniqueOptions{$Arg}) {
547f4a2713aSLionel Sambuc      if (defined $Uniqued{$Arg}) {
548f4a2713aSLionel Sambuc        $i += $Cnt;
549f4a2713aSLionel Sambuc        next;
550f4a2713aSLionel Sambuc      }
551f4a2713aSLionel Sambuc      $Uniqued{$Arg} = 1;
552f4a2713aSLionel Sambuc    }
553f4a2713aSLionel Sambuc
554f4a2713aSLionel Sambuc    push @CompileOpts,$Arg;
555f4a2713aSLionel Sambuc    push @LinkOpts,$Arg;
556f4a2713aSLionel Sambuc
557f4a2713aSLionel Sambuc    while ($Cnt > 0) {
558f4a2713aSLionel Sambuc      ++$i; --$Cnt;
559f4a2713aSLionel Sambuc      push @CompileOpts, $ARGV[$i];
560f4a2713aSLionel Sambuc      push @LinkOpts, $ARGV[$i];
561f4a2713aSLionel Sambuc    }
562f4a2713aSLionel Sambuc    next;
563f4a2713aSLionel Sambuc  }
564f4a2713aSLionel Sambuc
565f4a2713aSLionel Sambuc  # Ignored options.
566f4a2713aSLionel Sambuc  if (defined $IgnoredOptionMap{$ArgKey}) {
567f4a2713aSLionel Sambuc    my $Cnt = $IgnoredOptionMap{$ArgKey};
568f4a2713aSLionel Sambuc    while ($Cnt > 0) {
569f4a2713aSLionel Sambuc      ++$i; --$Cnt;
570f4a2713aSLionel Sambuc    }
571f4a2713aSLionel Sambuc    next;
572f4a2713aSLionel Sambuc  }
573f4a2713aSLionel Sambuc
574f4a2713aSLionel Sambuc  # Compile mode flags.
575f4a2713aSLionel Sambuc  if ($Arg =~ /^-[D,I,U](.*)$/) {
576f4a2713aSLionel Sambuc    my $Tmp = $Arg;
577f4a2713aSLionel Sambuc    if ($1 eq '') {
578f4a2713aSLionel Sambuc      # FIXME: Check if we are going off the end.
579f4a2713aSLionel Sambuc      ++$i;
580f4a2713aSLionel Sambuc      $Tmp = $Arg . $ARGV[$i];
581f4a2713aSLionel Sambuc    }
582f4a2713aSLionel Sambuc    push @CompileOpts,$Tmp;
583f4a2713aSLionel Sambuc    next;
584f4a2713aSLionel Sambuc  }
585f4a2713aSLionel Sambuc
586*0a6a1f1dSLionel Sambuc  if ($Arg =~ /^-m.*/) {
587f4a2713aSLionel Sambuc    push @CompileOpts,$Arg;
588f4a2713aSLionel Sambuc    next;
589f4a2713aSLionel Sambuc  }
590f4a2713aSLionel Sambuc
591f4a2713aSLionel Sambuc  # Language.
592f4a2713aSLionel Sambuc  if ($Arg eq '-x') {
593f4a2713aSLionel Sambuc    $Lang = $ARGV[$i+1];
594f4a2713aSLionel Sambuc    ++$i; next;
595f4a2713aSLionel Sambuc  }
596f4a2713aSLionel Sambuc
597f4a2713aSLionel Sambuc  # Output file.
598f4a2713aSLionel Sambuc  if ($Arg eq '-o') {
599f4a2713aSLionel Sambuc    ++$i;
600f4a2713aSLionel Sambuc    $Output = $ARGV[$i];
601f4a2713aSLionel Sambuc    next;
602f4a2713aSLionel Sambuc  }
603f4a2713aSLionel Sambuc
604f4a2713aSLionel Sambuc  # Get the link mode.
605f4a2713aSLionel Sambuc  if ($Arg =~ /^-[l,L,O]/) {
606f4a2713aSLionel Sambuc    if ($Arg eq '-O') { push @LinkOpts,'-O1'; }
607f4a2713aSLionel Sambuc    elsif ($Arg eq '-Os') { push @LinkOpts,'-O2'; }
608f4a2713aSLionel Sambuc    else { push @LinkOpts,$Arg; }
609f4a2713aSLionel Sambuc
610f4a2713aSLionel Sambuc    # Must pass this along for the __OPTIMIZE__ macro
611f4a2713aSLionel Sambuc    if ($Arg =~ /^-O/) { push @CompileOpts,$Arg; }
612f4a2713aSLionel Sambuc    next;
613f4a2713aSLionel Sambuc  }
614f4a2713aSLionel Sambuc
615f4a2713aSLionel Sambuc  if ($Arg =~ /^-std=/) {
616f4a2713aSLionel Sambuc    push @CompileOpts,$Arg;
617f4a2713aSLionel Sambuc    next;
618f4a2713aSLionel Sambuc  }
619f4a2713aSLionel Sambuc
620f4a2713aSLionel Sambuc  # Get the compiler/link mode.
621f4a2713aSLionel Sambuc  if ($Arg =~ /^-F(.+)$/) {
622f4a2713aSLionel Sambuc    my $Tmp = $Arg;
623f4a2713aSLionel Sambuc    if ($1 eq '') {
624f4a2713aSLionel Sambuc      # FIXME: Check if we are going off the end.
625f4a2713aSLionel Sambuc      ++$i;
626f4a2713aSLionel Sambuc      $Tmp = $Arg . $ARGV[$i];
627f4a2713aSLionel Sambuc    }
628f4a2713aSLionel Sambuc    push @CompileOpts,$Tmp;
629f4a2713aSLionel Sambuc    push @LinkOpts,$Tmp;
630f4a2713aSLionel Sambuc    next;
631f4a2713aSLionel Sambuc  }
632f4a2713aSLionel Sambuc
633f4a2713aSLionel Sambuc  # Input files.
634f4a2713aSLionel Sambuc  if ($Arg eq '-filelist') {
635f4a2713aSLionel Sambuc    # FIXME: Make sure we aren't walking off the end.
636f4a2713aSLionel Sambuc    open(IN, $ARGV[$i+1]);
637f4a2713aSLionel Sambuc    while (<IN>) { s/\015?\012//; push @Files,$_; }
638f4a2713aSLionel Sambuc    close(IN);
639f4a2713aSLionel Sambuc    ++$i;
640f4a2713aSLionel Sambuc    next;
641f4a2713aSLionel Sambuc  }
642f4a2713aSLionel Sambuc
643f4a2713aSLionel Sambuc  if ($Arg =~ /^-f/) {
644f4a2713aSLionel Sambuc    push @CompileOpts,$Arg;
645f4a2713aSLionel Sambuc    push @LinkOpts,$Arg;
646f4a2713aSLionel Sambuc    next;
647f4a2713aSLionel Sambuc  }
648f4a2713aSLionel Sambuc
649f4a2713aSLionel Sambuc  # Handle -Wno-.  We don't care about extra warnings, but
650f4a2713aSLionel Sambuc  # we should suppress ones that we don't want to see.
651f4a2713aSLionel Sambuc  if ($Arg =~ /^-Wno-/) {
652f4a2713aSLionel Sambuc    push @CompileOpts, $Arg;
653f4a2713aSLionel Sambuc    next;
654f4a2713aSLionel Sambuc  }
655f4a2713aSLionel Sambuc
656f4a2713aSLionel Sambuc  if (!($Arg =~ /^-/)) {
657f4a2713aSLionel Sambuc    push @Files, $Arg;
658f4a2713aSLionel Sambuc    next;
659f4a2713aSLionel Sambuc  }
660f4a2713aSLionel Sambuc}
661f4a2713aSLionel Sambuc
662*0a6a1f1dSLionel Sambuc# If we are on OSX and have an installation where the
663*0a6a1f1dSLionel Sambuc# default SDK is inferred by xcrun use xcrun to infer
664*0a6a1f1dSLionel Sambuc# the SDK.
665*0a6a1f1dSLionel Sambucif (not $HasSDK and $UseXCRUN) {
666*0a6a1f1dSLionel Sambuc  my $sdk = `/usr/bin/xcrun --show-sdk-path -sdk macosx`;
667*0a6a1f1dSLionel Sambuc  chomp $sdk;
668*0a6a1f1dSLionel Sambuc  push @CompileOpts, "-isysroot", $sdk;
669*0a6a1f1dSLionel Sambuc}
670*0a6a1f1dSLionel Sambuc
671f4a2713aSLionel Sambucif ($Action eq 'compile' or $Action eq 'link') {
672f4a2713aSLionel Sambuc  my @Archs = keys %ArchsSeen;
673f4a2713aSLionel Sambuc  # Skip the file if we don't support the architectures specified.
674f4a2713aSLionel Sambuc  exit 0 if ($HadArch && scalar(@Archs) == 0);
675f4a2713aSLionel Sambuc
676f4a2713aSLionel Sambuc  foreach my $file (@Files) {
677f4a2713aSLionel Sambuc    # Determine the language for the file.
678f4a2713aSLionel Sambuc    my $FileLang = $Lang;
679f4a2713aSLionel Sambuc
680f4a2713aSLionel Sambuc    if (!defined($FileLang)) {
681f4a2713aSLionel Sambuc      # Infer the language from the extension.
682f4a2713aSLionel Sambuc      if ($file =~ /[.]([^.]+)$/) {
683f4a2713aSLionel Sambuc        $FileLang = $LangMap{$1};
684f4a2713aSLionel Sambuc      }
685f4a2713aSLionel Sambuc    }
686f4a2713aSLionel Sambuc
687f4a2713aSLionel Sambuc    # FileLang still not defined?  Skip the file.
688f4a2713aSLionel Sambuc    next if (!defined $FileLang);
689f4a2713aSLionel Sambuc
690f4a2713aSLionel Sambuc    # Language not accepted?
691f4a2713aSLionel Sambuc    next if (!defined $LangsAccepted{$FileLang});
692f4a2713aSLionel Sambuc
693f4a2713aSLionel Sambuc    my @CmdArgs;
694f4a2713aSLionel Sambuc    my @AnalyzeArgs;
695f4a2713aSLionel Sambuc
696f4a2713aSLionel Sambuc    if ($FileLang ne 'unknown') {
697f4a2713aSLionel Sambuc      push @CmdArgs, '-x', $FileLang;
698f4a2713aSLionel Sambuc    }
699f4a2713aSLionel Sambuc
700f4a2713aSLionel Sambuc    if (defined $StoreModel) {
701f4a2713aSLionel Sambuc      push @AnalyzeArgs, "-analyzer-store=$StoreModel";
702f4a2713aSLionel Sambuc    }
703f4a2713aSLionel Sambuc
704f4a2713aSLionel Sambuc    if (defined $ConstraintsModel) {
705f4a2713aSLionel Sambuc      push @AnalyzeArgs, "-analyzer-constraints=$ConstraintsModel";
706f4a2713aSLionel Sambuc    }
707f4a2713aSLionel Sambuc
708f4a2713aSLionel Sambuc    if (defined $InternalStats) {
709f4a2713aSLionel Sambuc      push @AnalyzeArgs, "-analyzer-stats";
710f4a2713aSLionel Sambuc    }
711f4a2713aSLionel Sambuc
712f4a2713aSLionel Sambuc    if (defined $Analyses) {
713f4a2713aSLionel Sambuc      push @AnalyzeArgs, split '\s+', $Analyses;
714f4a2713aSLionel Sambuc    }
715f4a2713aSLionel Sambuc
716f4a2713aSLionel Sambuc    if (defined $Plugins) {
717f4a2713aSLionel Sambuc      push @AnalyzeArgs, split '\s+', $Plugins;
718f4a2713aSLionel Sambuc    }
719f4a2713aSLionel Sambuc
720f4a2713aSLionel Sambuc    if (defined $OutputFormat) {
721f4a2713aSLionel Sambuc      push @AnalyzeArgs, "-analyzer-output=" . $OutputFormat;
722f4a2713aSLionel Sambuc      if ($OutputFormat =~ /plist/) {
723f4a2713aSLionel Sambuc        # Change "Output" to be a file.
724f4a2713aSLionel Sambuc        my ($h, $f) = tempfile("report-XXXXXX", SUFFIX => ".plist",
725f4a2713aSLionel Sambuc                               DIR => $HtmlDir);
726f4a2713aSLionel Sambuc        $ResultFile = $f;
727*0a6a1f1dSLionel Sambuc        # If the HtmlDir is not set, we should clean up the plist files.
728f4a2713aSLionel Sambuc        if (!defined $HtmlDir || -z $HtmlDir) {
729f4a2713aSLionel Sambuc          $CleanupFile = $f;
730f4a2713aSLionel Sambuc        }
731f4a2713aSLionel Sambuc      }
732f4a2713aSLionel Sambuc    }
733*0a6a1f1dSLionel Sambuc    if (defined $ConfigOptions) {
734*0a6a1f1dSLionel Sambuc      push @AnalyzeArgs, split '\s+', $ConfigOptions;
735*0a6a1f1dSLionel Sambuc    }
736f4a2713aSLionel Sambuc
737f4a2713aSLionel Sambuc    push @CmdArgs, @CompileOpts;
738f4a2713aSLionel Sambuc    push @CmdArgs, $file;
739f4a2713aSLionel Sambuc
740f4a2713aSLionel Sambuc    if (scalar @Archs) {
741f4a2713aSLionel Sambuc      foreach my $arch (@Archs) {
742f4a2713aSLionel Sambuc        my @NewArgs;
743f4a2713aSLionel Sambuc        push @NewArgs, '-arch', $arch;
744f4a2713aSLionel Sambuc        push @NewArgs, @CmdArgs;
745f4a2713aSLionel Sambuc        Analyze($Clang, \@NewArgs, \@AnalyzeArgs, $FileLang, $Output,
746f4a2713aSLionel Sambuc                $Verbose, $HtmlDir, $file);
747f4a2713aSLionel Sambuc      }
748f4a2713aSLionel Sambuc    }
749f4a2713aSLionel Sambuc    else {
750f4a2713aSLionel Sambuc      Analyze($Clang, \@CmdArgs, \@AnalyzeArgs, $FileLang, $Output,
751f4a2713aSLionel Sambuc              $Verbose, $HtmlDir, $file);
752f4a2713aSLionel Sambuc    }
753f4a2713aSLionel Sambuc  }
754f4a2713aSLionel Sambuc}
755f4a2713aSLionel Sambuc
756f4a2713aSLionel Sambucexit($Status >> 8);
757