Home | History | Annotate | Download | only in metrics
      1 #!/usr/bin/env python
      2 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
      3 # Use of this source code is governed by a BSD-style license that can be
      4 # found in the LICENSE file.
      5 
      6 """Counts the number of #if or #ifdef lines containing at least one
      7 preprocessor token that is a full match for the given pattern, in the
      8 given directory.
      9 """
     10 
     11 
     12 import optparse
     13 import os
     14 import re
     15 import sys
     16 
     17 
     18 # Filename extensions we know will be handled by the C preprocessor.
     19 # We ignore files not matching these.
     20 CPP_EXTENSIONS = [
     21   '.h',
     22   '.cc',
     23   '.m',
     24   '.mm',
     25 ]
     26 
     27 
     28 def _IsTestFile(filename):
     29   """Does a rudimentary check to try to skip test files; this could be
     30   improved but is good enough for basic metrics generation.
     31   """
     32   return re.match('(test|mock|dummy)_.*|.*_[a-z]*test\.(h|cc|mm)', filename)
     33 
     34 
     35 def CountIfdefs(token_pattern, directory, skip_tests=False):
     36   """Returns the number of lines in files in |directory| and its
     37   subdirectories that have an extension from |CPP_EXTENSIONS| and are
     38   an #if or #ifdef line with a preprocessor token fully matching
     39   the string |token_pattern|.
     40 
     41   If |skip_tests| is true, a best effort is made to ignore test files.
     42   """
     43   token_line_re = re.compile(r'^#if(def)?.*\b(%s)\b.*$' % token_pattern)
     44   count = 0
     45   for root, dirs, files in os.walk(directory):
     46     for filename in files:
     47       if os.path.splitext(filename)[1] in CPP_EXTENSIONS:
     48         if not skip_tests or not _IsTestFile(filename):
     49           with open(os.path.join(root, filename)) as f:
     50             for line in f:
     51               line = line.strip()
     52               if token_line_re.match(line):
     53                 count += 1
     54   return count
     55 
     56 
     57 def PrintUsage():
     58   print "Usage: %s [--skip-tests] TOKEN_PATTERN DIRECTORY" % sys.argv[0]
     59 
     60 
     61 def main():
     62   option_parser = optparse.OptionParser()
     63   option_parser.add_option('', '--skip-tests', action='store_true',
     64                            dest='skip_tests', default=False,
     65                            help='Skip test files.')
     66   options, args = option_parser.parse_args()
     67 
     68   if len(args) < 2:
     69     PrintUsage()
     70     return 1
     71   else:
     72     print CountIfdefs(args[0], args[1], options.skip_tests)
     73     return 0
     74 
     75 
     76 if __name__ == '__main__':
     77   sys.exit(main())
     78