blob: db1bf00eb3c6cfadd21d0e809b699d95b933283a [file] [log] [blame]
Nico Weber077f1a32015-08-06 15:08:57 -07001# Copyright 2015 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""Presubmit script for pdfium.
6
7See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
8for more details about the presubmit API built into depot_tools.
9"""
10
Dan Sinclair22d66072016-02-22 11:56:05 -050011LINT_FILTERS = [
Dan Sinclair3ebd1212016-03-09 09:59:23 -050012 # Rvalue ref checks are unreliable.
dan sinclaird2019df2016-02-22 22:32:03 -050013 '-build/c++11',
Dan Sinclair3ebd1212016-03-09 09:59:23 -050014 # Need to fix header names not matching cpp names.
dan sinclaird2019df2016-02-22 22:32:03 -050015 '-build/include_order',
Dan Sinclair3ebd1212016-03-09 09:59:23 -050016 # Too many to fix at the moment.
dan sinclaird2019df2016-02-22 22:32:03 -050017 '-readability/casting',
Dan Sinclair3ebd1212016-03-09 09:59:23 -050018 # Need to refactor large methods to fix.
dan sinclaird2019df2016-02-22 22:32:03 -050019 '-readability/fn_size',
Dan Sinclair3ebd1212016-03-09 09:59:23 -050020 # Lots of usage to fix first.
dan sinclaird2019df2016-02-22 22:32:03 -050021 '-runtime/int',
Dan Sinclair3ebd1212016-03-09 09:59:23 -050022 # Lots of non-const references need to be fixed
dan sinclaird2019df2016-02-22 22:32:03 -050023 '-runtime/references',
Dan Sinclair3ebd1212016-03-09 09:59:23 -050024 # We are not thread safe, so this will never pass.
dan sinclaird2019df2016-02-22 22:32:03 -050025 '-runtime/threadsafe_fn',
Dan Sinclair3ebd1212016-03-09 09:59:23 -050026 # Figure out how to deal with #defines that git cl format creates.
dan sinclaird2019df2016-02-22 22:32:03 -050027 '-whitespace/indent',
Dan Sinclair22d66072016-02-22 11:56:05 -050028]
29
Dan Sinclair544bbc62016-03-14 15:07:39 -040030
dsinclair2ca2da52016-09-13 18:10:34 -070031_INCLUDE_ORDER_WARNING = (
32 'Your #include order seems to be broken. Remember to use the right '
33 'collation (LC_COLLATE=C) and check\nhttps://google.github.io/styleguide/'
34 'cppguide.html#Names_and_Order_of_Includes')
35
36
Dan Sinclair544bbc62016-03-14 15:07:39 -040037def _CheckUnwantedDependencies(input_api, output_api):
38 """Runs checkdeps on #include statements added in this
39 change. Breaking - rules is an error, breaking ! rules is a
40 warning.
41 """
42 import sys
43 # We need to wait until we have an input_api object and use this
44 # roundabout construct to import checkdeps because this file is
45 # eval-ed and thus doesn't have __file__.
46 original_sys_path = sys.path
47 try:
48 sys.path = sys.path + [input_api.os_path.join(
49 input_api.PresubmitLocalPath(), 'buildtools', 'checkdeps')]
50 import checkdeps
51 from cpp_checker import CppChecker
52 from rules import Rule
dsinclair4cd49e12016-04-05 10:28:48 -070053 except ImportError:
54 return [output_api.PresubmitError(
55 'Unable to run checkdeps, does pdfium/buildtools/checkdeps exist?')]
Dan Sinclair544bbc62016-03-14 15:07:39 -040056 finally:
57 # Restore sys.path to what it was before.
58 sys.path = original_sys_path
59
60 added_includes = []
61 for f in input_api.AffectedFiles():
62 if not CppChecker.IsCppFile(f.LocalPath()):
63 continue
64
65 changed_lines = [line for line_num, line in f.ChangedContents()]
66 added_includes.append([f.LocalPath(), changed_lines])
67
68 deps_checker = checkdeps.DepsChecker(input_api.PresubmitLocalPath())
69
70 error_descriptions = []
71 warning_descriptions = []
72 for path, rule_type, rule_description in deps_checker.CheckAddedCppIncludes(
73 added_includes):
74 description_with_path = '%s\n %s' % (path, rule_description)
75 if rule_type == Rule.DISALLOW:
76 error_descriptions.append(description_with_path)
77 else:
78 warning_descriptions.append(description_with_path)
79
80 results = []
81 if error_descriptions:
82 results.append(output_api.PresubmitError(
83 'You added one or more #includes that violate checkdeps rules.',
84 error_descriptions))
85 if warning_descriptions:
86 results.append(output_api.PresubmitPromptOrNotify(
87 'You added one or more #includes of files that are temporarily\n'
88 'allowed but being removed. Can you avoid introducing the\n'
89 '#include? See relevant DEPS file(s) for details and contacts.',
90 warning_descriptions))
91 return results
92
93
dsinclair2ca2da52016-09-13 18:10:34 -070094def _CheckIncludeOrderForScope(scope, input_api, file_path, changed_linenums):
95 """Checks that the lines in scope occur in the right order.
96
97 1. C system files in alphabetical order
98 2. C++ system files in alphabetical order
99 3. Project's .h files
100 """
101
102 c_system_include_pattern = input_api.re.compile(r'\s*#include <.*\.h>')
103 cpp_system_include_pattern = input_api.re.compile(r'\s*#include <.*>')
104 custom_include_pattern = input_api.re.compile(r'\s*#include ".*')
105
106 C_SYSTEM_INCLUDES, CPP_SYSTEM_INCLUDES, CUSTOM_INCLUDES = range(3)
107
108 state = C_SYSTEM_INCLUDES
109
110 previous_line = ''
111 previous_line_num = 0
112 problem_linenums = []
113 out_of_order = " - line belongs before previous line"
114 for line_num, line in scope:
115 if c_system_include_pattern.match(line):
116 if state != C_SYSTEM_INCLUDES:
117 problem_linenums.append((line_num, previous_line_num,
118 " - C system include file in wrong block"))
119 elif previous_line and previous_line > line:
120 problem_linenums.append((line_num, previous_line_num,
121 out_of_order))
122 elif cpp_system_include_pattern.match(line):
123 if state == C_SYSTEM_INCLUDES:
124 state = CPP_SYSTEM_INCLUDES
125 elif state == CUSTOM_INCLUDES:
126 problem_linenums.append((line_num, previous_line_num,
127 " - c++ system include file in wrong block"))
128 elif previous_line and previous_line > line:
129 problem_linenums.append((line_num, previous_line_num, out_of_order))
130 elif custom_include_pattern.match(line):
131 if state != CUSTOM_INCLUDES:
132 state = CUSTOM_INCLUDES
133 elif previous_line and previous_line > line:
134 problem_linenums.append((line_num, previous_line_num, out_of_order))
135 else:
136 problem_linenums.append((line_num, previous_line_num,
137 "Unknown include type"))
138 previous_line = line
139 previous_line_num = line_num
140
141 warnings = []
142 for (line_num, previous_line_num, failure_type) in problem_linenums:
143 if line_num in changed_linenums or previous_line_num in changed_linenums:
144 warnings.append(' %s:%d:%s' % (file_path, line_num, failure_type))
145 return warnings
146
147
148def _CheckIncludeOrderInFile(input_api, f, changed_linenums):
149 """Checks the #include order for the given file f."""
150
151 system_include_pattern = input_api.re.compile(r'\s*#include \<.*')
152 # Exclude the following includes from the check:
153 # 1) #include <.../...>, e.g., <sys/...> includes often need to appear in a
154 # specific order.
155 # 2) <atlbase.h>, "build/build_config.h"
156 excluded_include_pattern = input_api.re.compile(
157 r'\s*#include (\<.*/.*|\<atlbase\.h\>|"build/build_config.h")')
158 custom_include_pattern = input_api.re.compile(r'\s*#include "(?P<FILE>.*)"')
159 # Match the final or penultimate token if it is xxxtest so we can ignore it
160 # when considering the special first include.
161 test_file_tag_pattern = input_api.re.compile(
162 r'_[a-z]+test(?=(_[a-zA-Z0-9]+)?\.)')
163 if_pattern = input_api.re.compile(
164 r'\s*#\s*(if|elif|else|endif|define|undef).*')
165 # Some files need specialized order of includes; exclude such files from this
166 # check.
167 uncheckable_includes_pattern = input_api.re.compile(
168 r'\s*#include '
169 '("ipc/.*macros\.h"|<windows\.h>|".*gl.*autogen.h")\s*')
170
171 contents = f.NewContents()
172 warnings = []
173 line_num = 0
174
175 # Handle the special first include. If the first include file is
176 # some/path/file.h, the corresponding including file can be some/path/file.cc,
177 # some/other/path/file.cc, some/path/file_platform.cc, some/path/file-suffix.h
178 # etc. It's also possible that no special first include exists.
179 # If the included file is some/path/file_platform.h the including file could
180 # also be some/path/file_xxxtest_platform.h.
181 including_file_base_name = test_file_tag_pattern.sub(
182 '', input_api.os_path.basename(f.LocalPath()))
183
184 for line in contents:
185 line_num += 1
186 if system_include_pattern.match(line):
187 # No special first include -> process the line again along with normal
188 # includes.
189 line_num -= 1
190 break
191 match = custom_include_pattern.match(line)
192 if match:
193 match_dict = match.groupdict()
194 header_basename = test_file_tag_pattern.sub(
195 '', input_api.os_path.basename(match_dict['FILE'])).replace('.h', '')
196
197 if header_basename not in including_file_base_name:
198 # No special first include -> process the line again along with normal
199 # includes.
200 line_num -= 1
201 break
202
203 # Split into scopes: Each region between #if and #endif is its own scope.
204 scopes = []
205 current_scope = []
206 for line in contents[line_num:]:
207 line_num += 1
208 if uncheckable_includes_pattern.match(line):
209 continue
210 if if_pattern.match(line):
211 scopes.append(current_scope)
212 current_scope = []
213 elif ((system_include_pattern.match(line) or
214 custom_include_pattern.match(line)) and
215 not excluded_include_pattern.match(line)):
216 current_scope.append((line_num, line))
217 scopes.append(current_scope)
218
219 for scope in scopes:
220 warnings.extend(_CheckIncludeOrderForScope(scope, input_api, f.LocalPath(),
221 changed_linenums))
222 return warnings
223
224
225def _CheckIncludeOrder(input_api, output_api):
226 """Checks that the #include order is correct.
227
228 1. The corresponding header for source files.
229 2. C system files in alphabetical order
230 3. C++ system files in alphabetical order
231 4. Project's .h files in alphabetical order
232
233 Each region separated by #if, #elif, #else, #endif, #define and #undef follows
234 these rules separately.
235 """
236 def FileFilterIncludeOrder(affected_file):
237 black_list = (input_api.DEFAULT_BLACK_LIST)
238 return input_api.FilterSourceFile(affected_file, black_list=black_list)
239
240 warnings = []
241 for f in input_api.AffectedFiles(file_filter=FileFilterIncludeOrder):
dsinclaird33c8e32016-11-21 13:31:16 -0800242 if f.LocalPath().endswith(('.cc', '.cpp', '.h', '.mm')):
dsinclair2ca2da52016-09-13 18:10:34 -0700243 changed_linenums = set(line_num for line_num, _ in f.ChangedContents())
244 warnings.extend(_CheckIncludeOrderInFile(input_api, f, changed_linenums))
245
246 results = []
247 if warnings:
248 results.append(output_api.PresubmitPromptOrNotify(_INCLUDE_ORDER_WARNING,
249 warnings))
250 return results
251
252
Nico Weber077f1a32015-08-06 15:08:57 -0700253def CheckChangeOnUpload(input_api, output_api):
254 results = []
Dan Sinclair544bbc62016-03-14 15:07:39 -0400255 results += _CheckUnwantedDependencies(input_api, output_api)
Nico Weber077f1a32015-08-06 15:08:57 -0700256 results += input_api.canned_checks.CheckPatchFormatted(input_api, output_api)
Dan Sinclair22d66072016-02-22 11:56:05 -0500257 results += input_api.canned_checks.CheckChangeLintsClean(
258 input_api, output_api, None, LINT_FILTERS)
dsinclair2ca2da52016-09-13 18:10:34 -0700259 results += _CheckIncludeOrder(input_api, output_api)
Dan Sinclair544bbc62016-03-14 15:07:39 -0400260
Nico Weber077f1a32015-08-06 15:08:57 -0700261 return results