[cfe-commits] r53783 - /cfe/trunk/utils/ccc-analyzer

Ted Kremenek kremenek at apple.com
Fri Jul 18 23:11:04 PDT 2008


Author: kremenek
Date: Sat Jul 19 01:11:04 2008
New Revision: 53783

URL: http://llvm.org/viewvc/llvm-project?rev=53783&view=rev
Log:
Reimplement ccc-analyzer in a language I actually know, and implement some obvious optimizations when processing command line arguments.

Modified:
    cfe/trunk/utils/ccc-analyzer

Modified: cfe/trunk/utils/ccc-analyzer
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/utils/ccc-analyzer?rev=53783&r1=53782&r2=53783&view=diff

==============================================================================
--- cfe/trunk/utils/ccc-analyzer (original)
+++ cfe/trunk/utils/ccc-analyzer Sat Jul 19 01:11:04 2008
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env perl
 #
 #                     The LLVM Compiler Infrastructure
 #
@@ -7,278 +7,303 @@
 #
 ##===----------------------------------------------------------------------===##
 #
-# A reduced version of the 'ccc' script that is designed to handle off
-# actual compilation to gcc, but run the code passed to gcc through the
-# static analyzer.
+#  A script designed to interpose between the build system and gcc.  It invokes
+#  both gcc and the static analyzer.
 #
 ##===----------------------------------------------------------------------===##
 
-import sys
-import subprocess
-import os
-
-def error(message):
-  print >> sys.stderr, 'ccc: ' + message
-  sys.exit(1)
-
-def quote(arg):
-  if '"' in arg:
-    return repr(arg)
-  return arg
-
-def run(args):
-  code = subprocess.call(args)
-  if code > 255:
-    code = 1
-  if code:
-    sys.exit(code)
-
-def compile(args):
-  command = 'gcc'.split()
-  run(command + args)
-
-def remove_pch_extension(path):
-  i = path.rfind('.gch')
-  if i < 0:
-    return path
-  return path[:i]
-
-def analyze(clang, args,language,output,files,verbose,htmldir,file,analysis_type):
-  if language.rfind("c++") >= 0:
-    return
-
-  RunAnalyzer = 0;
-
-  if language.find("header") > 0:
-    target = remove_pch_extension(output)
-    command = ['cp']
-    args = command + files + [ target ]
-  else:
-    command = clang.split() + analysis_type.split()
-    args = command + "-DIBOutlet=__attribute__((iboutlet))".split() + args;
-    RunAnalyzer = 1
+use strict;
+use warnings;
+use Cwd;
+
+##----------------------------------------------------------------------------##
+#  Running the analyzer.
+##----------------------------------------------------------------------------##
 
-  print_args = []
+sub Analyze {
+  my ($Clang, $Args, $Lang, $Output, $Verbose, $HtmlDir, $file, $Analyses) = @_;
+
+  # Skip anything related to C++.
+  return if ($Lang =~ /c[+][+]/);
   
-  if verbose:
-    # We MUST print to stderr.  Some clients use the stdout output of
-    # gcc for various purposes.
-    print >> sys.stderr, ' '.join(['\n[LOCATION]:', os.getcwd(), '\n' ])
-    i = 0
-    while i < len(args):
-      print_args.append(''.join([ '\'', args[i], '\'' ]))
-      i += 1
-
-  if verbose == 2:
-    print >> sys.stderr, '#SHELL (cd ' + os.getcwd() + ' && ' + ' '.join(print_args) + ')\n'
-
-  if RunAnalyzer and htmldir is not None:
-    args.append('-o')
-    print_args.append('-o')
-    args.append(htmldir)
-    print_args.append(htmldir)
+  my $RunAnalyzer = 0;
+  my $Cmd;
+  my @CmdArgs;
+  
+  if ($Lang =~ /header/) {
+    exit 0 if (!defined ($Output));
+    $Cmd = 'cp';
+    push @CmdArgs,$file;
+    # Remove the PCH extension.
+    $Output =~ s/[.]gch$//;
+    push @CmdArgs,$Output;
+  }
+  else {
+    $Cmd = $Clang;
+    push @CmdArgs,(split /\s/,$Analyses);
+    push @CmdArgs,'-DIBOutlet=__attribute__((iboutlet))';
+    push @CmdArgs,@$Args;
+    $RunAnalyzer = 1;
+  }
+  
+  my @PrintArgs;
+  my $dir;
+  
+  if ($Verbose) {
+    $dir = getcwd();
+    print STDERR "\n[LOCATION]: $dir\n";
+    push @PrintArgs,"'$Cmd'";
+    foreach my $arg (@CmdArgs) { push @PrintArgs,"\'$arg\'"; }
+  }
   
-  if verbose == 1: 
+  if ($Verbose == 1) {
     # We MUST print to stderr.  Some clients use the stdout output of
     # gcc for various purposes. 
-    print >> sys.stderr, ' '.join(print_args)
-    print >> sys.stderr, '\n'
-
-  subprocess.call(args)
-
-def extension(path):
-    return path.split(".")[-1]
+    print STDERR join(' ', at PrintArgs);
+    print STDERR "\n";
+  }
+  elsif ($Verbose == 2) {
+    print STDERR "#SHELL (cd '$dir' && @PrintArgs)\n";
+  }
+  
+  if ($RunAnalyzer and defined($HtmlDir)) {
+    push @CmdArgs,'-o';
+    push @CmdArgs,$HtmlDir;
+  }
+  
+  system $Cmd, at CmdArgs;
+}
 
-def changeextension(path, newext):
-    i = path.rfind('.')
-    if i < 0:
-        return path
-    j = path.rfind('/', 0, i)
-    if j < 0:
-        return path[:i] + "." + newext
-    return path[j+1:i] + "." + newext
-
-def inferlanguage(extension):
-  if extension == "c":
-      return "c"
-  elif extension in ["cpp", "cc"]:
-      return "c++"
-  elif extension == "i":
-      return "c-cpp-output"
-  elif extension == "m":
-      return "objective-c"
-  elif extension == "mi":
-      return "objective-c-cpp-output"
-  elif extension in [ "s", "o", "a", "so" ]:
-      return "skip"
-  else:
-      return "skip"  # Skip files with unknown extensions.  This
-                     # deviates from ccc, but this works very well for
-                     # the analyzer.
-
-def main(args):
-  old_args = args
-  action = 'link'
-  output = ''
-  compile_opts = [ ]
-  link_opts = [ ]
-  files = []
-  save_temps = 0
-  language = ''
-  
-  verbose = 0
-  clang = "clang"    
-  
-  # Forward to GCC.
-  compile(args)    
-  
-  # Set the analyzer flag.
-  analysis_type = os.environ.get('CCC_ANALYZER_ANALYSIS')
-  
-  if analysis_type is None:
-    analysis_type = "-checker-cfref"
-
-  # Determine the level of verbosity.
-  if os.environ.get('CCC_ANALYZER_VERBOSE') is not None:
-    verbose = 1
-    
-  if os.environ.get('CCC_ANALYZER_LOG') is not None:
-    verbose = 2
+##----------------------------------------------------------------------------##
+#  Lookup tables.
+##----------------------------------------------------------------------------##
+
+my %CompileOptionMap = (
+  '-nostdinc' => 0,
+  '-fobjc-gc-only' => 0,
+  '-fobjc-gc' => 0,  
+  '-include' => 1,
+  '-idirafter' => 1,
+  '-iprefix' => 1,
+  '-iquote' => 1,
+  '-isystem' => 1,
+  '-iwithprefix' => 1,
+  '-iwithprefixbefore' => 1
+);
+
+my %LinkerOptionMap = (
+  '-framework' => 1
+);
+
+my %CompilerLinkerOptionMap = (
+  '-isysroot' => 1,
+  '-arch' => 1,
+  '-v' => 0
+);
+
+my %IgnoredOptionMap = (
+  '-fsyntax-only' => 0,
+  '-save-temps' => 0,
+  '-install_name' => 1,
+  '-exported_symbols_list' => 1,
+  '-current_version' => 1,
+  '-compatibility_version' => 1,
+  '-init' => 1,
+  '-e' => 1,
+  '-seg1addr' => 1,
+  '-bundle_loader' => 1,
+  '-multiply_defined' => 1,
+  '-sectorder' => 3,
+  '--param' => 1,
+  '-u' => 1
+);
+
+my %LangMap = (
+  'c'   => 'c',
+  'cpp' => 'c++',
+  'cc'  => 'c++',
+  'i'   => 'c-cpp-output',
+  'm'   => 'objective-c',
+  'mi'  => 'objective-c-cpp-output'
+);
+
+##----------------------------------------------------------------------------##
+#  Main Logic.
+##----------------------------------------------------------------------------##
+
+my $Action = 'link';
+my @CompileOpts;
+my @LinkOpts;
+my @Files;
+my $Lang;
+my $Output;
+
+# Forward arguments to gcc.
+my $Status = system("gcc", at ARGV);
+if ($Status) { exit($Status); }
+
+# Get the analysis options.
+my $Analyses = $ENV{'CCC_ANALYZER_ANALYSIS'};
+if (!defined($Analyses)) { $Analyses = '-checker-cfref'; }
+
+# Determine the level of verbosity.
+my $Verbose = 0;
+if (defined $ENV{CCC_ANALYZER_VERBOSE}) { $Verbose = 1; }
+if (defined $ENV{CCC_ANALYZER_LOG}) { $Verbose = 2; }
+
+# Determine what clang executable to use.
+my $Clang = $ENV{'CLANG'};
+if (!defined $Clang) { $Clang = 'clang'; }
+
+# Get the HTML output directory.
+my $HtmlDir = $ENV{'CCC_ANALYZER_HTML'};
+
+
+# Process the arguments.
+foreach (my $i = 0; $i < scalar(@ARGV); ++$i) {
+  my $Arg = $ARGV[$i];
+  
+  # Modes ccc-analyzer supports
+  if ($Arg eq '-E') { $Action = 'preprocess'; }
+  elsif ($Arg eq '-c') { $Action = 'compile'; }
+  elsif ($Arg =~ /^-print-prog-name/) { exit 0; }
     
-  # Determine what clang executable to use.
-  clang_env = os.environ.get('CLANG') 
+  # Options with possible arguments that should pass through to compiler.
+  if (defined $CompileOptionMap{$Arg}) {
+    my $Cnt = $CompileOptionMap{$Arg};
+    push @CompileOpts,$Arg;
+    while ($Cnt > 0) { ++$i; --$Cnt; push @CompileOpts, $ARGV[$i]; }
+    next;
+  }
+
+  # Options with possible arguments that should pass through to linker.
+  if (defined $LinkerOptionMap{$Arg}) {
+    my $Cnt = $LinkerOptionMap{$Arg};
+    push @LinkOpts,$Arg;
+    while ($Cnt > 0) { ++$i; --$Cnt; push @LinkOpts, $ARGV[$i]; }
+    next;
+  }
+
+  # Options with possible arguments that should pass through to both compiler
+  # and the linker.
+  if (defined $CompilerLinkerOptionMap{$Arg}) {
+    my $Cnt = $CompilerLinkerOptionMap{$Arg};
+    push @CompileOpts,$Arg;    
+    push @LinkOpts,$Arg;
+    while ($Cnt > 0) {
+      ++$i; --$Cnt;
+      push @CompileOpts, $ARGV[$i];
+      push @LinkOpts, $ARGV[$i];
+    }
+    next;
+  }
+  
+  # Ignored options.
+  if (defined $IgnoredOptionMap{$Arg}) {
+    my $Cnt = $IgnoredOptionMap{$Arg};
+    while ($Cnt > 0) {
+      ++$i; --$Cnt;
+    }
+    next;
+  }
+  
+  # Compile mode flags.
+  if ($Arg =~ /^-[D,I,U](.*)$/) {
+    my $Tmp = $Arg;    
+    if ($1 eq '') {
+      # FIXME: Check if we are going off the end.
+      ++$i;
+      $Tmp = $Arg . $ARGV[$i];
+    }
+    push @CompileOpts,$Tmp;
+    next;
+  }
+  
+  # Language.
+  if ($Arg eq '-x') {
+    $Lang = $ARGV[$i+1];
+    ++$i; next;
+  }
+
+  # Output file.
+  if ($Arg eq '-o') {
+    ++$i;
+    $Output = $ARGV[$i];
+    next;
+  }
+  
+  # Get the link mode.
+  if ($Arg =~ /^-[l,L,O]/) {
+    if ($Arg eq '-O') { push @LinkOpts,'-O1'; }
+    elsif ($Arg eq '-Os') { push @LinkOpts,'-O2'; }
+    else { push @LinkOpts,$Arg; }
+    next;
+  }
   
-  if clang_env is not None:
-    clang = clang_env
+  if ($Arg =~ /^-std=/) {
+    push @CompileOpts,$Arg;
+    next;
+  }
   
-  # Get the HTML output directory.
-  htmldir = os.environ.get('CCC_ANALYZER_HTML')
+#  if ($Arg =~ /^-f/) {
+#    # FIXME: Not sure if the remaining -fxxxx options have no arguments.
+#    push @CompileOpts,$Arg;
+#    push @LinkOpts,$Arg;  # FIXME: Not sure if these are link opts.
+#  }
+  
+  # Get the compiler/link mode.
+  if ($Arg =~ /^-F(.+)$/) {
+    my $Tmp = $Arg;
+    if ($1 eq '') {
+      # FIXME: Check if we are going off the end.
+      ++$i;
+      $Tmp = $Arg . $ARGV[$i];
+    }
+    push @CompileOpts,$Tmp;
+    push @LinkOpts,$Tmp;
+    next;
+  }
+
+  # Input files.
+  if ($Arg eq '-filelist') {
+    # FIXME: Make sure we aren't walking off the end.
+    open(IN, $ARGV[$i+1]);
+    while (<IN>) { s/\015?\012//; push @Files,$_; }
+    close(IN);
+    ++$i; next;
+  }
+  
+  if (!($Arg =~ /^-/)) {
+    push @Files,$Arg; next;
+  }
+}
+
+if ($Action eq 'compile' or $Action eq 'link') {
+  foreach my $file (@Files) {
+    # Determine the language for the file.
+    my $FileLang = $Lang;
+
+    if (!defined($FileLang)) {
+      # Infer the language from the extension.
+      if ($file =~ /[.]([^.]+)$/) {
+        $FileLang = $LangMap{$1};
+      }
+    }
+    
+    next if (!defined $FileLang);
+    
+    my @AnalyzeArgs;
+    
+    if ($FileLang ne 'unknown') {
+      push @AnalyzeArgs,'-x';
+      push @AnalyzeArgs,$FileLang;
+    }
 
-  # Process the arguments.
-  i = 0
-  while i < len(args):
-    arg = args[i]
-
-    # Modes ccc supports
-    if arg == '-E':
-        action = 'preprocess'
-    if arg == '-c':
-        action = 'compile'
-    if arg.startswith('-print-prog-name'):
-        action = 'print-prog-name'
-    if arg == '-save-temps':
-        save_temps = 1
-
-    # Options with no arguments that should pass through
-    if arg in ['-v']:
-        compile_opts.append(arg)
-        link_opts.append(arg)
+    push @AnalyzeArgs, at CompileOpts;
+    push @AnalyzeArgs,$file;
     
-    # Options with one argument that should be ignored
-    if arg in ['--param', '-u']:
-        i += 1
-
-    # Prefix matches for the compile mode
-    if arg[:2] in ['-D', '-I', '-U', '-F' ]:
-        if not arg[2:]:
-            arg += args[i+1]
-            i += 1
-        compile_opts.append(arg)
-
-    if arg[:5] in ['-std=']:
-        compile_opts.append(arg)
-
-    # Options with one argument that should pass through to compiler
-    if arg in [ '-include', '-idirafter', '-iprefix',
-                '-iquote', '-isystem', '-iwithprefix',
-                '-iwithprefixbefore']:
-        compile_opts.append(arg)
-        compile_opts.append(args[i+1])
-        i += 1
-        
-    # Options with no argument that should pass through to compiler
-    if arg in [ '-nostdinc', '-fobjc-gc-only', '-fobjc-gc' ]:
-      compile_opts.append(arg)          
-
-    # Options with one argument that should pass through to linker
-    if arg == '-framework':
-        link_opts.append(arg)
-        link_opts.append(args[i+1])
-        i += 1
-
-    # Options with one argument that should pass through to both
-    if arg in ['-isysroot', '-arch']:
-        compile_opts.append(arg)
-        compile_opts.append(args[i+1])
-        link_opts.append(arg)
-        link_opts.append(args[i+1])
-        i += 1
-
-    # Prefix matches for the link mode
-    if arg[:2] in ['-l', '-L', '-O', '-F']:
-        if arg == '-O': arg = '-O1'
-        if arg == '-Os': arg = '-O2'
-        link_opts.append(arg)
-
-    # Input files
-    if arg == '-filelist':
-        f = open(args[i+1])
-        for line in f:
-            files.append(line.strip())
-        f.close()
-        i += 1
-    if arg == '-x':
-        language = args[i+1]
-        i += 1
-    if arg[0] != '-':
-        files.append(arg)
-
-    # Output file
-    if arg == '-o':
-        output = args[i+1]
-        i += 1
-                
-    # Arguments we currently ignore with one option.
-    if arg in ['-install_name', '-exported_symbols_list', 
-               '-current_version', '-compatibility_version', '-init', '-e',
-               '-seg1addr', '-bundle_loader', '-multiply_defined']:
-      i += 1
-      
-    # Arguments we currently ignore with three options.
-    if arg in ['-sectorder']:
-      i += 3
-
-    i += 1
-
-  if action == 'print-prog-name':
-      # assume we can handle everything
-      print sys.argv[0]
-      return
-
-  if not files:
-      error('no input files')
-
-  if action == 'compile' or save_temps or action == 'link':
-    for i, file in enumerate(files):
-      file_language = language
-      if not language:
-        file_language = inferlanguage(extension(file))
-        if file_language == "skip":
-          continue
-
-      if save_temps and action != "compile":
-        # Need a temporary output file
-        coutput = changeextension(file, "o");
-        files[i] = coutput
-      elif not output:
-        coutput = changeextension(file, "o")
-      else:
-        coutput = output
-      analyze_args = [ file ]
-      if file_language != 'unknown':
-        analyze_args = [ '-x', file_language ] + analyze_args
-      analyze_args = analyze_args + compile_opts
-      analyze(clang, analyze_args, language, output, files, verbose, htmldir, file, analysis_type)
+    Analyze($Clang, \@AnalyzeArgs, $FileLang, $Output,
+            $Verbose, $HtmlDir, $file, $Analyses);
+  }
+}
 
-if __name__ == '__main__':
-    main(sys.argv[1:])





More information about the cfe-commits mailing list