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 
     33 if (`uname -a` =~ m/Darwin/) { 
     34 	$DefaultCCompiler = 'clang';
     35 	$DefaultCXXCompiler = 'clang++'; 
     36 } else {
     37     $DefaultCCompiler = 'gcc';
     38     $DefaultCXXCompiler = 'g++'; 	
     39 }
     40 
     41 if ($FindBin::Script =~ /c\+\+-analyzer/) {
     42   $Compiler = $ENV{'CCC_CXX'};
     43   if (!defined $Compiler) { $Compiler = $DefaultCXXCompiler; }
     44   
     45   $Clang = $ENV{'CLANG_CXX'};
     46   if (!defined $Clang) { $Clang = 'clang++'; }
     47 }
     48 else {
     49   $Compiler = $ENV{'CCC_CC'};
     50   if (!defined $Compiler) { $Compiler = $DefaultCCompiler; }
     51 
     52   $Clang = $ENV{'CLANG'};
     53   if (!defined $Clang) { $Clang = 'clang'; }
     54 }
     55 
     56 ##===----------------------------------------------------------------------===##
     57 # Cleanup.
     58 ##===----------------------------------------------------------------------===##
     59 
     60 my $ReportFailures = $ENV{'CCC_REPORT_FAILURES'};
     61 if (!defined $ReportFailures) { $ReportFailures = 1; }
     62 
     63 my $CleanupFile;
     64 my $ResultFile;
     65 
     66 # Remove any stale files at exit.
     67 END { 
     68   if (defined $ResultFile && -z $ResultFile) {
     69     `rm -f $ResultFile`;
     70   }
     71   if (defined $CleanupFile) {
     72     `rm -f $CleanupFile`;
     73   }
     74 }
     75 
     76 ##----------------------------------------------------------------------------##
     77 #  Process Clang Crashes.
     78 ##----------------------------------------------------------------------------##
     79 
     80 sub GetPPExt {
     81   my $Lang = shift;
     82   if ($Lang =~ /objective-c\+\+/) { return ".mii" };
     83   if ($Lang =~ /objective-c/) { return ".mi"; }
     84   if ($Lang =~ /c\+\+/) { return ".ii"; }
     85   return ".i";
     86 }
     87 
     88 # Set this to 1 if we want to include 'parser rejects' files.
     89 my $IncludeParserRejects = 0;
     90 my $ParserRejects = "Parser Rejects";
     91 my $AttributeIgnored = "Attribute Ignored";
     92 my $OtherError = "Other Error";
     93 
     94 sub ProcessClangFailure {
     95   my ($Clang, $Lang, $file, $Args, $HtmlDir, $ErrorType, $ofile) = @_;
     96   my $Dir = "$HtmlDir/failures";
     97   mkpath $Dir;
     98   
     99   my $prefix = "clang_crash";
    100   if ($ErrorType eq $ParserRejects) {
    101     $prefix = "clang_parser_rejects";
    102   }
    103   elsif ($ErrorType eq $AttributeIgnored) {
    104     $prefix = "clang_attribute_ignored";
    105   }
    106   elsif ($ErrorType eq $OtherError) {
    107     $prefix = "clang_other_error";
    108   }
    109 
    110   # Generate the preprocessed file with Clang.
    111   my ($PPH, $PPFile) = tempfile( $prefix . "_XXXXXX",
    112                                  SUFFIX => GetPPExt($Lang),
    113                                  DIR => $Dir);
    114   system $Clang, @$Args, "-E", "-o", $PPFile;
    115   close ($PPH);
    116   
    117   # Create the info file.
    118   open (OUT, ">", "$PPFile.info.txt") or die "Cannot open $PPFile.info.txt\n";
    119   print OUT abs_path($file), "\n";
    120   print OUT "$ErrorType\n";
    121   print OUT "@$Args\n";
    122   close OUT;
    123   `uname -a >> $PPFile.info.txt 2>&1`;
    124   `$Compiler -v >> $PPFile.info.txt 2>&1`;
    125   system 'mv',$ofile,"$PPFile.stderr.txt";
    126   return (basename $PPFile);
    127 }
    128 
    129 ##----------------------------------------------------------------------------##
    130 #  Running the analyzer.
    131 ##----------------------------------------------------------------------------##
    132 
    133 sub GetCCArgs {
    134   my $mode = shift;
    135   my $Args = shift;
    136   
    137   pipe (FROM_CHILD, TO_PARENT);
    138   my $pid = fork();
    139   if ($pid == 0) {
    140     close FROM_CHILD;
    141     open(STDOUT,">&", \*TO_PARENT);
    142     open(STDERR,">&", \*TO_PARENT);
    143     exec $Clang, "-###", $mode, @$Args;
    144   }  
    145   close(TO_PARENT);
    146   my $line;
    147   while (<FROM_CHILD>) {
    148     next if (!/-cc1/);
    149     $line = $_;
    150   }
    151 
    152   waitpid($pid,0);
    153   close(FROM_CHILD);
    154   
    155   die "could not find clang line\n" if (!defined $line);
    156   # Strip the newline and initial whitspace
    157   chomp $line;
    158   $line =~ s/^\s+//;
    159   my @items = quotewords('\s+', 0, $line);
    160   my $cmd = shift @items;
    161   die "cannot find 'clang' in 'clang' command\n" if (!($cmd =~ /clang/));
    162   return \@items;
    163 }
    164 
    165 sub Analyze {
    166   my ($Clang, $OriginalArgs, $AnalyzeArgs, $Lang, $Output, $Verbose, $HtmlDir,
    167       $file) = @_;
    168 
    169   my @Args = @$OriginalArgs;
    170   my $Cmd;
    171   my @CmdArgs;
    172   my @CmdArgsSansAnalyses;
    173 
    174   if ($Lang =~ /header/) {
    175     exit 0 if (!defined ($Output));
    176     $Cmd = 'cp';
    177     push @CmdArgs, $file;
    178     # Remove the PCH extension.
    179     $Output =~ s/[.]gch$//;
    180     push @CmdArgs, $Output;
    181     @CmdArgsSansAnalyses = @CmdArgs;
    182   }
    183   else {
    184     $Cmd = $Clang;
    185     if ($Lang eq "objective-c" || $Lang eq "objective-c++") {
    186       push @Args,'-DIBOutlet=__attribute__((iboutlet))';
    187       push @Args,'-DIBOutletCollection(ClassName)=__attribute__((iboutletcollection)))';
    188       push @Args,'-DIBAction=void)__attribute__((ibaction)';
    189     }
    190 
    191     # Create arguments for doing regular parsing.
    192     my $SyntaxArgs = GetCCArgs("-fsyntax-only", \@Args);
    193     @CmdArgsSansAnalyses = @$SyntaxArgs;
    194 
    195     # Create arguments for doing static analysis.
    196     if (defined $ResultFile) {
    197       push @Args, '-o', $ResultFile;
    198     }
    199     elsif (defined $HtmlDir) {
    200       push @Args, '-o', $HtmlDir;
    201     }
    202     if ($Verbose) {
    203       push @Args, "-Xclang", "-analyzer-display-progress";
    204     }
    205 
    206     foreach my $arg (@$AnalyzeArgs) {
    207       push @Args, "-Xclang", $arg;
    208     }
    209 
    210     # Display Ubiviz graph?
    211     if (defined $ENV{'CCC_UBI'}) {   
    212       push @Args, "-Xclang", "-analyzer-viz-egraph-ubigraph";
    213     }
    214 
    215     my $AnalysisArgs = GetCCArgs("--analyze", \@Args);
    216     @CmdArgs = @$AnalysisArgs;
    217   }
    218 
    219   my @PrintArgs;
    220   my $dir;
    221 
    222   if ($Verbose) {
    223     $dir = getcwd();
    224     print STDERR "\n[LOCATION]: $dir\n";
    225     push @PrintArgs,"'$Cmd'";
    226     foreach my $arg (@CmdArgs) {
    227         push @PrintArgs,"\'$arg\'";
    228     }
    229   }
    230   if ($Verbose == 1) {
    231     # We MUST print to stderr.  Some clients use the stdout output of
    232     # gcc for various purposes. 
    233     print STDERR join(' ', @PrintArgs);
    234     print STDERR "\n";
    235   }
    236   elsif ($Verbose == 2) {
    237     print STDERR "#SHELL (cd '$dir' && @PrintArgs)\n";
    238   }
    239 
    240   # Capture the STDERR of clang and send it to a temporary file.
    241   # Capture the STDOUT of clang and reroute it to ccc-analyzer's STDERR.
    242   # We save the output file in the 'crashes' directory if clang encounters
    243   # any problems with the file.  
    244   pipe (FROM_CHILD, TO_PARENT);
    245   my $pid = fork();
    246   if ($pid == 0) {
    247     close FROM_CHILD;
    248     open(STDOUT,">&", \*TO_PARENT);
    249     open(STDERR,">&", \*TO_PARENT);
    250     exec $Cmd, @CmdArgs;
    251   }
    252 
    253   close TO_PARENT;
    254   my ($ofh, $ofile) = tempfile("clang_output_XXXXXX", DIR => $HtmlDir);
    255   
    256   while (<FROM_CHILD>) {
    257     print $ofh $_;
    258     print STDERR $_;
    259   }
    260 
    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   '-fblocks' => 0,
    333   '-fno-builtin' => 0,
    334   '-fobjc-gc-only' => 0,
    335   '-fobjc-gc' => 0,
    336   '-ffreestanding' => 0,
    337   '-include' => 1,
    338   '-idirafter' => 1,
    339   '-imacros' => 1,
    340   '-iprefix' => 1,
    341   '-iquote' => 1,
    342   '-isystem' => 1,
    343   '-iwithprefix' => 1,
    344   '-iwithprefixbefore' => 1
    345 );
    346 
    347 my %LinkerOptionMap = (
    348   '-framework' => 1,
    349   '-fobjc-link-runtime' => 0
    350 );
    351 
    352 my %CompilerLinkerOptionMap = (
    353   '-fobjc-arc' => 0,
    354   '-fobjc-abi-version' => 0, # This is really a 1 argument, but always has '='
    355   '-isysroot' => 1,
    356   '-arch' => 1,
    357   '-m32' => 0,
    358   '-m64' => 0,
    359   '-v' => 0,
    360   '-fpascal-strings' => 0,
    361   '-mmacosx-version-min' => 0, # This is really a 1 argument, but always has '='
    362   '-miphoneos-version-min' => 0 # This is really a 1 argument, but always has '='
    363 );
    364 
    365 my %IgnoredOptionMap = (
    366   '-MT' => 1,  # Ignore these preprocessor options.
    367   '-MF' => 1,
    368 
    369   '-fsyntax-only' => 0,
    370   '-save-temps' => 0,
    371   '-install_name' => 1,
    372   '-exported_symbols_list' => 1,
    373   '-current_version' => 1,
    374   '-compatibility_version' => 1,
    375   '-init' => 1,
    376   '-e' => 1,
    377   '-seg1addr' => 1,
    378   '-bundle_loader' => 1,
    379   '-multiply_defined' => 1,
    380   '-sectorder' => 3,
    381   '--param' => 1,
    382   '-u' => 1,
    383   '--serialize-diagnostics' => 1
    384 );
    385 
    386 my %LangMap = (
    387   'c'   => 'c',
    388   'cp'  => 'c++',
    389   'cpp' => 'c++',
    390   'cxx' => 'c++',
    391   'txx' => 'c++',
    392   'cc'  => 'c++',
    393   'ii'  => 'c++',
    394   'i'   => 'c-cpp-output',
    395   'm'   => 'objective-c',
    396   'mi'  => 'objective-c-cpp-output',
    397   'mm'  => 'objective-c++'
    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 );
    414 
    415 ##----------------------------------------------------------------------------##
    416 #  Main Logic.
    417 ##----------------------------------------------------------------------------##
    418 
    419 my $Action = 'link';
    420 my @CompileOpts;
    421 my @LinkOpts;
    422 my @Files;
    423 my $Lang;
    424 my $Output;
    425 my %Uniqued;
    426 
    427 # Forward arguments to gcc.
    428 my $Status = system($Compiler,@ARGV);
    429 if  (defined $ENV{'CCC_ANALYZER_LOG'}) {
    430   print "$Compiler @ARGV\n";
    431 }
    432 if ($Status) { exit($Status >> 8); }
    433 
    434 # Get the analysis options.
    435 my $Analyses = $ENV{'CCC_ANALYZER_ANALYSIS'};
    436 
    437 # Get the store model.
    438 my $StoreModel = $ENV{'CCC_ANALYZER_STORE_MODEL'};
    439 
    440 # Get the constraints engine.
    441 my $ConstraintsModel = $ENV{'CCC_ANALYZER_CONSTRAINTS_MODEL'};
    442 
    443 # Get the output format.
    444 my $OutputFormat = $ENV{'CCC_ANALYZER_OUTPUT_FORMAT'};
    445 if (!defined $OutputFormat) { $OutputFormat = "html"; }
    446 
    447 # Determine the level of verbosity.
    448 my $Verbose = 0;
    449 if (defined $ENV{CCC_ANALYZER_VERBOSE}) { $Verbose = 1; }
    450 if (defined $ENV{CCC_ANALYZER_LOG}) { $Verbose = 2; }
    451 
    452 # Get the HTML output directory.
    453 my $HtmlDir = $ENV{'CCC_ANALYZER_HTML'};
    454 
    455 my %DisabledArchs = ('ppc' => 1, 'ppc64' => 1);
    456 my %ArchsSeen;
    457 my $HadArch = 0;
    458 
    459 # Process the arguments.
    460 foreach (my $i = 0; $i < scalar(@ARGV); ++$i) {
    461   my $Arg = $ARGV[$i];  
    462   my ($ArgKey) = split /=/,$Arg,2;
    463 
    464   # Modes ccc-analyzer supports
    465   if ($Arg =~ /^-(E|MM?)$/) { $Action = 'preprocess'; }
    466   elsif ($Arg eq '-c') { $Action = 'compile'; }
    467   elsif ($Arg =~ /^-print-prog-name/) { exit 0; }
    468 
    469   # Specially handle duplicate cases of -arch
    470   if ($Arg eq "-arch") {
    471     my $arch = $ARGV[$i+1];
    472     # We don't want to process 'ppc' because of Clang's lack of support
    473     # for Altivec (also some #defines won't likely be defined correctly, etc.)
    474     if (!(defined $DisabledArchs{$arch})) { $ArchsSeen{$arch} = 1; }
    475     $HadArch = 1;
    476     ++$i;
    477     next;
    478   }
    479 
    480   # Options with possible arguments that should pass through to compiler.
    481   if (defined $CompileOptionMap{$ArgKey}) {
    482     my $Cnt = $CompileOptionMap{$ArgKey};
    483     push @CompileOpts,$Arg;
    484     while ($Cnt > 0) { ++$i; --$Cnt; push @CompileOpts, $ARGV[$i]; }
    485     next;
    486   }
    487 
    488   # Options with possible arguments that should pass through to linker.
    489   if (defined $LinkerOptionMap{$ArgKey}) {
    490     my $Cnt = $LinkerOptionMap{$ArgKey};
    491     push @LinkOpts,$Arg;
    492     while ($Cnt > 0) { ++$i; --$Cnt; push @LinkOpts, $ARGV[$i]; }
    493     next;
    494   }
    495 
    496   # Options with possible arguments that should pass through to both compiler
    497   # and the linker.
    498   if (defined $CompilerLinkerOptionMap{$ArgKey}) {
    499     my $Cnt = $CompilerLinkerOptionMap{$ArgKey};
    500     
    501     # Check if this is an option that should have a unique value, and if so
    502     # determine if the value was checked before.
    503     if ($UniqueOptions{$Arg}) {
    504       if (defined $Uniqued{$Arg}) {
    505         $i += $Cnt;
    506         next;
    507       }
    508       $Uniqued{$Arg} = 1;
    509     }
    510     
    511     push @CompileOpts,$Arg;    
    512     push @LinkOpts,$Arg;
    513 
    514     while ($Cnt > 0) {
    515       ++$i; --$Cnt;
    516       push @CompileOpts, $ARGV[$i];
    517       push @LinkOpts, $ARGV[$i];
    518     }
    519     next;
    520   }
    521   
    522   # Ignored options.
    523   if (defined $IgnoredOptionMap{$ArgKey}) {
    524     my $Cnt = $IgnoredOptionMap{$ArgKey};
    525     while ($Cnt > 0) {
    526       ++$i; --$Cnt;
    527     }
    528     next;
    529   }
    530   
    531   # Compile mode flags.
    532   if ($Arg =~ /^-[D,I,U](.*)$/) {
    533     my $Tmp = $Arg;    
    534     if ($1 eq '') {
    535       # FIXME: Check if we are going off the end.
    536       ++$i;
    537       $Tmp = $Arg . $ARGV[$i];
    538     }
    539     push @CompileOpts,$Tmp;
    540     next;
    541   }
    542   
    543   # Language.
    544   if ($Arg eq '-x') {
    545     $Lang = $ARGV[$i+1];
    546     ++$i; next;
    547   }
    548 
    549   # Output file.
    550   if ($Arg eq '-o') {
    551     ++$i;
    552     $Output = $ARGV[$i];
    553     next;
    554   }
    555   
    556   # Get the link mode.
    557   if ($Arg =~ /^-[l,L,O]/) {
    558     if ($Arg eq '-O') { push @LinkOpts,'-O1'; }
    559     elsif ($Arg eq '-Os') { push @LinkOpts,'-O2'; }
    560     else { push @LinkOpts,$Arg; }
    561     next;
    562   }
    563   
    564   if ($Arg =~ /^-std=/) {
    565     push @CompileOpts,$Arg;
    566     next;
    567   }
    568   
    569 #  if ($Arg =~ /^-f/) {
    570 #    # FIXME: Not sure if the remaining -fxxxx options have no arguments.
    571 #    push @CompileOpts,$Arg;
    572 #    push @LinkOpts,$Arg;  # FIXME: Not sure if these are link opts.
    573 #  }
    574   
    575   # Get the compiler/link mode.
    576   if ($Arg =~ /^-F(.+)$/) {
    577     my $Tmp = $Arg;
    578     if ($1 eq '') {
    579       # FIXME: Check if we are going off the end.
    580       ++$i;
    581       $Tmp = $Arg . $ARGV[$i];
    582     }
    583     push @CompileOpts,$Tmp;
    584     push @LinkOpts,$Tmp;
    585     next;
    586   }
    587 
    588   # Input files.
    589   if ($Arg eq '-filelist') {
    590     # FIXME: Make sure we aren't walking off the end.
    591     open(IN, $ARGV[$i+1]);
    592     while (<IN>) { s/\015?\012//; push @Files,$_; }
    593     close(IN);
    594     ++$i;
    595     next;
    596   }
    597   
    598   # Handle -Wno-.  We don't care about extra warnings, but
    599   # we should suppress ones that we don't want to see.
    600   if ($Arg =~ /^-Wno-/) {
    601     push @CompileOpts, $Arg;
    602     next;
    603   }
    604 
    605   if (!($Arg =~ /^-/)) {
    606     push @Files, $Arg;
    607     next;
    608   }
    609 }
    610 
    611 if ($Action eq 'compile' or $Action eq 'link') {
    612   my @Archs = keys %ArchsSeen;
    613   # Skip the file if we don't support the architectures specified.
    614   exit 0 if ($HadArch && scalar(@Archs) == 0);
    615   
    616   foreach my $file (@Files) {
    617     # Determine the language for the file.
    618     my $FileLang = $Lang;
    619 
    620     if (!defined($FileLang)) {
    621       # Infer the language from the extension.
    622       if ($file =~ /[.]([^.]+)$/) {
    623         $FileLang = $LangMap{$1};
    624       }
    625     }
    626     
    627     # FileLang still not defined?  Skip the file.
    628     next if (!defined $FileLang);
    629 
    630     # Language not accepted?
    631     next if (!defined $LangsAccepted{$FileLang});
    632 
    633     my @CmdArgs;
    634     my @AnalyzeArgs;    
    635     
    636     if ($FileLang ne 'unknown') {
    637       push @CmdArgs, '-x', $FileLang;
    638     }
    639 
    640     if (defined $StoreModel) {
    641       push @AnalyzeArgs, "-analyzer-store=$StoreModel";
    642     }
    643 
    644     if (defined $ConstraintsModel) {
    645       push @AnalyzeArgs, "-analyzer-constraints=$ConstraintsModel";
    646     }
    647     
    648     if (defined $Analyses) {
    649       push @AnalyzeArgs, split '\s+', $Analyses;
    650     }
    651 
    652     if (defined $OutputFormat) {
    653       push @AnalyzeArgs, "-analyzer-output=" . $OutputFormat;
    654       if ($OutputFormat =~ /plist/) {
    655         # Change "Output" to be a file.
    656         my ($h, $f) = tempfile("report-XXXXXX", SUFFIX => ".plist",
    657                                DIR => $HtmlDir);
    658         $ResultFile = $f;
    659         # If the HtmlDir is not set, we sould clean up the plist files.
    660         if (!defined $HtmlDir || -z $HtmlDir) {
    661         	$CleanupFile = $f; 
    662         }
    663       }
    664     }
    665 
    666     push @CmdArgs, @CompileOpts;
    667     push @CmdArgs, $file;
    668 
    669     if (scalar @Archs) {
    670       foreach my $arch (@Archs) {
    671         my @NewArgs;
    672         push @NewArgs, '-arch', $arch;
    673         push @NewArgs, @CmdArgs;
    674         Analyze($Clang, \@NewArgs, \@AnalyzeArgs, $FileLang, $Output,
    675                 $Verbose, $HtmlDir, $file);
    676       }
    677     }
    678     else {
    679       Analyze($Clang, \@CmdArgs, \@AnalyzeArgs, $FileLang, $Output,
    680               $Verbose, $HtmlDir, $file);
    681     }
    682   }
    683 }
    684 
    685 exit($Status >> 8);
    686 
    687