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
7preprocessor token that is a full match for the given pattern, in the
8given directory.
9"""
10
11
12import optparse
13import os
14import re
15import sys
16
17
18# Filename extensions we know will be handled by the C preprocessor.
19# We ignore files not matching these.
20CPP_EXTENSIONS = [
21  '.h',
22  '.cc',
23  '.m',
24  '.mm',
25]
26
27
28def _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
35def 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
57def PrintUsage():
58  print "Usage: %s [--skip-tests] TOKEN_PATTERN DIRECTORY" % sys.argv[0]
59
60
61def 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
76if __name__ == '__main__':
77  sys.exit(main())
78