15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#!/usr/bin/env python
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Copyright (c) 2012 The Chromium Authors. All rights reserved.
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# found in the LICENSE file.
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)"""Counts the number of #if or #ifdef lines containing at least one
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)preprocessor token that is a full match for the given pattern, in the
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)given directory.
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)"""
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import optparse
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import os
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import re
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import sys
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Filename extensions we know will be handled by the C preprocessor.
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# We ignore files not matching these.
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)CPP_EXTENSIONS = [
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  '.h',
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  '.cc',
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  '.m',
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  '.mm',
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)]
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def _IsTestFile(filename):
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Does a rudimentary check to try to skip test files; this could be
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  improved but is good enough for basic metrics generation.
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return re.match('(test|mock|dummy)_.*|.*_[a-z]*test\.(h|cc|mm)', filename)
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def CountIfdefs(token_pattern, directory, skip_tests=False):
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Returns the number of lines in files in |directory| and its
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  subdirectories that have an extension from |CPP_EXTENSIONS| and are
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  an #if or #ifdef line with a preprocessor token fully matching
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  the string |token_pattern|.
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  If |skip_tests| is true, a best effort is made to ignore test files.
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  token_line_re = re.compile(r'^#if(def)?.*\b(%s)\b.*$' % token_pattern)
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  count = 0
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for root, dirs, files in os.walk(directory):
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for filename in files:
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if os.path.splitext(filename)[1] in CPP_EXTENSIONS:
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if not skip_tests or not _IsTestFile(filename):
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          with open(os.path.join(root, filename)) as f:
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            for line in f:
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              line = line.strip()
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)              if token_line_re.match(line):
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                count += 1
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return count
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def PrintUsage():
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  print "Usage: %s [--skip-tests] TOKEN_PATTERN DIRECTORY" % sys.argv[0]
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def main():
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  option_parser = optparse.OptionParser()
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  option_parser.add_option('', '--skip-tests', action='store_true',
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                           dest='skip_tests', default=False,
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                           help='Skip test files.')
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  options, args = option_parser.parse_args()
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if len(args) < 2:
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    PrintUsage()
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return 1
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else:
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    print CountIfdefs(args[0], args[1], options.skip_tests)
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return 0
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)if __name__ == '__main__':
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  sys.exit(main())
78