fix mistake in RELEASE.txt content
[scons.git] / bin / linecount.py
blobae45c55455b4db2b6594dec2a9ec8b3af8bd0e7d
1 #!/usr/bin/env python
3 # __COPYRIGHT__
5 # Count statistics about SCons test and source files. This must be run
6 # against a fully-populated tree (for example, one that's been freshly
7 # checked out).
9 # A test file is anything under the src/ directory that begins with
10 # 'test_' or ends in 'Tests.py', or anything under the test/ directory
11 # that ends in '.py'. Note that runtest.py script does *not*, by default,
12 # consider the files that begin with 'test_' to be tests, because they're
13 # tests of SCons packaging and installation, not functional tests of
14 # SCons code.
16 # A source file is anything under the src/engine/ or src/script/
17 # directories that ends in '.py' but does NOT begin with 'test_'
18 # or end in 'Tests.py'.
20 # We report the number of tests and sources, the total number of lines
21 # in each category, the number of non-blank lines, and the number of
22 # non-comment lines. The last figure (non-comment) lines is the most
23 # interesting one for most purposes.
24 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
26 import os.path
28 fmt = "%-16s %5s %7s %9s %11s %11s"
30 class Collection:
31 def __init__(self, name, files=None, pred=None):
32 self._name = name
33 if files is None:
34 files = []
35 self.files = files
36 if pred is None:
37 pred = lambda x: True
38 self.pred = pred
39 def __call__(self, fname):
40 return self.pred(fname)
41 def __len__(self):
42 return len(self.files)
43 def collect(self, directory):
44 for dirpath, dirnames, filenames in os.walk(directory):
45 try: dirnames.remove('.svn')
46 except ValueError: pass
47 self.files.extend([ os.path.join(dirpath, f)
48 for f in filenames if self.pred(f) ])
49 def lines(self):
50 try:
51 return self._lines
52 except AttributeError:
53 self._lines = lines = []
54 for file in self.files:
55 file_lines = open(file).readlines()
56 lines.extend([s.lstrip() for s in file_lines])
57 return lines
58 def non_blank(self):
59 return [s for s in self.lines() if s != '']
60 def non_comment(self):
61 return [s for s in self.lines() if s == '' or s[0] != '#']
62 def non_blank_non_comment(self):
63 return [s for s in self.lines() if s != '' and s[0] != '#']
64 def printables(self):
65 return (self._name + ':',
66 len(self.files),
67 len(self.lines()),
68 len(self.non_blank()),
69 len(self.non_comment()),
70 len(self.non_blank_non_comment()))
72 def is_Tests_py(x):
73 return x[-8:] == 'Tests.py'
74 def is_test_(x):
75 return x[:5] == 'test_'
76 def is_python(x):
77 return x[-3:] == '.py'
78 def is_source(x):
79 return is_python(x) and not is_Tests_py(x) and not is_test_(x)
81 src_Tests_py_tests = Collection('src/ *Tests.py', pred=is_Tests_py)
82 src_test_tests = Collection('src/ test_*.py', pred=is_test_)
83 test_tests = Collection('test/ tests', pred=is_python)
84 sources = Collection('sources', pred=is_source)
86 src_Tests_py_tests.collect('src')
87 src_test_tests.collect('src')
88 test_tests.collect('test')
89 sources.collect('src/engine')
90 sources.collect('src/script')
92 src_tests = Collection('src/ tests', src_Tests_py_tests.files
93 + src_test_tests.files)
94 all_tests = Collection('all tests', src_tests.files + test_tests.files)
96 def ratio(over, under):
97 return "%.2f" % (float(len(over)) / float(len(under)))
99 print(fmt % ('', '', '', '', '', 'non-blank'))
100 print(fmt % ('', 'files', 'lines', 'non-blank', 'non-comment', 'non-comment'))
101 print()
102 print(fmt % src_Tests_py_tests.printables())
103 print(fmt % src_test_tests.printables())
104 print()
105 print(fmt % src_tests.printables())
106 print(fmt % test_tests.printables())
107 print()
108 print(fmt % all_tests.printables())
109 print(fmt % sources.printables())
110 print()
111 print(fmt % ('ratio:',
112 ratio(all_tests, sources),
113 ratio(all_tests.lines(), sources.lines()),
114 ratio(all_tests.non_blank(), sources.non_blank()),
115 ratio(all_tests.non_comment(), sources.non_comment()),
116 ratio(all_tests.non_blank_non_comment(),
117 sources.non_blank_non_comment())