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 );
    390 
    391 my %IgnoredOptionMap = (
    392   '-MT' => 1,  # Ignore these preprocessor options.
    393   '-MF' => 1,
    394 
    395   '-fsyntax-only' => 0,
    396   '-save-temps' => 0,
    397   '-install_name' => 1,
    398   '-exported_symbols_list' => 1,
    399   '-current_version' => 1,
    400   '-compatibility_version' => 1,
    401   '-init' => 1,
    402   '-e' => 1,
    403   '-seg1addr' => 1,
    404   '-bundle_loader' => 1,
    405   '-multiply_defined' => 1,
    406   '-sectorder' => 3,
    407   '--param' => 1,
    408   '-u' => 1,
    409   '--serialize-diagnostics' => 1
    410 );
    411 
    412 my %LangMap = (
    413   'c'   => $IsCXX ? 'c++' : 'c',
    414   'cp'  => 'c++',
    415   'cpp' => 'c++',
    416   'cxx' => 'c++',
    417   'txx' => 'c++',
    418   'cc'  => 'c++',
    419   'C'   => 'c++',
    420   'ii'  => 'c++-cpp-output',
    421   'i'   => $IsCXX ? 'c++-cpp-output' : 'c-cpp-output',
    422   'm'   => 'objective-c',
    423   'mi'  => 'objective-c-cpp-output',
    424   'mm'  => 'objective-c++',
    425   'mii' => 'objective-c++-cpp-output',
    426 );
    427 
    428 my %UniqueOptions = (
    429   '-isysroot' => 0
    430 );
    431 
    432 ##----------------------------------------------------------------------------##
    433 # Languages accepted.
    434 ##----------------------------------------------------------------------------##
    435 
    436 my %LangsAccepted = (
    437   "objective-c" => 1,
    438   "c" => 1,
    439   "c++" => 1,
    440   "objective-c++" => 1,
    441   "c-cpp-output" => 1,
    442   "objective-c-cpp-output" => 1,
    443   "c++-cpp-output" => 1
    444 );
    445 
    446 ##----------------------------------------------------------------------------##
    447 #  Main Logic.
    448 ##----------------------------------------------------------------------------##
    449 
    450 my $Action = 'link';
    451 my @CompileOpts;
    452 my @LinkOpts;
    453 my @Files;
    454 my $Lang;
    455 my $Output;
    456 my %Uniqued;
    457 
    458 # Forward arguments to gcc.
    459 my $Status = system($Compiler,@ARGV);
    460 if (defined $ENV{'CCC_ANALYZER_LOG'}) {
    461   print STDERR "$Compiler @ARGV\n";
    462 }
    463 if ($Status) { exit($Status >> 8); }
    464 
    465 # Get the analysis options.
    466 my $Analyses = $ENV{'CCC_ANALYZER_ANALYSIS'};
    467 
    468 # Get the plugins to load.
    469 my $Plugins = $ENV{'CCC_ANALYZER_PLUGINS'};
    470 
    471 # Get the store model.
    472 my $StoreModel = $ENV{'CCC_ANALYZER_STORE_MODEL'};
    473 
    474 # Get the constraints engine.
    475 my $ConstraintsModel = $ENV{'CCC_ANALYZER_CONSTRAINTS_MODEL'};
    476 
    477 #Get the internal stats setting.
    478 my $InternalStats = $ENV{'CCC_ANALYZER_INTERNAL_STATS'};
    479 
    480 # Get the output format.
    481 my $OutputFormat = $ENV{'CCC_ANALYZER_OUTPUT_FORMAT'};
    482 if (!defined $OutputFormat) { $OutputFormat = "html"; }
    483 
    484 # Get the config options.
    485 my $ConfigOptions = $ENV{'CCC_ANALYZER_CONFIG'};
    486 
    487 # Determine the level of verbosity.
    488 my $Verbose = 0;
    489 if (defined $ENV{'CCC_ANALYZER_VERBOSE'}) { $Verbose = 1; }
    490 if (defined $ENV{'CCC_ANALYZER_LOG'}) { $Verbose = 2; }
    491 
    492 # Get the HTML output directory.
    493 my $HtmlDir = $ENV{'CCC_ANALYZER_HTML'};
    494 
    495 # Get force-analyze-debug-code option.
    496 my $ForceAnalyzeDebugCode = $ENV{'CCC_ANALYZER_FORCE_ANALYZE_DEBUG_CODE'};
    497 
    498 my %DisabledArchs = ('ppc' => 1, 'ppc64' => 1);
    499 my %ArchsSeen;
    500 my $HadArch = 0;
    501 my $HasSDK = 0;
    502 
    503 # Process the arguments.
    504 foreach (my $i = 0; $i < scalar(@ARGV); ++$i) {
    505   my $Arg = $ARGV[$i];
    506   my ($ArgKey) = split /=/,$Arg,2;
    507 
    508   # Be friendly to "" in the argument list.
    509   if (!defined($ArgKey)) {
    510     next;
    511   }
    512 
    513   # Modes ccc-analyzer supports
    514   if ($Arg =~ /^-(E|MM?)$/) { $Action = 'preprocess'; }
    515   elsif ($Arg eq '-c') { $Action = 'compile'; }
    516   elsif ($Arg =~ /^-print-prog-name/) { exit 0; }
    517 
    518   # Specially handle duplicate cases of -arch
    519   if ($Arg eq "-arch") {
    520     my $arch = $ARGV[$i+1];
    521     # We don't want to process 'ppc' because of Clang's lack of support
    522     # for Altivec (also some #defines won't likely be defined correctly, etc.)
    523     if (!(defined $DisabledArchs{$arch})) { $ArchsSeen{$arch} = 1; }
    524     $HadArch = 1;
    525     ++$i;
    526     next;
    527   }
    528 
    529   # On OSX/iOS, record if an SDK path was specified.  This
    530   # is innocuous for other platforms, so the check just happens.
    531   if ($Arg =~ /^-isysroot/) {
    532     $HasSDK = 1;
    533   }
    534 
    535   # Options with possible arguments that should pass through to compiler.
    536   if (defined $CompileOptionMap{$ArgKey}) {
    537     my $Cnt = $CompileOptionMap{$ArgKey};
    538     push @CompileOpts,$Arg;
    539     while ($Cnt > 0) { ++$i; --$Cnt; push @CompileOpts, $ARGV[$i]; }
    540     next;
    541   }
    542   # Handle the case where there isn't a space after -iquote
    543   if ($Arg =~ /^-iquote.*/) {
    544     push @CompileOpts,$Arg;
    545     next;
    546   }
    547 
    548   # Options with possible arguments that should pass through to linker.
    549   if (defined $LinkerOptionMap{$ArgKey}) {
    550     my $Cnt = $LinkerOptionMap{$ArgKey};
    551     push @LinkOpts,$Arg;
    552     while ($Cnt > 0) { ++$i; --$Cnt; push @LinkOpts, $ARGV[$i]; }
    553     next;
    554   }
    555 
    556   # Options with possible arguments that should pass through to both compiler
    557   # and the linker.
    558   if (defined $CompilerLinkerOptionMap{$ArgKey}) {
    559     my $Cnt = $CompilerLinkerOptionMap{$ArgKey};
    560 
    561     # Check if this is an option that should have a unique value, and if so
    562     # determine if the value was checked before.
    563     if ($UniqueOptions{$Arg}) {
    564       if (defined $Uniqued{$Arg}) {
    565         $i += $Cnt;
    566         next;
    567       }
    568       $Uniqued{$Arg} = 1;
    569     }
    570 
    571     push @CompileOpts,$Arg;
    572     push @LinkOpts,$Arg;
    573 
    574     while ($Cnt > 0) {
    575       ++$i; --$Cnt;
    576       push @CompileOpts, $ARGV[$i];
    577       push @LinkOpts, $ARGV[$i];
    578     }
    579     next;
    580   }
    581 
    582   # Ignored options.
    583   if (defined $IgnoredOptionMap{$ArgKey}) {
    584     my $Cnt = $IgnoredOptionMap{$ArgKey};
    585     while ($Cnt > 0) {
    586       ++$i; --$Cnt;
    587     }
    588     next;
    589   }
    590 
    591   # Compile mode flags.
    592   if ($Arg =~ /^-(?:[DIU]|isystem)(.*)$/) {
    593     my $Tmp = $Arg;
    594     if ($1 eq '') {
    595       # FIXME: Check if we are going off the end.
    596       ++$i;
    597       $Tmp = $Arg . $ARGV[$i];
    598     }
    599     push @CompileOpts,$Tmp;
    600     next;
    601   }
    602 
    603   if ($Arg =~ /^-m.*/) {
    604     push @CompileOpts,$Arg;
    605     next;
    606   }
    607 
    608   # Language.
    609   if ($Arg eq '-x') {
    610     $Lang = $ARGV[$i+1];
    611     ++$i; next;
    612   }
    613 
    614   # Output file.
    615   if ($Arg eq '-o') {
    616     ++$i;
    617     $Output = $ARGV[$i];
    618     next;
    619   }
    620 
    621   # Get the link mode.
    622   if ($Arg =~ /^-[l,L,O]/) {
    623     if ($Arg eq '-O') { push @LinkOpts,'-O1'; }
    624     elsif ($Arg eq '-Os') { push @LinkOpts,'-O2'; }
    625     else { push @LinkOpts,$Arg; }
    626 
    627     # Must pass this along for the __OPTIMIZE__ macro
    628     if ($Arg =~ /^-O/) { push @CompileOpts,$Arg; }
    629     next;
    630   }
    631 
    632   if ($Arg =~ /^-std=/) {
    633     push @CompileOpts,$Arg;
    634     next;
    635   }
    636 
    637   # Get the compiler/link mode.
    638   if ($Arg =~ /^-F(.+)$/) {
    639     my $Tmp = $Arg;
    640     if ($1 eq '') {
    641       # FIXME: Check if we are going off the end.
    642       ++$i;
    643       $Tmp = $Arg . $ARGV[$i];
    644     }
    645     push @CompileOpts,$Tmp;
    646     push @LinkOpts,$Tmp;
    647     next;
    648   }
    649 
    650   # Input files.
    651   if ($Arg eq '-filelist') {
    652     # FIXME: Make sure we aren't walking off the end.
    653     open(IN, $ARGV[$i+1]);
    654     while (<IN>) { s/\015?\012//; push @Files,$_; }
    655     close(IN);
    656     ++$i;
    657     next;
    658   }
    659 
    660   if ($Arg =~ /^-f/) {
    661     push @CompileOpts,$Arg;
    662     push @LinkOpts,$Arg;
    663     next;
    664   }
    665 
    666   # Handle -Wno-.  We don't care about extra warnings, but
    667   # we should suppress ones that we don't want to see.
    668   if ($Arg =~ /^-Wno-/) {
    669     push @CompileOpts, $Arg;
    670     next;
    671   }
    672 
    673   # Handle -Xclang some-arg. Add both arguments to the compiler options.
    674   if ($Arg =~ /^-Xclang$/) {
    675     # FIXME: Check if we are going off the end.
    676     ++$i;
    677     push @CompileOpts, $Arg;
    678     push @CompileOpts, $ARGV[$i];
    679     next;
    680   }
    681 
    682   if (!($Arg =~ /^-/)) {
    683     push @Files, $Arg;
    684     next;
    685   }
    686 }
    687 
    688 # Forcedly enable debugging if requested by user.
    689 if ($ForceAnalyzeDebugCode) {
    690   push @CompileOpts, '-UNDEBUG';
    691 }
    692 
    693 # If we are on OSX and have an installation where the
    694 # default SDK is inferred by xcrun use xcrun to infer
    695 # the SDK.
    696 if (not $HasSDK and $UseXCRUN) {
    697   my $sdk = `/usr/bin/xcrun --show-sdk-path -sdk macosx`;
    698   chomp $sdk;
    699   push @CompileOpts, "-isysroot", $sdk;
    700 }
    701 
    702 if ($Action eq 'compile' or $Action eq 'link') {
    703   my @Archs = keys %ArchsSeen;
    704   # Skip the file if we don't support the architectures specified.
    705   exit 0 if ($HadArch && scalar(@Archs) == 0);
    706 
    707   foreach my $file (@Files) {
    708     # Determine the language for the file.
    709     my $FileLang = $Lang;
    710 
    711     if (!defined($FileLang)) {
    712       # Infer the language from the extension.
    713       if ($file =~ /[.]([^.]+)$/) {
    714         $FileLang = $LangMap{$1};
    715       }
    716     }
    717 
    718     # FileLang still not defined?  Skip the file.
    719     next if (!defined $FileLang);
    720 
    721     # Language not accepted?
    722     next if (!defined $LangsAccepted{$FileLang});
    723 
    724     my @CmdArgs;
    725     my @AnalyzeArgs;
    726 
    727     if ($FileLang ne 'unknown') {
    728       push @CmdArgs, '-x', $FileLang;
    729     }
    730 
    731     if (defined $StoreModel) {
    732       push @AnalyzeArgs, "-analyzer-store=$StoreModel";
    733     }
    734 
    735     if (defined $ConstraintsModel) {
    736       push @AnalyzeArgs, "-analyzer-constraints=$ConstraintsModel";
    737     }
    738 
    739     if (defined $InternalStats) {
    740       push @AnalyzeArgs, "-analyzer-stats";
    741     }
    742 
    743     if (defined $Analyses) {
    744       push @AnalyzeArgs, split '\s+', $Analyses;
    745     }
    746 
    747     if (defined $Plugins) {
    748       push @AnalyzeArgs, split '\s+', $Plugins;
    749     }
    750 
    751     if (defined $OutputFormat) {
    752       push @AnalyzeArgs, "-analyzer-output=" . $OutputFormat;
    753       if ($OutputFormat =~ /plist/) {
    754         # Change "Output" to be a file.
    755         my ($h, $f) = tempfile("report-XXXXXX", SUFFIX => ".plist",
    756                                DIR => $HtmlDir);
    757         $ResultFile = $f;
    758         # If the HtmlDir is not set, we should clean up the plist files.
    759         if (!defined $HtmlDir || -z $HtmlDir) {
    760           $CleanupFile = $f;
    761         }
    762       }
    763     }
    764     if (defined $ConfigOptions) {
    765       push @AnalyzeArgs, split '\s+', $ConfigOptions;
    766     }
    767 
    768     push @CmdArgs, @CompileOpts;
    769     push @CmdArgs, $file;
    770 
    771     if (scalar @Archs) {
    772       foreach my $arch (@Archs) {
    773         my @NewArgs;
    774         push @NewArgs, '-arch', $arch;
    775         push @NewArgs, @CmdArgs;
    776         Analyze($Clang, \@NewArgs, \@AnalyzeArgs, $FileLang, $Output,
    777                 $Verbose, $HtmlDir, $file);
    778       }
    779     }
    780     else {
    781       Analyze($Clang, \@CmdArgs, \@AnalyzeArgs, $FileLang, $Output,
    782               $Verbose, $HtmlDir, $file);
    783     }
    784   }
    785 }
    786