5"""Presubmit script for pdfium.
7See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
8for more details about the presubmit API built into depot_tools.
11PRESUBMIT_VERSION =
'2.0.0'
19 '-build/include_order',
21 '-readability/casting',
23 '-readability/fn_size',
27 '-runtime/references',
29 '-runtime/threadsafe_fn',
35_INCLUDE_ORDER_WARNING = (
36 'Your #include order seems to be broken. Remember to use the right '
37 'collation (LC_COLLATE=C) and check\nhttps://google.github.io/styleguide/'
38 'cppguide.html#Names_and_Order_of_Includes')
43 '%s@skia-public.iam.gserviceaccount.com' % s
for s
in (
'pdfium-autoroll',))
45_THIRD_PARTY =
'third_party/'
52_BANNED_CPP_FUNCTIONS = (
54 r'/\busing namespace ',
56 'Using directives ("using namespace x") are banned by the Google',
58 'https://google.github.io/styleguide/cppguide.html#Namespaces ).',
59 'Explicitly qualify symbols or use using declarations ("using',
66 r'/v8::Isolate::(?:|Try)GetCurrent\(\)',
68 'v8::Isolate::GetCurrent() and v8::Isolate::TryGetCurrent() are',
69 'banned. Hold a pointer to the v8::Isolate that was entered. Use',
70 'v8::Isolate::IsCurrent() to check whether a given v8::Isolate is',
78 (
'Use FXSYS_memcpy() in place of memcpy().',),
84 (
'Use FXSYS_memmove() in place of memmove().',),
90 (
'Use FXSYS_memset() in place of memset().',),
96 (
'Use FXSYS_memclr() in place of memclr().',),
104 """Makes sure that banned functions are not used."""
108 def _GetMessageForMatchingType(input_api, affected_file, line_number, line,
110 """Returns an string composed of the name of the file, the line number where
111 the match has been found and the additional text passed as `message` in case
112 the target type name matches the text inside the line passed as parameter.
116 if input_api.re.search(
r"^ *//",
124 if type_name[0:1] ==
'/':
125 regex = type_name[1:]
126 if input_api.re.search(regex, line):
128 elif type_name
in line:
132 result.append(
' %s:%d:' % (affected_file.LocalPath(), line_number))
133 for message_line
in message:
134 result.append(
' %s' % message_line)
138 def IsExcludedFile(affected_file, excluded_paths):
139 local_path = affected_file.LocalPath()
140 for item
in excluded_paths:
141 if input_api.re.match(item, local_path):
145 def CheckForMatch(affected_file, line_num, line, func_name, message, error):
146 problems = _GetMessageForMatchingType(input_api, f, line_num, line,
150 errors.extend(problems)
152 warnings.extend(problems)
154 file_filter =
lambda f: f.LocalPath().endswith((
'.cc',
'.cpp',
'.h'))
155 for f
in input_api.AffectedFiles(file_filter=file_filter):
156 for line_num, line
in f.ChangedContents():
157 for func_name, message, error, excluded_paths
in _BANNED_CPP_FUNCTIONS:
158 if IsExcludedFile(f, excluded_paths):
160 CheckForMatch(f, line_num, line, func_name, message, error)
165 output_api.PresubmitPromptWarning(
'Banned functions were used.\n' +
166 '\n'.join(warnings)))
169 output_api.PresubmitError(
'Banned functions were used.\n' +
175 """Runs checkdeps on #include statements added in this
176 change. Breaking - rules is an error, breaking ! rules is a
183 original_sys_path = sys.path
185 def GenerateCheckdepsPath(base_path):
186 return input_api.os_path.join(base_path,
'buildtools',
'checkdeps')
188 presubmit_path = input_api.PresubmitLocalPath()
189 presubmit_parent_path = input_api.os_path.dirname(presubmit_path)
190 not_standalone_pdfium = \
191 input_api.os_path.basename(presubmit_parent_path) ==
"third_party" and \
192 input_api.os_path.basename(presubmit_path) ==
"pdfium"
194 sys.path.append(GenerateCheckdepsPath(presubmit_path))
195 if not_standalone_pdfium:
196 presubmit_grandparent_path = input_api.os_path.dirname(
197 presubmit_parent_path)
198 sys.path.append(GenerateCheckdepsPath(presubmit_grandparent_path))
201 from cpp_checker
import CppChecker
202 from rules
import Rule
204 return [output_api.PresubmitError(
205 'Unable to run checkdeps, does pdfium/buildtools/checkdeps exist?')]
208 sys.path = original_sys_path
211 for f
in input_api.AffectedFiles():
212 if not CppChecker.IsCppFile(f.LocalPath()):
215 changed_lines = [line
for line_num, line
in f.ChangedContents()]
216 added_includes.append([f.LocalPath(), changed_lines])
218 deps_checker = checkdeps.DepsChecker(input_api.PresubmitLocalPath())
220 error_descriptions = []
221 warning_descriptions = []
222 for path, rule_type, rule_description
in deps_checker.CheckAddedCppIncludes(
224 description_with_path =
'%s\n %s' % (path, rule_description)
225 if rule_type == Rule.DISALLOW:
226 error_descriptions.append(description_with_path)
228 warning_descriptions.append(description_with_path)
231 if error_descriptions:
232 results.append(output_api.PresubmitError(
233 'You added one or more #includes that violate checkdeps rules.',
235 if warning_descriptions:
236 results.append(output_api.PresubmitPromptOrNotify(
237 'You added one or more #includes of files that are temporarily\n'
238 'allowed but being removed. Can you avoid introducing the\n'
239 '#include? See relevant DEPS file(s) for details and contacts.',
240 warning_descriptions))
245 """Checks that the lines in scope occur in the right order.
247 1. C system files in alphabetical order
248 2. C++ system files in alphabetical order
249 3. Project's .h files
252 c_system_include_pattern = input_api.re.compile(
r'\s*#include <.*\.h>')
253 cpp_system_include_pattern = input_api.re.compile(
r'\s*#include <.*>')
254 custom_include_pattern = input_api.re.compile(
r'\s*#include ".*')
256 C_SYSTEM_INCLUDES, CPP_SYSTEM_INCLUDES, CUSTOM_INCLUDES = range(3)
258 state = C_SYSTEM_INCLUDES
261 previous_line_num = 0
262 problem_linenums = []
263 out_of_order =
" - line belongs before previous line"
264 for line_num, line
in scope:
265 if c_system_include_pattern.match(line):
266 if state != C_SYSTEM_INCLUDES:
267 problem_linenums.append((line_num, previous_line_num,
268 " - C system include file in wrong block"))
269 elif previous_line
and previous_line > line:
270 problem_linenums.append((line_num, previous_line_num,
272 elif cpp_system_include_pattern.match(line):
273 if state == C_SYSTEM_INCLUDES:
274 state = CPP_SYSTEM_INCLUDES
275 elif state == CUSTOM_INCLUDES:
276 problem_linenums.append((line_num, previous_line_num,
277 " - c++ system include file in wrong block"))
278 elif previous_line
and previous_line > line:
279 problem_linenums.append((line_num, previous_line_num, out_of_order))
280 elif custom_include_pattern.match(line):
281 if state != CUSTOM_INCLUDES:
282 state = CUSTOM_INCLUDES
283 elif previous_line
and previous_line > line:
284 problem_linenums.append((line_num, previous_line_num, out_of_order))
286 problem_linenums.append((line_num, previous_line_num,
287 "Unknown include type"))
289 previous_line_num = line_num
292 for (line_num, previous_line_num, failure_type)
in problem_linenums:
293 if line_num
in changed_linenums
or previous_line_num
in changed_linenums:
294 warnings.append(
' %s:%d:%s' % (file_path, line_num, failure_type))
299 """Checks the #include order for the given file f."""
301 system_include_pattern = input_api.re.compile(
r'\s*#include <.*')
306 excluded_include_pattern = input_api.re.compile(
307 r'\s*#include (<.*/.*|<atlbase\.h>|"build/build_config.h")')
308 custom_include_pattern = input_api.re.compile(
r'\s*#include "(?P<FILE>.*)"')
311 test_file_tag_pattern = input_api.re.compile(
312 r'_[a-z]+test(?=(_[a-zA-Z0-9]+)?\.)')
313 if_pattern = input_api.re.compile(
314 r'\s*#\s*(if|elif|else|endif|define|undef).*')
317 uncheckable_includes_pattern = input_api.re.compile(
319 '("ipc/.*macros\.h"|<windows\.h>|".*gl.*autogen.h")\s*')
321 contents = f.NewContents()
331 including_file_base_name = test_file_tag_pattern.sub(
332 '', input_api.os_path.basename(f.LocalPath()))
334 for line
in contents:
336 if system_include_pattern.match(line):
341 match = custom_include_pattern.match(line)
343 match_dict = match.groupdict()
344 header_basename = test_file_tag_pattern.sub(
345 '', input_api.os_path.basename(match_dict[
'FILE'])).replace(
'.h',
'')
347 if header_basename
not in including_file_base_name:
356 for line
in contents[line_num:]:
358 if uncheckable_includes_pattern.match(line):
360 if if_pattern.match(line):
361 scopes.append(current_scope)
363 elif ((system_include_pattern.match(line)
or
364 custom_include_pattern.match(line))
and
365 not excluded_include_pattern.match(line)):
366 current_scope.append((line_num, line))
367 scopes.append(current_scope)
376 """Checks that the #include order is correct.
378 1. The corresponding header for source files.
379 2. C system files in alphabetical order
380 3. C++ system files in alphabetical order
381 4. Project's .h files in alphabetical order
383 Each region separated by #if, #elif, #else, #endif, #define and #undef follows
384 these rules separately.
387 for f
in input_api.AffectedFiles(file_filter=input_api.FilterSourceFile):
388 if f.LocalPath().endswith((
'.cc',
'.cpp',
'.h',
'.mm')):
389 changed_linenums =
set(line_num
for line_num, _
in f.ChangedContents())
394 results.append(output_api.PresubmitPromptOrNotify(_INCLUDE_ORDER_WARNING,
400 """Makes sure that libcxx_revision is set correctly."""
401 if 'DEPS' not in [f.LocalPath()
for f
in input_api.AffectedFiles()]:
404 script_path = input_api.os_path.join(
'testing',
'tools',
'libcxx_check.py')
405 buildtools_deps_path = input_api.os_path.join(
'buildtools',
406 'deps_revisions.gni')
409 errors = input_api.subprocess.check_output(
410 [script_path,
'DEPS', buildtools_deps_path])
411 except input_api.subprocess.CalledProcessError
as error:
412 msg =
'libcxx_check.py failed:'
413 long_text = error.output.decode(
'utf-8',
'ignore')
414 return [output_api.PresubmitError(msg, long_text=long_text)]
417 return [output_api.PresubmitError(errors)]
422 """Checks that pixel and javascript tests don't contain duplicates.
423 We use .in and .pdf files, having both can cause race conditions on the bots,
424 which run the tests in parallel.
428 for f
in input_api.AffectedFiles():
429 if f.Action() ==
'D':
431 if not f.LocalPath().startswith((
'testing/resources/pixel/',
432 'testing/resources/javascript/')):
435 if f.LocalPath().endswith(
'.in'):
437 elif f.LocalPath().endswith(
'.pdf'):
441 path = f.LocalPath()[:-end_len]
442 if path
in tests_added:
443 results.append(output_api.PresubmitError(
444 'Remove %s to prevent shadowing %s' % (path +
'.pdf',
447 tests_added.append(path)
452 """Checks that .png files have the right file name format, which must be in
455 NAME_expected(_gdi)?(_(agg|skia))?(_(linux|mac|win))?.pdf.\d+.png
457 This must be the same format as the one in testing/corpus's PRESUBMIT.py.
459 expected_pattern = input_api.re.compile(
460 r'.+_expected(_gdi)?(_(agg|skia))?(_(linux|mac|win))?\.pdf\.\d+.png')
462 for f
in input_api.AffectedFiles(include_deletes=
False):
463 if not f.LocalPath().endswith(
'.png'):
465 if expected_pattern.match(f.LocalPath()):
468 output_api.PresubmitError(
469 'PNG file %s does not have the correct format' % f.LocalPath()))
474 """Checks that added or removed lines in non third party affected
475 header files do not lead to new useless class or struct forward
479 class_pattern = input_api.re.compile(
r'^class\s+(\w+);$',
480 input_api.re.MULTILINE)
481 struct_pattern = input_api.re.compile(
r'^struct\s+(\w+);$',
482 input_api.re.MULTILINE)
483 for f
in input_api.AffectedFiles(include_deletes=
False):
484 if f.LocalPath().startswith(
'third_party'):
487 if not f.LocalPath().endswith(
'.h'):
490 contents = input_api.ReadFile(f)
491 fwd_decls = input_api.re.findall(class_pattern, contents)
492 fwd_decls.extend(input_api.re.findall(struct_pattern, contents))
494 useless_fwd_decls = []
495 for decl
in fwd_decls:
498 for _
in input_api.re.finditer(
r'\b%s\b' %
499 input_api.re.escape(decl), contents))
501 useless_fwd_decls.append(decl)
503 if not useless_fwd_decls:
506 for line
in f.GenerateScmDiff().splitlines():
507 if (line.startswith(
'-')
and not line.startswith(
'--')
or
508 line.startswith(
'+')
and not line.startswith(
'++')):
509 for decl
in useless_fwd_decls:
510 if input_api.re.search(
r'\b%s\b' % decl, line[1:]):
512 output_api.PresubmitPromptWarning(
513 '%s: %s forward declaration is no longer needed' %
514 (f.LocalPath(), decl)))
515 useless_fwd_decls.remove(decl)
524 input_api.canned_checks.PanProjectChecks(
525 input_api, output_api, project_name=
'PDFium'))
534 input_api.canned_checks.CheckLicense(
537 project_name=
'PDFium',
538 source_file_filter=
lambda x: input_api.FilterSourceFile(
539 x, files_to_check=files_to_check)))
548 input_api.canned_checks.CheckPatchFormatted(input_api, output_api))
550 input_api.canned_checks.CheckChangeLintsClean(
551 input_api, output_api, lint_filters=LINT_FILTERS))
558 author = input_api.change.author_email
559 if author
and author
not in _KNOWN_ROBOTS:
561 input_api.canned_checks.CheckAuthorizedAuthor(input_api, output_api))
563 for f
in input_api.AffectedFiles():
564 path, name = input_api.os_path.split(f.LocalPath())
565 if name ==
'PRESUBMIT.py':
566 full_path = input_api.os_path.join(input_api.PresubmitLocalPath(), path)
567 test_file = input_api.os_path.join(path,
'PRESUBMIT_test.py')
568 if f.Action() !=
'D' and input_api.os_path.exists(test_file):
573 input_api.canned_checks.RunUnitTestsInDirectory(
577 files_to_check=[
r'^PRESUBMIT_test\.py$'],
578 run_on_python2=
not USE_PYTHON3,
579 run_on_python3=USE_PYTHON3,
580 skip_shebang_check=
True))
_CheckLibcxxRevision(input_api, output_api)
_CheckPngNames(input_api, output_api)
_CheckIncludeOrderForScope(scope, input_api, file_path, changed_linenums)
_CheckNoBannedFunctions(input_api, output_api)
_CheckUselessForwardDeclarations(input_api, output_api)
_CheckIncludeOrderInFile(input_api, f, changed_linenums)
ChecksCommon(input_api, output_api)
_CheckIncludeOrder(input_api, output_api)
_CheckTestDuplicates(input_api, output_api)
CheckChangeOnUpload(input_api, output_api)
_CheckUnwantedDependencies(input_api, output_api)
QFuture< QSet< QChar > > set