1a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block#!/usr/bin/env python
2a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block#
33ef787dbeca8a5fb1086949cda830dccee07bfbdBen Murdoch# Copyright 2012 the V8 project authors. All rights reserved.
4a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block# Redistribution and use in source and binary forms, with or without
5a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block# modification, are permitted provided that the following conditions are
6a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block# met:
7a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block#
8a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block#     * Redistributions of source code must retain the above copyright
9a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block#       notice, this list of conditions and the following disclaimer.
10a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block#     * Redistributions in binary form must reproduce the above
11a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block#       copyright notice, this list of conditions and the following
12a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block#       disclaimer in the documentation and/or other materials provided
13a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block#       with the distribution.
14a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block#     * Neither the name of Google Inc. nor the names of its
15a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block#       contributors may be used to endorse or promote products derived
16a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block#       from this software without specific prior written permission.
17a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block#
18a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
30bb769b257e753aafcbd96767abb2abc645eaa20cBen Murdochtry:
31bb769b257e753aafcbd96767abb2abc645eaa20cBen Murdoch  import hashlib
32bb769b257e753aafcbd96767abb2abc645eaa20cBen Murdoch  md5er = hashlib.md5
33bb769b257e753aafcbd96767abb2abc645eaa20cBen Murdochexcept ImportError, e:
34bb769b257e753aafcbd96767abb2abc645eaa20cBen Murdoch  import md5
35bb769b257e753aafcbd96767abb2abc645eaa20cBen Murdoch  md5er = md5.new
36bb769b257e753aafcbd96767abb2abc645eaa20cBen Murdoch
37a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
38a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockimport optparse
39a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockimport os
40a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockfrom os.path import abspath, join, dirname, basename, exists
41d0582a6c46733687d045e4188a1bcd0123c758a1Steve Blockimport pickle
42a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockimport re
43a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockimport sys
44a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockimport subprocess
453ef787dbeca8a5fb1086949cda830dccee07bfbdBen Murdochimport multiprocessing
46589d6979ff2ef66fca2d8fa51404c369ca5e9250Ben Murdochfrom subprocess import PIPE
47a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
48a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block# Disabled LINT rules and reason.
49a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block# build/include_what_you_use: Started giving false positives for variables
50a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block#  named "string" and "map" assuming that you needed to include STL headers.
51a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
52a7e24c173cf37484693b9abb38e494fa7bd7baebSteve BlockENABLED_LINT_RULES = """
53a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockbuild/class
54a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockbuild/deprecated
55a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockbuild/endif_comment
56a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockbuild/forward_decl
57a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockbuild/include_order
58a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockbuild/printf_format
59a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockbuild/storage_class
60a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blocklegal/copyright
61a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockreadability/boost
62a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockreadability/braces
63a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockreadability/casting
64a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockreadability/check
65a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockreadability/constructors
66a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockreadability/fn_size
67a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockreadability/function
68a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockreadability/multiline_comment
69a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockreadability/multiline_string
70a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockreadability/streams
71a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockreadability/todo
72a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockreadability/utf8
73a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockruntime/arrays
74a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockruntime/casting
75a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockruntime/deprecated_fn
76a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockruntime/explicit
77a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockruntime/int
78a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockruntime/memset
79a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockruntime/mutex
80a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockruntime/nonconf
81a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockruntime/printf
82a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockruntime/printf_format
83a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockruntime/references
84a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockruntime/rtti
85a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockruntime/sizeof
86a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockruntime/string
87a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockruntime/virtual
88a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockruntime/vlog
89a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockwhitespace/blank_line
90a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockwhitespace/braces
91a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockwhitespace/comma
92a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockwhitespace/comments
93a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockwhitespace/ending_newline
94a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockwhitespace/indent
95a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockwhitespace/labels
96a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockwhitespace/line_length
97a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockwhitespace/newline
98a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockwhitespace/operators
99a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockwhitespace/parens
100a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockwhitespace/tab
101a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockwhitespace/todo
102a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block""".split()
103a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
104a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
1053ef787dbeca8a5fb1086949cda830dccee07bfbdBen MurdochLINT_OUTPUT_PATTERN = re.compile(r'^.+[:(]\d+[:)]|^Done processing')
1063ef787dbeca8a5fb1086949cda830dccee07bfbdBen Murdoch
1073ef787dbeca8a5fb1086949cda830dccee07bfbdBen Murdoch
1083ef787dbeca8a5fb1086949cda830dccee07bfbdBen Murdochdef CppLintWorker(command):
1093ef787dbeca8a5fb1086949cda830dccee07bfbdBen Murdoch  try:
1103ef787dbeca8a5fb1086949cda830dccee07bfbdBen Murdoch    process = subprocess.Popen(command, stderr=subprocess.PIPE)
1113ef787dbeca8a5fb1086949cda830dccee07bfbdBen Murdoch    process.wait()
1123ef787dbeca8a5fb1086949cda830dccee07bfbdBen Murdoch    out_lines = ""
1133ef787dbeca8a5fb1086949cda830dccee07bfbdBen Murdoch    error_count = -1
1143ef787dbeca8a5fb1086949cda830dccee07bfbdBen Murdoch    while True:
1153ef787dbeca8a5fb1086949cda830dccee07bfbdBen Murdoch      out_line = process.stderr.readline()
1163ef787dbeca8a5fb1086949cda830dccee07bfbdBen Murdoch      if out_line == '' and process.poll() != None:
1173ef787dbeca8a5fb1086949cda830dccee07bfbdBen Murdoch        break
1183ef787dbeca8a5fb1086949cda830dccee07bfbdBen Murdoch      m = LINT_OUTPUT_PATTERN.match(out_line)
1193ef787dbeca8a5fb1086949cda830dccee07bfbdBen Murdoch      if m:
1203ef787dbeca8a5fb1086949cda830dccee07bfbdBen Murdoch        out_lines += out_line
1213ef787dbeca8a5fb1086949cda830dccee07bfbdBen Murdoch        error_count += 1
1223ef787dbeca8a5fb1086949cda830dccee07bfbdBen Murdoch    sys.stderr.write(out_lines)
1233ef787dbeca8a5fb1086949cda830dccee07bfbdBen Murdoch    return error_count
1243ef787dbeca8a5fb1086949cda830dccee07bfbdBen Murdoch  except KeyboardInterrupt:
1253ef787dbeca8a5fb1086949cda830dccee07bfbdBen Murdoch    process.kill()
1263ef787dbeca8a5fb1086949cda830dccee07bfbdBen Murdoch  except:
1273ef787dbeca8a5fb1086949cda830dccee07bfbdBen Murdoch    print('Error running cpplint.py. Please make sure you have depot_tools' +
1283ef787dbeca8a5fb1086949cda830dccee07bfbdBen Murdoch          ' in your $PATH. Lint check skipped.')
1293ef787dbeca8a5fb1086949cda830dccee07bfbdBen Murdoch    process.kill()
1303ef787dbeca8a5fb1086949cda830dccee07bfbdBen Murdoch
1313ef787dbeca8a5fb1086949cda830dccee07bfbdBen Murdoch
132d0582a6c46733687d045e4188a1bcd0123c758a1Steve Blockclass FileContentsCache(object):
133d0582a6c46733687d045e4188a1bcd0123c758a1Steve Block
134d0582a6c46733687d045e4188a1bcd0123c758a1Steve Block  def __init__(self, sums_file_name):
135d0582a6c46733687d045e4188a1bcd0123c758a1Steve Block    self.sums = {}
136d0582a6c46733687d045e4188a1bcd0123c758a1Steve Block    self.sums_file_name = sums_file_name
137d0582a6c46733687d045e4188a1bcd0123c758a1Steve Block
138d0582a6c46733687d045e4188a1bcd0123c758a1Steve Block  def Load(self):
139d0582a6c46733687d045e4188a1bcd0123c758a1Steve Block    try:
140d0582a6c46733687d045e4188a1bcd0123c758a1Steve Block      sums_file = None
141d0582a6c46733687d045e4188a1bcd0123c758a1Steve Block      try:
142d0582a6c46733687d045e4188a1bcd0123c758a1Steve Block        sums_file = open(self.sums_file_name, 'r')
143d0582a6c46733687d045e4188a1bcd0123c758a1Steve Block        self.sums = pickle.load(sums_file)
144d0582a6c46733687d045e4188a1bcd0123c758a1Steve Block      except IOError:
145d0582a6c46733687d045e4188a1bcd0123c758a1Steve Block        # File might not exist, this is OK.
146d0582a6c46733687d045e4188a1bcd0123c758a1Steve Block        pass
147d0582a6c46733687d045e4188a1bcd0123c758a1Steve Block    finally:
148d0582a6c46733687d045e4188a1bcd0123c758a1Steve Block      if sums_file:
149d0582a6c46733687d045e4188a1bcd0123c758a1Steve Block        sums_file.close()
150d0582a6c46733687d045e4188a1bcd0123c758a1Steve Block
151d0582a6c46733687d045e4188a1bcd0123c758a1Steve Block  def Save(self):
152d0582a6c46733687d045e4188a1bcd0123c758a1Steve Block    try:
153d0582a6c46733687d045e4188a1bcd0123c758a1Steve Block      sums_file = open(self.sums_file_name, 'w')
154d0582a6c46733687d045e4188a1bcd0123c758a1Steve Block      pickle.dump(self.sums, sums_file)
155d0582a6c46733687d045e4188a1bcd0123c758a1Steve Block    finally:
156d0582a6c46733687d045e4188a1bcd0123c758a1Steve Block      sums_file.close()
157d0582a6c46733687d045e4188a1bcd0123c758a1Steve Block
158d0582a6c46733687d045e4188a1bcd0123c758a1Steve Block  def FilterUnchangedFiles(self, files):
159d0582a6c46733687d045e4188a1bcd0123c758a1Steve Block    changed_or_new = []
160d0582a6c46733687d045e4188a1bcd0123c758a1Steve Block    for file in files:
161d0582a6c46733687d045e4188a1bcd0123c758a1Steve Block      try:
162d0582a6c46733687d045e4188a1bcd0123c758a1Steve Block        handle = open(file, "r")
163bb769b257e753aafcbd96767abb2abc645eaa20cBen Murdoch        file_sum = md5er(handle.read()).digest()
164d0582a6c46733687d045e4188a1bcd0123c758a1Steve Block        if not file in self.sums or self.sums[file] != file_sum:
165d0582a6c46733687d045e4188a1bcd0123c758a1Steve Block          changed_or_new.append(file)
166d0582a6c46733687d045e4188a1bcd0123c758a1Steve Block          self.sums[file] = file_sum
167d0582a6c46733687d045e4188a1bcd0123c758a1Steve Block      finally:
168d0582a6c46733687d045e4188a1bcd0123c758a1Steve Block        handle.close()
169d0582a6c46733687d045e4188a1bcd0123c758a1Steve Block    return changed_or_new
170d0582a6c46733687d045e4188a1bcd0123c758a1Steve Block
171d0582a6c46733687d045e4188a1bcd0123c758a1Steve Block  def RemoveFile(self, file):
172d0582a6c46733687d045e4188a1bcd0123c758a1Steve Block    if file in self.sums:
173d0582a6c46733687d045e4188a1bcd0123c758a1Steve Block      self.sums.pop(file)
174d0582a6c46733687d045e4188a1bcd0123c758a1Steve Block
175d0582a6c46733687d045e4188a1bcd0123c758a1Steve Block
176a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockclass SourceFileProcessor(object):
177a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  """
178a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  Utility class that can run through a directory structure, find all relevant
179a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  files and invoke a custom check on the files.
180a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  """
181a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
182a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  def Run(self, path):
183a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    all_files = []
184a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    for file in self.GetPathsToSearch():
185a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block      all_files += self.FindFilesIn(join(path, file))
186a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    if not self.ProcessFiles(all_files, path):
187a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block      return False
188a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    return True
189a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
190a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  def IgnoreDir(self, name):
191d0582a6c46733687d045e4188a1bcd0123c758a1Steve Block    return name.startswith('.') or name == 'data' or name == 'sputniktests'
192a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
193a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  def IgnoreFile(self, name):
194a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    return name.startswith('.')
195a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
196a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  def FindFilesIn(self, path):
197a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    result = []
198a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    for (root, dirs, files) in os.walk(path):
199a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block      for ignored in [x for x in dirs if self.IgnoreDir(x)]:
200a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block        dirs.remove(ignored)
201a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block      for file in files:
202a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block        if not self.IgnoreFile(file) and self.IsRelevant(file):
203a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block          result.append(join(root, file))
204a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    return result
205a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
206a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
207a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockclass CppLintProcessor(SourceFileProcessor):
208a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  """
209a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  Lint files to check that they follow the google code style.
210a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  """
211a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
212a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  def IsRelevant(self, name):
213a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    return name.endswith('.cc') or name.endswith('.h')
214a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
215a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  def IgnoreDir(self, name):
216a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    return (super(CppLintProcessor, self).IgnoreDir(name)
217a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block              or (name == 'third_party'))
218a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
219a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  IGNORE_LINT = ['flag-definitions.h']
220d0582a6c46733687d045e4188a1bcd0123c758a1Steve Block
221a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  def IgnoreFile(self, name):
222a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    return (super(CppLintProcessor, self).IgnoreFile(name)
223a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block              or (name in CppLintProcessor.IGNORE_LINT))
224a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
225a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  def GetPathsToSearch(self):
2268a31eba00023874d4a1dcdc5f411cc4336776874Shimeng (Simon) Wang    return ['src', 'preparser', 'include', 'samples', join('test', 'cctest')]
227a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
228a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  def ProcessFiles(self, files, path):
229d0582a6c46733687d045e4188a1bcd0123c758a1Steve Block    good_files_cache = FileContentsCache('.cpplint-cache')
230d0582a6c46733687d045e4188a1bcd0123c758a1Steve Block    good_files_cache.Load()
231d0582a6c46733687d045e4188a1bcd0123c758a1Steve Block    files = good_files_cache.FilterUnchangedFiles(files)
232d0582a6c46733687d045e4188a1bcd0123c758a1Steve Block    if len(files) == 0:
233d0582a6c46733687d045e4188a1bcd0123c758a1Steve Block      print 'No changes in files detected. Skipping cpplint check.'
234d0582a6c46733687d045e4188a1bcd0123c758a1Steve Block      return True
235d0582a6c46733687d045e4188a1bcd0123c758a1Steve Block
236a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    filt = '-,' + ",".join(['+' + n for n in ENABLED_LINT_RULES])
2373ef787dbeca8a5fb1086949cda830dccee07bfbdBen Murdoch    command = ['cpplint.py', '--filter', filt]
238a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    local_cpplint = join(path, "tools", "cpplint.py")
239a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    if exists(local_cpplint):
2403ef787dbeca8a5fb1086949cda830dccee07bfbdBen Murdoch      command = ['python', local_cpplint, '--filter', filt]
241c7cc028aaeedbbfa11c11d0b7b243b3d9e837ed9Ben Murdoch
2423ef787dbeca8a5fb1086949cda830dccee07bfbdBen Murdoch    commands = join([command + [file] for file in files])
2433ef787dbeca8a5fb1086949cda830dccee07bfbdBen Murdoch    count = multiprocessing.cpu_count()
2443ef787dbeca8a5fb1086949cda830dccee07bfbdBen Murdoch    pool = multiprocessing.Pool(count)
2453ef787dbeca8a5fb1086949cda830dccee07bfbdBen Murdoch    try:
2463ef787dbeca8a5fb1086949cda830dccee07bfbdBen Murdoch      results = pool.map_async(CppLintWorker, commands).get(999999)
2473ef787dbeca8a5fb1086949cda830dccee07bfbdBen Murdoch    except KeyboardInterrupt:
2483ef787dbeca8a5fb1086949cda830dccee07bfbdBen Murdoch      print "\nCaught KeyboardInterrupt, terminating workers."
2493ef787dbeca8a5fb1086949cda830dccee07bfbdBen Murdoch      sys.exit(1)
2503ef787dbeca8a5fb1086949cda830dccee07bfbdBen Murdoch
2513ef787dbeca8a5fb1086949cda830dccee07bfbdBen Murdoch    for i in range(len(files)):
2523ef787dbeca8a5fb1086949cda830dccee07bfbdBen Murdoch      if results[i] > 0:
2533ef787dbeca8a5fb1086949cda830dccee07bfbdBen Murdoch        good_files_cache.RemoveFile(files[i])
254d0582a6c46733687d045e4188a1bcd0123c758a1Steve Block
2553ef787dbeca8a5fb1086949cda830dccee07bfbdBen Murdoch    total_errors = sum(results)
2563ef787dbeca8a5fb1086949cda830dccee07bfbdBen Murdoch    print "Total errors found: %d" % total_errors
257d0582a6c46733687d045e4188a1bcd0123c758a1Steve Block    good_files_cache.Save()
2583ef787dbeca8a5fb1086949cda830dccee07bfbdBen Murdoch    return total_errors == 0
259a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
260a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
261a7e24c173cf37484693b9abb38e494fa7bd7baebSteve BlockCOPYRIGHT_HEADER_PATTERN = re.compile(
262e46be819fca9468a0cd4e74859ce0f778eb8ca60Leon Clarke    r'Copyright [\d-]*20[0-1][0-9] the V8 project authors. All rights reserved.')
263a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
264a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockclass SourceProcessor(SourceFileProcessor):
265a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  """
266589d6979ff2ef66fca2d8fa51404c369ca5e9250Ben Murdoch  Check that all files include a copyright notice and no trailing whitespaces.
267a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  """
268a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
269a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  RELEVANT_EXTENSIONS = ['.js', '.cc', '.h', '.py', '.c', 'SConscript',
270589d6979ff2ef66fca2d8fa51404c369ca5e9250Ben Murdoch      'SConstruct', '.status', '.gyp', '.gypi']
271589d6979ff2ef66fca2d8fa51404c369ca5e9250Ben Murdoch
272589d6979ff2ef66fca2d8fa51404c369ca5e9250Ben Murdoch  # Overwriting the one in the parent class.
273589d6979ff2ef66fca2d8fa51404c369ca5e9250Ben Murdoch  def FindFilesIn(self, path):
274589d6979ff2ef66fca2d8fa51404c369ca5e9250Ben Murdoch    if os.path.exists(path+'/.git'):
275589d6979ff2ef66fca2d8fa51404c369ca5e9250Ben Murdoch      output = subprocess.Popen('git ls-files --full-name',
276589d6979ff2ef66fca2d8fa51404c369ca5e9250Ben Murdoch                                stdout=PIPE, cwd=path, shell=True)
277589d6979ff2ef66fca2d8fa51404c369ca5e9250Ben Murdoch      result = []
278589d6979ff2ef66fca2d8fa51404c369ca5e9250Ben Murdoch      for file in output.stdout.read().split():
279589d6979ff2ef66fca2d8fa51404c369ca5e9250Ben Murdoch        for dir_part in os.path.dirname(file).split(os.sep):
280589d6979ff2ef66fca2d8fa51404c369ca5e9250Ben Murdoch          if self.IgnoreDir(dir_part):
281589d6979ff2ef66fca2d8fa51404c369ca5e9250Ben Murdoch            break
282589d6979ff2ef66fca2d8fa51404c369ca5e9250Ben Murdoch        else:
283589d6979ff2ef66fca2d8fa51404c369ca5e9250Ben Murdoch          if self.IsRelevant(file) and not self.IgnoreFile(file):
284589d6979ff2ef66fca2d8fa51404c369ca5e9250Ben Murdoch            result.append(join(path, file))
285589d6979ff2ef66fca2d8fa51404c369ca5e9250Ben Murdoch      if output.wait() == 0:
286589d6979ff2ef66fca2d8fa51404c369ca5e9250Ben Murdoch        return result
287589d6979ff2ef66fca2d8fa51404c369ca5e9250Ben Murdoch    return super(SourceProcessor, self).FindFilesIn(path)
288589d6979ff2ef66fca2d8fa51404c369ca5e9250Ben Murdoch
289a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  def IsRelevant(self, name):
290a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    for ext in SourceProcessor.RELEVANT_EXTENSIONS:
291a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block      if name.endswith(ext):
292a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block        return True
293a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    return False
294a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
295a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  def GetPathsToSearch(self):
296a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    return ['.']
297a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
298a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  def IgnoreDir(self, name):
299a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    return (super(SourceProcessor, self).IgnoreDir(name)
300a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block              or (name == 'third_party')
30169a99ed0b2b2ef69d393c371b03db3a98aaf880eBen Murdoch              or (name == 'gyp')
30269a99ed0b2b2ef69d393c371b03db3a98aaf880eBen Murdoch              or (name == 'out')
303a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block              or (name == 'obj'))
304a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
30569a99ed0b2b2ef69d393c371b03db3a98aaf880eBen Murdoch  IGNORE_COPYRIGHTS = ['cpplint.py',
30669a99ed0b2b2ef69d393c371b03db3a98aaf880eBen Murdoch                       'earley-boyer.js',
30769a99ed0b2b2ef69d393c371b03db3a98aaf880eBen Murdoch                       'raytrace.js',
30869a99ed0b2b2ef69d393c371b03db3a98aaf880eBen Murdoch                       'crypto.js',
30969a99ed0b2b2ef69d393c371b03db3a98aaf880eBen Murdoch                       'libraries.cc',
31069a99ed0b2b2ef69d393c371b03db3a98aaf880eBen Murdoch                       'libraries-empty.cc',
31169a99ed0b2b2ef69d393c371b03db3a98aaf880eBen Murdoch                       'jsmin.py',
31269a99ed0b2b2ef69d393c371b03db3a98aaf880eBen Murdoch                       'regexp-pcre.js']
31369a99ed0b2b2ef69d393c371b03db3a98aaf880eBen Murdoch  IGNORE_TABS = IGNORE_COPYRIGHTS + ['unicode-test.js', 'html-comments.js']
314a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
315a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  def ProcessContents(self, name, contents):
316a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    result = True
317a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    base = basename(name)
318a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    if not base in SourceProcessor.IGNORE_TABS:
319a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block      if '\t' in contents:
320a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block        print "%s contains tabs" % name
321a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block        result = False
322a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    if not base in SourceProcessor.IGNORE_COPYRIGHTS:
323a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block      if not COPYRIGHT_HEADER_PATTERN.search(contents):
324a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block        print "%s is missing a correct copyright header." % name
325a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block        result = False
326589d6979ff2ef66fca2d8fa51404c369ca5e9250Ben Murdoch    ext = base.split('.').pop()
327589d6979ff2ef66fca2d8fa51404c369ca5e9250Ben Murdoch    if ' \n' in contents or contents.endswith(' '):
328589d6979ff2ef66fca2d8fa51404c369ca5e9250Ben Murdoch      line = 0
329589d6979ff2ef66fca2d8fa51404c369ca5e9250Ben Murdoch      lines = []
330589d6979ff2ef66fca2d8fa51404c369ca5e9250Ben Murdoch      parts = contents.split(' \n')
331589d6979ff2ef66fca2d8fa51404c369ca5e9250Ben Murdoch      if not contents.endswith(' '):
332589d6979ff2ef66fca2d8fa51404c369ca5e9250Ben Murdoch        parts.pop()
333589d6979ff2ef66fca2d8fa51404c369ca5e9250Ben Murdoch      for part in parts:
334589d6979ff2ef66fca2d8fa51404c369ca5e9250Ben Murdoch        line += part.count('\n') + 1
335589d6979ff2ef66fca2d8fa51404c369ca5e9250Ben Murdoch        lines.append(str(line))
336589d6979ff2ef66fca2d8fa51404c369ca5e9250Ben Murdoch      linenumbers = ', '.join(lines)
337589d6979ff2ef66fca2d8fa51404c369ca5e9250Ben Murdoch      if len(lines) > 1:
338589d6979ff2ef66fca2d8fa51404c369ca5e9250Ben Murdoch        print "%s has trailing whitespaces in lines %s." % (name, linenumbers)
339589d6979ff2ef66fca2d8fa51404c369ca5e9250Ben Murdoch      else:
340589d6979ff2ef66fca2d8fa51404c369ca5e9250Ben Murdoch        print "%s has trailing whitespaces in line %s." % (name, linenumbers)
341589d6979ff2ef66fca2d8fa51404c369ca5e9250Ben Murdoch      result = False
342a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    return result
343a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
344a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  def ProcessFiles(self, files, path):
345a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    success = True
346589d6979ff2ef66fca2d8fa51404c369ca5e9250Ben Murdoch    violations = 0
347a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    for file in files:
348a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block      try:
349a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block        handle = open(file)
350a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block        contents = handle.read()
351589d6979ff2ef66fca2d8fa51404c369ca5e9250Ben Murdoch        if not self.ProcessContents(file, contents):
352589d6979ff2ef66fca2d8fa51404c369ca5e9250Ben Murdoch          success = False
353589d6979ff2ef66fca2d8fa51404c369ca5e9250Ben Murdoch          violations += 1
354a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block      finally:
355a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block        handle.close()
356589d6979ff2ef66fca2d8fa51404c369ca5e9250Ben Murdoch    print "Total violating files: %s" % violations
357a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    return success
358a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
359a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
360a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockdef GetOptions():
361a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  result = optparse.OptionParser()
362a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  result.add_option('--no-lint', help="Do not run cpplint", default=False,
363a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block                    action="store_true")
364a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  return result
365a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
366a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
367a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockdef Main():
368a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  workspace = abspath(join(dirname(sys.argv[0]), '..'))
369a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  parser = GetOptions()
370a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  (options, args) = parser.parse_args()
371a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  success = True
372589d6979ff2ef66fca2d8fa51404c369ca5e9250Ben Murdoch  print "Running C++ lint check..."
373a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  if not options.no_lint:
374a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    success = CppLintProcessor().Run(workspace) and success
375589d6979ff2ef66fca2d8fa51404c369ca5e9250Ben Murdoch  print "Running copyright header and trailing whitespaces check..."
376a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  success = SourceProcessor().Run(workspace) and success
377a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  if success:
378a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    return 0
379a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  else:
380a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block    return 1
381a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
382a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block
383a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Blockif __name__ == '__main__':
384a7e24c173cf37484693b9abb38e494fa7bd7baebSteve Block  sys.exit(Main())
385