blob: c4f78536bb7c70d33940cbca4722983aad86d88b [file] [log] [blame]
Steve Blocka7e24c12009-10-30 11:49:00 +00001#!/usr/bin/env python
2#
3# Copyright 2008 the V8 project authors. All rights reserved.
4# Redistribution and use in source and binary forms, with or without
5# modification, are permitted provided that the following conditions are
6# met:
7#
8# * Redistributions of source code must retain the above copyright
9# notice, this list of conditions and the following disclaimer.
10# * Redistributions in binary form must reproduce the above
11# copyright notice, this list of conditions and the following
12# disclaimer in the documentation and/or other materials provided
13# with the distribution.
14# * Neither the name of Google Inc. nor the names of its
15# contributors may be used to endorse or promote products derived
16# from this software without specific prior written permission.
17#
18# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30
31import optparse
32import os
33from os.path import abspath, join, dirname, basename, exists
34import re
35import sys
36import subprocess
37
38# Disabled LINT rules and reason.
39# build/include_what_you_use: Started giving false positives for variables
40# named "string" and "map" assuming that you needed to include STL headers.
41
42ENABLED_LINT_RULES = """
43build/class
44build/deprecated
45build/endif_comment
46build/forward_decl
47build/include_order
48build/printf_format
49build/storage_class
50legal/copyright
51readability/boost
52readability/braces
53readability/casting
54readability/check
55readability/constructors
56readability/fn_size
57readability/function
58readability/multiline_comment
59readability/multiline_string
60readability/streams
61readability/todo
62readability/utf8
63runtime/arrays
64runtime/casting
65runtime/deprecated_fn
66runtime/explicit
67runtime/int
68runtime/memset
69runtime/mutex
70runtime/nonconf
71runtime/printf
72runtime/printf_format
73runtime/references
74runtime/rtti
75runtime/sizeof
76runtime/string
77runtime/virtual
78runtime/vlog
79whitespace/blank_line
80whitespace/braces
81whitespace/comma
82whitespace/comments
83whitespace/end_of_line
84whitespace/ending_newline
85whitespace/indent
86whitespace/labels
87whitespace/line_length
88whitespace/newline
89whitespace/operators
90whitespace/parens
91whitespace/tab
92whitespace/todo
93""".split()
94
95
96class SourceFileProcessor(object):
97 """
98 Utility class that can run through a directory structure, find all relevant
99 files and invoke a custom check on the files.
100 """
101
102 def Run(self, path):
103 all_files = []
104 for file in self.GetPathsToSearch():
105 all_files += self.FindFilesIn(join(path, file))
106 if not self.ProcessFiles(all_files, path):
107 return False
108 return True
109
110 def IgnoreDir(self, name):
111 return name.startswith('.') or name == 'data'
112
113 def IgnoreFile(self, name):
114 return name.startswith('.')
115
116 def FindFilesIn(self, path):
117 result = []
118 for (root, dirs, files) in os.walk(path):
119 for ignored in [x for x in dirs if self.IgnoreDir(x)]:
120 dirs.remove(ignored)
121 for file in files:
122 if not self.IgnoreFile(file) and self.IsRelevant(file):
123 result.append(join(root, file))
124 return result
125
126
127class CppLintProcessor(SourceFileProcessor):
128 """
129 Lint files to check that they follow the google code style.
130 """
131
132 def IsRelevant(self, name):
133 return name.endswith('.cc') or name.endswith('.h')
134
135 def IgnoreDir(self, name):
136 return (super(CppLintProcessor, self).IgnoreDir(name)
137 or (name == 'third_party'))
138
139 IGNORE_LINT = ['flag-definitions.h']
140
141 def IgnoreFile(self, name):
142 return (super(CppLintProcessor, self).IgnoreFile(name)
143 or (name in CppLintProcessor.IGNORE_LINT))
144
145 def GetPathsToSearch(self):
146 return ['src', 'public', 'samples', join('test', 'cctest')]
147
148 def ProcessFiles(self, files, path):
149 filt = '-,' + ",".join(['+' + n for n in ENABLED_LINT_RULES])
150 command = ['cpplint.py', '--filter', filt] + join(files)
151 local_cpplint = join(path, "tools", "cpplint.py")
152 if exists(local_cpplint):
153 command = ['python', local_cpplint, '--filter', filt] + join(files)
154 process = subprocess.Popen(command)
155 return process.wait() == 0
156
157
158COPYRIGHT_HEADER_PATTERN = re.compile(
159 r'Copyright [\d-]*200[8-9] the V8 project authors. All rights reserved.')
160
161class SourceProcessor(SourceFileProcessor):
162 """
163 Check that all files include a copyright notice.
164 """
165
166 RELEVANT_EXTENSIONS = ['.js', '.cc', '.h', '.py', '.c', 'SConscript',
167 'SConstruct', '.status']
168 def IsRelevant(self, name):
169 for ext in SourceProcessor.RELEVANT_EXTENSIONS:
170 if name.endswith(ext):
171 return True
172 return False
173
174 def GetPathsToSearch(self):
175 return ['.']
176
177 def IgnoreDir(self, name):
178 return (super(SourceProcessor, self).IgnoreDir(name)
179 or (name == 'third_party')
180 or (name == 'obj'))
181
182 IGNORE_COPYRIGHTS = ['earley-boyer.js', 'raytrace.js', 'crypto.js',
183 'libraries.cc', 'libraries-empty.cc', 'jsmin.py', 'regexp-pcre.js']
184 IGNORE_TABS = IGNORE_COPYRIGHTS + ['unicode-test.js',
185 'html-comments.js']
186
187 def ProcessContents(self, name, contents):
188 result = True
189 base = basename(name)
190 if not base in SourceProcessor.IGNORE_TABS:
191 if '\t' in contents:
192 print "%s contains tabs" % name
193 result = False
194 if not base in SourceProcessor.IGNORE_COPYRIGHTS:
195 if not COPYRIGHT_HEADER_PATTERN.search(contents):
196 print "%s is missing a correct copyright header." % name
197 result = False
198 return result
199
200 def ProcessFiles(self, files, path):
201 success = True
202 for file in files:
203 try:
204 handle = open(file)
205 contents = handle.read()
206 success = self.ProcessContents(file, contents) and success
207 finally:
208 handle.close()
209 return success
210
211
212def GetOptions():
213 result = optparse.OptionParser()
214 result.add_option('--no-lint', help="Do not run cpplint", default=False,
215 action="store_true")
216 return result
217
218
219def Main():
220 workspace = abspath(join(dirname(sys.argv[0]), '..'))
221 parser = GetOptions()
222 (options, args) = parser.parse_args()
223 success = True
224 if not options.no_lint:
225 success = CppLintProcessor().Run(workspace) and success
226 success = SourceProcessor().Run(workspace) and success
227 if success:
228 return 0
229 else:
230 return 1
231
232
233if __name__ == '__main__':
234 sys.exit(Main())