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 $DefaultCCompiler;
     31 my $DefaultCXXCompiler;
     32 my $IsCXX;
     33 
     34 if (`uname -a` =~ m/Darwin/) {
     35   $DefaultCCompiler = 'clang';
     36   $DefaultCXXCompiler = 'clang++';
     37 } else {
     38   $DefaultCCompiler = 'gcc';
     39   $DefaultCXXCompiler = 'g++';
     40 }
     41 
     42 if ($FindBin::Script =~ /c\+\+-analyzer/) {
     43   $Compiler = $ENV{'CCC_CXX'};
     44   if (!defined $Compiler || ! -x $Compiler) { $Compiler = $DefaultCXXCompiler; }
     45 
     46   $Clang = $ENV{'CLANG_CXX'};
     47   if (!defined $Clang || ! -x $Clang) { $Clang = 'clang++'; }
     48 
     49   $IsCXX = 1
     50 }
     51 else {
     52   $Compiler = $ENV{'CCC_CC'};
     53   if (!defined $Compiler || ! -x $Compiler) { $Compiler = $DefaultCCompiler; }
     54 
     55   $Clang = $ENV{'CLANG'};
     56   if (!defined $Clang || ! -x $Clang) { $Clang = 'clang'; }
     57 
     58   $IsCXX = 0
     59 }
     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     unlink($ResultFile);
     75   }
     76   if (defined $CleanupFile) {
     77     unlink($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, @$Args, "-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 "@$Args\n";
    127   close OUT;
    128   `uname -a >> $PPFile.info.txt 2>&1`;
    129   `$Compiler -v >> $PPFile.info.txt 2>&1`;
    130   rename($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, @$Args;
    149   }
    150   close(TO_PARENT);
    151   my $line;
    152   while (<FROM_CHILD>) {
    153     next if (!/\s"?-cc1"?\s/);
    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 leading and trailing whitespace characters.
    162   $line =~ s/^\s+|\s+$//g;
    163   my @items = quotewords('\s+', 0, $line);
    164   my $cmd = shift @items;
    165   die "cannot find 'clang' in 'clang' command\n" if (!($cmd =~ /clang/));
    166   return \@items;
    167 }
    168 
    169 sub Analyze {
    170   my ($Clang, $OriginalArgs, $AnalyzeArgs, $Lang, $Output, $Verbose, $HtmlDir,
    171       $file) = @_;
    172 
    173   my @Args = @$OriginalArgs;
    174   my $Cmd;
    175   my @CmdArgs;
    176   my @CmdArgsSansAnalyses;
    177 
    178   if ($Lang =~ /header/) {
    179     exit 0 if (!defined ($Output));
    180     $Cmd = 'cp';
    181     push @CmdArgs, $file;
    182     # Remove the PCH extension.
    183     $Output =~ s/[.]gch$//;
    184     push @CmdArgs, $Output;
    185     @CmdArgsSansAnalyses = @CmdArgs;
    186   }
    187   else {
    188     $Cmd = $Clang;
    189 
    190     # Create arguments for doing regular parsing.
    191     my $SyntaxArgs = GetCCArgs("-fsyntax-only", \@Args);
    192     @CmdArgsSansAnalyses = @$SyntaxArgs;
    193 
    194     # Create arguments for doing static analysis.
    195     if (defined $ResultFile) {
    196       push @Args, '-o', $ResultFile;
    197     }
    198     elsif (defined $HtmlDir) {
    199       push @Args, '-o', $HtmlDir;
    200     }
    201     if ($Verbose) {
    202       push @Args, "-Xclang", "-analyzer-display-progress";
    203     }
    204 
    205     foreach my $arg (@$AnalyzeArgs) {
    206       push @Args, "-Xclang", $arg;
    207     }
    208 
    209     # Display Ubiviz graph?
    210     if (defined $ENV{'CCC_UBI'}) {
    211       push @Args, "-Xclang", "-analyzer-viz-egraph-ubigraph";
    212     }
    213 
    214     my $AnalysisArgs = GetCCArgs("--analyze", \@Args);
    215     @CmdArgs = @$AnalysisArgs;
    216   }
    217 
    218   my @PrintArgs;
    219   my $dir;
    220 
    221   if ($Verbose) {
    222     $dir = getcwd();
    223     print STDERR "\n[LOCATION]: $dir\n";
    224     push @PrintArgs,"'$Cmd'";
    225     foreach my $arg (@CmdArgs) {
    226         push @PrintArgs,"\'$arg\'";
    227     }
    228   }
    229   if ($Verbose == 1) {
    230     # We MUST print to stderr.  Some clients use the stdout output of
    231     # gcc for various purposes.
    232     print STDERR join(' ', @PrintArgs);
    233     print STDERR "\n";
    234   }
    235   elsif ($Verbose == 2) {
    236     print STDERR "#SHELL (cd '$dir' && @PrintArgs)\n";
    237   }
    238 
    239   # Capture the STDERR of clang and send it to a temporary file.
    240   # Capture the STDOUT of clang and reroute it to ccc-analyzer's STDERR.
    241   # We save the output file in the 'crashes' directory if clang encounters
    242   # any problems with the file.
    243   pipe (FROM_CHILD, TO_PARENT);
    244   my $pid = fork();
    245   if ($pid == 0) {
    246     close FROM_CHILD;
    247     open(STDOUT,">&", \*TO_PARENT);
    248     open(STDERR,">&", \*TO_PARENT);
    249     exec $Cmd, @CmdArgs;
    250   }
    251 
    252   close TO_PARENT;
    253   my ($ofh, $ofile) = tempfile("clang_output_XXXXXX", DIR => $HtmlDir);
    254 
    255   while (<FROM_CHILD>) {
    256     print $ofh $_;
    257     print STDERR $_;
    258   }
    259   close $ofh;
    260 
    261   waitpid($pid,0);
    262   close(FROM_CHILD);
    263   my $Result = $?;
    264 
    265   # Did the command die because of a signal?
    266   if ($ReportFailures) {
    267     if ($Result & 127 and $Cmd eq $Clang and defined $HtmlDir) {
    268       ProcessClangFailure($Clang, $Lang, $file, \@CmdArgsSansAnalyses,
    269                           $HtmlDir, "Crash", $ofile);
    270     }
    271     elsif ($Result) {
    272       if ($IncludeParserRejects && !($file =~/conftest/)) {
    273         ProcessClangFailure($Clang, $Lang, $file, \@CmdArgsSansAnalyses,
    274                             $HtmlDir, $ParserRejects, $ofile);
    275       } else {
    276         ProcessClangFailure($Clang, $Lang, $file, \@CmdArgsSansAnalyses,
    277                             $HtmlDir, $OtherError, $ofile);
    278       }
    279     }
    280     else {
    281       # Check if there were any unhandled attributes.
    282       if (open(CHILD, $ofile)) {
    283         my %attributes_not_handled;
    284 
    285         # Don't flag warnings about the following attributes that we
    286         # know are currently not supported by Clang.
    287         $attributes_not_handled{"cdecl"} = 1;
    288 
    289         my $ppfile;
    290         while (<CHILD>) {
    291           next if (! /warning: '([^\']+)' attribute ignored/);
    292 
    293           # Have we already spotted this unhandled attribute?
    294           next if (defined $attributes_not_handled{$1});
    295           $attributes_not_handled{$1} = 1;
    296 
    297           # Get the name of the attribute file.
    298           my $dir = "$HtmlDir/failures";
    299           my $afile = "$dir/attribute_ignored_$1.txt";
    300 
    301           # Only create another preprocessed file if the attribute file
    302           # doesn't exist yet.
    303           next if (-e $afile);
    304 
    305           # Add this file to the list of files that contained this attribute.
    306           # Generate a preprocessed file if we haven't already.
    307           if (!(defined $ppfile)) {
    308             $ppfile = ProcessClangFailure($Clang, $Lang, $file,
    309                                           \@CmdArgsSansAnalyses,
    310                                           $HtmlDir, $AttributeIgnored, $ofile);
    311           }
    312 
    313           mkpath $dir;
    314           open(AFILE, ">$afile");
    315           print AFILE "$ppfile\n";
    316           close(AFILE);
    317         }
    318         close CHILD;
    319       }
    320     }
    321   }
    322 
    323   unlink($ofile);
    324 }
    325 
    326 ##----------------------------------------------------------------------------##
    327 #  Lookup tables.
    328 ##----------------------------------------------------------------------------##
    329 
    330 my %CompileOptionMap = (
    331   '-nostdinc' => 0,
    332   '-include' => 1,
    333   '-idirafter' => 1,
    334   '-imacros' => 1,
    335   '-iprefix' => 1,
    336   '-iquote' => 1,
    337   '-isystem' => 1,
    338   '-iwithprefix' => 1,
    339   '-iwithprefixbefore' => 1
    340 );
    341 
    342 my %LinkerOptionMap = (
    343   '-framework' => 1,
    344   '-fobjc-link-runtime' => 0
    345 );
    346 
    347 my %CompilerLinkerOptionMap = (
    348   '-Wwrite-strings' => 0,
    349   '-ftrapv-handler' => 1, # specifically call out separated -f flag
    350   '-mios-simulator-version-min' => 0, # This really has 1 argument, but always has '='
    351   '-isysroot' => 1,
    352   '-arch' => 1,
    353   '-m32' => 0,
    354   '-m64' => 0,
    355   '-stdlib' => 0, # This is really a 1 argument, but always has '='
    356   '--sysroot' => 1,
    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'   => $IsCXX ? 'c++' : 'c',
    386   'cp'  => 'c++',
    387   'cpp' => 'c++',
    388   'cxx' => 'c++',
    389   'txx' => 'c++',
    390   'cc'  => 'c++',
    391   'C'   => 'c++',
    392   'ii'  => 'c++-cpp-output',
    393   'i'   => $IsCXX ? 'c++-cpp-output' : 'c-cpp-output',
    394   'm'   => 'objective-c',
    395   'mi'  => 'objective-c-cpp-output',
    396   'mm'  => 'objective-c++',
    397   'mii' => 'objective-c++-cpp-output',
    398 );
    399 
    400 my %UniqueOptions = (
    401   '-isysroot' => 0
    402 );
    403 
    404 ##----------------------------------------------------------------------------##
    405 # Languages accepted.
    406 ##----------------------------------------------------------------------------##
    407 
    408 my %LangsAccepted = (
    409   "objective-c" => 1,
    410   "c" => 1,
    411   "c++" => 1,
    412   "objective-c++" => 1,
    413   "c-cpp-output" => 1,
    414   "objective-c-cpp-output" => 1,
    415   "c++-cpp-output" => 1
    416 );
    417 
    418 ##----------------------------------------------------------------------------##
    419 #  Main Logic.
    420 ##----------------------------------------------------------------------------##
    421 
    422 my $Action = 'link';
    423 my @CompileOpts;
    424 my @LinkOpts;
    425 my @Files;
    426 my $Lang;
    427 my $Output;
    428 my %Uniqued;
    429 
    430 # Forward arguments to gcc.
    431 my $Status = system($Compiler,@ARGV);
    432 if (defined $ENV{'CCC_ANALYZER_LOG'}) {
    433   print STDERR "$Compiler @ARGV\n";
    434 }
    435 if ($Status) { exit($Status >> 8); }
    436 
    437 # Get the analysis options.
    438 my $Analyses = $ENV{'CCC_ANALYZER_ANALYSIS'};
    439 
    440 # Get the plugins to load.
    441 my $Plugins = $ENV{'CCC_ANALYZER_PLUGINS'};
    442 
    443 # Get the store model.
    444 my $StoreModel = $ENV{'CCC_ANALYZER_STORE_MODEL'};
    445 
    446 # Get the constraints engine.
    447 my $ConstraintsModel = $ENV{'CCC_ANALYZER_CONSTRAINTS_MODEL'};
    448 
    449 #Get the internal stats setting.
    450 my $InternalStats = $ENV{'CCC_ANALYZER_INTERNAL_STATS'};
    451 
    452 # Get the output format.
    453 my $OutputFormat = $ENV{'CCC_ANALYZER_OUTPUT_FORMAT'};
    454 if (!defined $OutputFormat) { $OutputFormat = "html"; }
    455 
    456 # Get the config options.
    457 my $ConfigOptions = $ENV{'CCC_ANALYZER_CONFIG'};
    458 
    459 # Determine the level of verbosity.
    460 my $Verbose = 0;
    461 if (defined $ENV{'CCC_ANALYZER_VERBOSE'}) { $Verbose = 1; }
    462 if (defined $ENV{'CCC_ANALYZER_LOG'}) { $Verbose = 2; }
    463 
    464 # Get the HTML output directory.
    465 my $HtmlDir = $ENV{'CCC_ANALYZER_HTML'};
    466 
    467 my %DisabledArchs = ('ppc' => 1, 'ppc64' => 1);
    468 my %ArchsSeen;
    469 my $HadArch = 0;
    470 
    471 # Process the arguments.
    472 foreach (my $i = 0; $i < scalar(@ARGV); ++$i) {
    473   my $Arg = $ARGV[$i];
    474   my ($ArgKey) = split /=/,$Arg,2;
    475 
    476   # Modes ccc-analyzer supports
    477   if ($Arg =~ /^-(E|MM?)$/) { $Action = 'preprocess'; }
    478   elsif ($Arg eq '-c') { $Action = 'compile'; }
    479   elsif ($Arg =~ /^-print-prog-name/) { exit 0; }
    480 
    481   # Specially handle duplicate cases of -arch
    482   if ($Arg eq "-arch") {
    483     my $arch = $ARGV[$i+1];
    484     # We don't want to process 'ppc' because of Clang's lack of support
    485     # for Altivec (also some #defines won't likely be defined correctly, etc.)
    486     if (!(defined $DisabledArchs{$arch})) { $ArchsSeen{$arch} = 1; }
    487     $HadArch = 1;
    488     ++$i;
    489     next;
    490   }
    491 
    492   # Options with possible arguments that should pass through to compiler.
    493   if (defined $CompileOptionMap{$ArgKey}) {
    494     my $Cnt = $CompileOptionMap{$ArgKey};
    495     push @CompileOpts,$Arg;
    496     while ($Cnt > 0) { ++$i; --$Cnt; push @CompileOpts, $ARGV[$i]; }
    497     next;
    498   }
    499   # Handle the case where there isn't a space after -iquote
    500   if ($Arg =~ /^-iquote.*/) {
    501     push @CompileOpts,$Arg;
    502     next;
    503   }
    504 
    505   # Options with possible arguments that should pass through to linker.
    506   if (defined $LinkerOptionMap{$ArgKey}) {
    507     my $Cnt = $LinkerOptionMap{$ArgKey};
    508     push @LinkOpts,$Arg;
    509     while ($Cnt > 0) { ++$i; --$Cnt; push @LinkOpts, $ARGV[$i]; }
    510     next;
    511   }
    512 
    513   # Options with possible arguments that should pass through to both compiler
    514   # and the linker.
    515   if (defined $CompilerLinkerOptionMap{$ArgKey}) {
    516     my $Cnt = $CompilerLinkerOptionMap{$ArgKey};
    517 
    518     # Check if this is an option that should have a unique value, and if so
    519     # determine if the value was checked before.
    520     if ($UniqueOptions{$Arg}) {
    521       if (defined $Uniqued{$Arg}) {
    522         $i += $Cnt;
    523         next;
    524       }
    525       $Uniqued{$Arg} = 1;
    526     }
    527 
    528     push @CompileOpts,$Arg;
    529     push @LinkOpts,$Arg;
    530 
    531     while ($Cnt > 0) {
    532       ++$i; --$Cnt;
    533       push @CompileOpts, $ARGV[$i];
    534       push @LinkOpts, $ARGV[$i];
    535     }
    536     next;
    537   }
    538 
    539   # Ignored options.
    540   if (defined $IgnoredOptionMap{$ArgKey}) {
    541     my $Cnt = $IgnoredOptionMap{$ArgKey};
    542     while ($Cnt > 0) {
    543       ++$i; --$Cnt;
    544     }
    545     next;
    546   }
    547 
    548   # Compile mode flags.
    549   if ($Arg =~ /^-[D,I,U](.*)$/) {
    550     my $Tmp = $Arg;
    551     if ($1 eq '') {
    552       # FIXME: Check if we are going off the end.
    553       ++$i;
    554       $Tmp = $Arg . $ARGV[$i];
    555     }
    556     push @CompileOpts,$Tmp;
    557     next;
    558   }
    559 
    560   if ($Arg =~ /^-m.*/) {
    561     push @CompileOpts,$Arg;
    562     next;
    563   }
    564 
    565   # Language.
    566   if ($Arg eq '-x') {
    567     $Lang = $ARGV[$i+1];
    568     ++$i; next;
    569   }
    570 
    571   # Output file.
    572   if ($Arg eq '-o') {
    573     ++$i;
    574     $Output = $ARGV[$i];
    575     next;
    576   }
    577 
    578   # Get the link mode.
    579   if ($Arg =~ /^-[l,L,O]/) {
    580     if ($Arg eq '-O') { push @LinkOpts,'-O1'; }
    581     elsif ($Arg eq '-Os') { push @LinkOpts,'-O2'; }
    582     else { push @LinkOpts,$Arg; }
    583 
    584     # Must pass this along for the __OPTIMIZE__ macro
    585     if ($Arg =~ /^-O/) { push @CompileOpts,$Arg; }
    586     next;
    587   }
    588 
    589   if ($Arg =~ /^-std=/) {
    590     push @CompileOpts,$Arg;
    591     next;
    592   }
    593 
    594   # Get the compiler/link mode.
    595   if ($Arg =~ /^-F(.+)$/) {
    596     my $Tmp = $Arg;
    597     if ($1 eq '') {
    598       # FIXME: Check if we are going off the end.
    599       ++$i;
    600       $Tmp = $Arg . $ARGV[$i];
    601     }
    602     push @CompileOpts,$Tmp;
    603     push @LinkOpts,$Tmp;
    604     next;
    605   }
    606 
    607   # Input files.
    608   if ($Arg eq '-filelist') {
    609     # FIXME: Make sure we aren't walking off the end.
    610     open(IN, $ARGV[$i+1]);
    611     while (<IN>) { s/\015?\012//; push @Files,$_; }
    612     close(IN);
    613     ++$i;
    614     next;
    615   }
    616 
    617   if ($Arg =~ /^-f/) {
    618     push @CompileOpts,$Arg;
    619     push @LinkOpts,$Arg;
    620     next;
    621   }
    622 
    623   # Handle -Wno-.  We don't care about extra warnings, but
    624   # we should suppress ones that we don't want to see.
    625   if ($Arg =~ /^-Wno-/) {
    626     push @CompileOpts, $Arg;
    627     next;
    628   }
    629 
    630   if (!($Arg =~ /^-/)) {
    631     push @Files, $Arg;
    632     next;
    633   }
    634 }
    635 
    636 if ($Action eq 'compile' or $Action eq 'link') {
    637   my @Archs = keys %ArchsSeen;
    638   # Skip the file if we don't support the architectures specified.
    639   exit 0 if ($HadArch && scalar(@Archs) == 0);
    640 
    641   foreach my $file (@Files) {
    642     # Determine the language for the file.
    643     my $FileLang = $Lang;
    644 
    645     if (!defined($FileLang)) {
    646       # Infer the language from the extension.
    647       if ($file =~ /[.]([^.]+)$/) {
    648         $FileLang = $LangMap{$1};
    649       }
    650     }
    651 
    652     # FileLang still not defined?  Skip the file.
    653     next if (!defined $FileLang);
    654 
    655     # Language not accepted?
    656     next if (!defined $LangsAccepted{$FileLang});
    657 
    658     my @CmdArgs;
    659     my @AnalyzeArgs;
    660 
    661     if ($FileLang ne 'unknown') {
    662       push @CmdArgs, '-x', $FileLang;
    663     }
    664 
    665     if (defined $StoreModel) {
    666       push @AnalyzeArgs, "-analyzer-store=$StoreModel";
    667     }
    668 
    669     if (defined $ConstraintsModel) {
    670       push @AnalyzeArgs, "-analyzer-constraints=$ConstraintsModel";
    671     }
    672 
    673     if (defined $InternalStats) {
    674       push @AnalyzeArgs, "-analyzer-stats";
    675     }
    676 
    677     if (defined $Analyses) {
    678       push @AnalyzeArgs, split '\s+', $Analyses;
    679     }
    680 
    681     if (defined $Plugins) {
    682       push @AnalyzeArgs, split '\s+', $Plugins;
    683     }
    684 
    685     if (defined $OutputFormat) {
    686       push @AnalyzeArgs, "-analyzer-output=" . $OutputFormat;
    687       if ($OutputFormat =~ /plist/) {
    688         # Change "Output" to be a file.
    689         my ($h, $f) = tempfile("report-XXXXXX", SUFFIX => ".plist",
    690                                DIR => $HtmlDir);
    691         $ResultFile = $f;
    692         # If the HtmlDir is not set, we should clean up the plist files.
    693         if (!defined $HtmlDir || -z $HtmlDir) {
    694           $CleanupFile = $f;
    695         }
    696       }
    697     }
    698     if (defined $ConfigOptions) {
    699       push @AnalyzeArgs, split '\s+', $ConfigOptions;
    700     }
    701 
    702     push @CmdArgs, @CompileOpts;
    703     push @CmdArgs, $file;
    704 
    705     if (scalar @Archs) {
    706       foreach my $arch (@Archs) {
    707         my @NewArgs;
    708         push @NewArgs, '-arch', $arch;
    709         push @NewArgs, @CmdArgs;
    710         Analyze($Clang, \@NewArgs, \@AnalyzeArgs, $FileLang, $Output,
    711                 $Verbose, $HtmlDir, $file);
    712       }
    713     }
    714     else {
    715       Analyze($Clang, \@CmdArgs, \@AnalyzeArgs, $FileLang, $Output,
    716               $Verbose, $HtmlDir, $file);
    717     }
    718   }
    719 }
    720 
    721 exit($Status >> 8);
    722