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