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 my %DisabledArchs = ('ppc' => 1, 'ppc64' => 1);
    496 my %ArchsSeen;
    497 my $HadArch = 0;
    498 my $HasSDK = 0;
    499 
    500 # Process the arguments.
    501 foreach (my $i = 0; $i < scalar(@ARGV); ++$i) {
    502   my $Arg = $ARGV[$i];
    503   my ($ArgKey) = split /=/,$Arg,2;
    504 
    505   # Be friendly to "" in the argument list.
    506   if (!defined($ArgKey)) {
    507     next;
    508   }
    509 
    510   # Modes ccc-analyzer supports
    511   if ($Arg =~ /^-(E|MM?)$/) { $Action = 'preprocess'; }
    512   elsif ($Arg eq '-c') { $Action = 'compile'; }
    513   elsif ($Arg =~ /^-print-prog-name/) { exit 0; }
    514 
    515   # Specially handle duplicate cases of -arch
    516   if ($Arg eq "-arch") {
    517     my $arch = $ARGV[$i+1];
    518     # We don't want to process 'ppc' because of Clang's lack of support
    519     # for Altivec (also some #defines won't likely be defined correctly, etc.)
    520     if (!(defined $DisabledArchs{$arch})) { $ArchsSeen{$arch} = 1; }
    521     $HadArch = 1;
    522     ++$i;
    523     next;
    524   }
    525 
    526   # On OSX/iOS, record if an SDK path was specified.  This
    527   # is innocuous for other platforms, so the check just happens.
    528   if ($Arg =~ /^-isysroot/) {
    529     $HasSDK = 1;
    530   }
    531 
    532   # Options with possible arguments that should pass through to compiler.
    533   if (defined $CompileOptionMap{$ArgKey}) {
    534     my $Cnt = $CompileOptionMap{$ArgKey};
    535     push @CompileOpts,$Arg;
    536     while ($Cnt > 0) { ++$i; --$Cnt; push @CompileOpts, $ARGV[$i]; }
    537     next;
    538   }
    539   # Handle the case where there isn't a space after -iquote
    540   if ($Arg =~ /^-iquote.*/) {
    541     push @CompileOpts,$Arg;
    542     next;
    543   }
    544 
    545   # Options with possible arguments that should pass through to linker.
    546   if (defined $LinkerOptionMap{$ArgKey}) {
    547     my $Cnt = $LinkerOptionMap{$ArgKey};
    548     push @LinkOpts,$Arg;
    549     while ($Cnt > 0) { ++$i; --$Cnt; push @LinkOpts, $ARGV[$i]; }
    550     next;
    551   }
    552 
    553   # Options with possible arguments that should pass through to both compiler
    554   # and the linker.
    555   if (defined $CompilerLinkerOptionMap{$ArgKey}) {
    556     my $Cnt = $CompilerLinkerOptionMap{$ArgKey};
    557 
    558     # Check if this is an option that should have a unique value, and if so
    559     # determine if the value was checked before.
    560     if ($UniqueOptions{$Arg}) {
    561       if (defined $Uniqued{$Arg}) {
    562         $i += $Cnt;
    563         next;
    564       }
    565       $Uniqued{$Arg} = 1;
    566     }
    567 
    568     push @CompileOpts,$Arg;
    569     push @LinkOpts,$Arg;
    570 
    571     while ($Cnt > 0) {
    572       ++$i; --$Cnt;
    573       push @CompileOpts, $ARGV[$i];
    574       push @LinkOpts, $ARGV[$i];
    575     }
    576     next;
    577   }
    578 
    579   # Ignored options.
    580   if (defined $IgnoredOptionMap{$ArgKey}) {
    581     my $Cnt = $IgnoredOptionMap{$ArgKey};
    582     while ($Cnt > 0) {
    583       ++$i; --$Cnt;
    584     }
    585     next;
    586   }
    587 
    588   # Compile mode flags.
    589   if ($Arg =~ /^-(?:[DIU]|isystem)(.*)$/) {
    590     my $Tmp = $Arg;
    591     if ($1 eq '') {
    592       # FIXME: Check if we are going off the end.
    593       ++$i;
    594       $Tmp = $Arg . $ARGV[$i];
    595     }
    596     push @CompileOpts,$Tmp;
    597     next;
    598   }
    599 
    600   if ($Arg =~ /^-m.*/) {
    601     push @CompileOpts,$Arg;
    602     next;
    603   }
    604 
    605   # Language.
    606   if ($Arg eq '-x') {
    607     $Lang = $ARGV[$i+1];
    608     ++$i; next;
    609   }
    610 
    611   # Output file.
    612   if ($Arg eq '-o') {
    613     ++$i;
    614     $Output = $ARGV[$i];
    615     next;
    616   }
    617 
    618   # Get the link mode.
    619   if ($Arg =~ /^-[l,L,O]/) {
    620     if ($Arg eq '-O') { push @LinkOpts,'-O1'; }
    621     elsif ($Arg eq '-Os') { push @LinkOpts,'-O2'; }
    622     else { push @LinkOpts,$Arg; }
    623 
    624     # Must pass this along for the __OPTIMIZE__ macro
    625     if ($Arg =~ /^-O/) { push @CompileOpts,$Arg; }
    626     next;
    627   }
    628 
    629   if ($Arg =~ /^-std=/) {
    630     push @CompileOpts,$Arg;
    631     next;
    632   }
    633 
    634   # Get the compiler/link mode.
    635   if ($Arg =~ /^-F(.+)$/) {
    636     my $Tmp = $Arg;
    637     if ($1 eq '') {
    638       # FIXME: Check if we are going off the end.
    639       ++$i;
    640       $Tmp = $Arg . $ARGV[$i];
    641     }
    642     push @CompileOpts,$Tmp;
    643     push @LinkOpts,$Tmp;
    644     next;
    645   }
    646 
    647   # Input files.
    648   if ($Arg eq '-filelist') {
    649     # FIXME: Make sure we aren't walking off the end.
    650     open(IN, $ARGV[$i+1]);
    651     while (<IN>) { s/\015?\012//; push @Files,$_; }
    652     close(IN);
    653     ++$i;
    654     next;
    655   }
    656 
    657   if ($Arg =~ /^-f/) {
    658     push @CompileOpts,$Arg;
    659     push @LinkOpts,$Arg;
    660     next;
    661   }
    662 
    663   # Handle -Wno-.  We don't care about extra warnings, but
    664   # we should suppress ones that we don't want to see.
    665   if ($Arg =~ /^-Wno-/) {
    666     push @CompileOpts, $Arg;
    667     next;
    668   }
    669 
    670   # Handle -Xclang some-arg. Add both arguments to the compiler options.
    671   if ($Arg =~ /^-Xclang$/) {
    672     # FIXME: Check if we are going off the end.
    673     ++$i;
    674     push @CompileOpts, $Arg;
    675     push @CompileOpts, $ARGV[$i];
    676     next;
    677   }
    678 
    679   if (!($Arg =~ /^-/)) {
    680     push @Files, $Arg;
    681     next;
    682   }
    683 }
    684 
    685 # If we are on OSX and have an installation where the
    686 # default SDK is inferred by xcrun use xcrun to infer
    687 # the SDK.
    688 if (not $HasSDK and $UseXCRUN) {
    689   my $sdk = `/usr/bin/xcrun --show-sdk-path -sdk macosx`;
    690   chomp $sdk;
    691   push @CompileOpts, "-isysroot", $sdk;
    692 }
    693 
    694 if ($Action eq 'compile' or $Action eq 'link') {
    695   my @Archs = keys %ArchsSeen;
    696   # Skip the file if we don't support the architectures specified.
    697   exit 0 if ($HadArch && scalar(@Archs) == 0);
    698 
    699   foreach my $file (@Files) {
    700     # Determine the language for the file.
    701     my $FileLang = $Lang;
    702 
    703     if (!defined($FileLang)) {
    704       # Infer the language from the extension.
    705       if ($file =~ /[.]([^.]+)$/) {
    706         $FileLang = $LangMap{$1};
    707       }
    708     }
    709 
    710     # FileLang still not defined?  Skip the file.
    711     next if (!defined $FileLang);
    712 
    713     # Language not accepted?
    714     next if (!defined $LangsAccepted{$FileLang});
    715 
    716     my @CmdArgs;
    717     my @AnalyzeArgs;
    718 
    719     if ($FileLang ne 'unknown') {
    720       push @CmdArgs, '-x', $FileLang;
    721     }
    722 
    723     if (defined $StoreModel) {
    724       push @AnalyzeArgs, "-analyzer-store=$StoreModel";
    725     }
    726 
    727     if (defined $ConstraintsModel) {
    728       push @AnalyzeArgs, "-analyzer-constraints=$ConstraintsModel";
    729     }
    730 
    731     if (defined $InternalStats) {
    732       push @AnalyzeArgs, "-analyzer-stats";
    733     }
    734 
    735     if (defined $Analyses) {
    736       push @AnalyzeArgs, split '\s+', $Analyses;
    737     }
    738 
    739     if (defined $Plugins) {
    740       push @AnalyzeArgs, split '\s+', $Plugins;
    741     }
    742 
    743     if (defined $OutputFormat) {
    744       push @AnalyzeArgs, "-analyzer-output=" . $OutputFormat;
    745       if ($OutputFormat =~ /plist/) {
    746         # Change "Output" to be a file.
    747         my ($h, $f) = tempfile("report-XXXXXX", SUFFIX => ".plist",
    748                                DIR => $HtmlDir);
    749         $ResultFile = $f;
    750         # If the HtmlDir is not set, we should clean up the plist files.
    751         if (!defined $HtmlDir || -z $HtmlDir) {
    752           $CleanupFile = $f;
    753         }
    754       }
    755     }
    756     if (defined $ConfigOptions) {
    757       push @AnalyzeArgs, split '\s+', $ConfigOptions;
    758     }
    759 
    760     push @CmdArgs, @CompileOpts;
    761     push @CmdArgs, $file;
    762 
    763     if (scalar @Archs) {
    764       foreach my $arch (@Archs) {
    765         my @NewArgs;
    766         push @NewArgs, '-arch', $arch;
    767         push @NewArgs, @CmdArgs;
    768         Analyze($Clang, \@NewArgs, \@AnalyzeArgs, $FileLang, $Output,
    769                 $Verbose, $HtmlDir, $file);
    770       }
    771     }
    772     else {
    773       Analyze($Clang, \@CmdArgs, \@AnalyzeArgs, $FileLang, $Output,
    774               $Verbose, $HtmlDir, $file);
    775     }
    776   }
    777 }
    778