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