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',
80 """Makes sure that banned functions are not used."""
84 def _GetMessageForMatchingType(input_api, affected_file, line_number, line,
86 """Returns an string composed of the name of the file, the line number where
87 the match has been found and the additional text passed as `message` in case
88 the target type name matches the text inside the line passed as parameter.
92 if input_api.re.search(
r"^ *//",
100 if type_name[0:1] ==
'/':
101 regex = type_name[1:]
102 if input_api.re.search(regex, line):
104 elif type_name
in line:
108 result.append(
' %s:%d:' % (affected_file.LocalPath(), line_number))
109 for message_line
in message:
110 result.append(
' %s' % message_line)
114 def IsExcludedFile(affected_file, excluded_paths):
115 local_path = affected_file.LocalPath()
116 for item
in excluded_paths:
117 if input_api.re.match(item, local_path):
121 def CheckForMatch(affected_file, line_num, line, func_name, message, error):
122 problems = _GetMessageForMatchingType(input_api, f, line_num, line,
126 errors.extend(problems)
128 warnings.extend(problems)
130 file_filter =
lambda f: f.LocalPath().endswith((
'.cc',
'.cpp',
'.h'))
131 for f
in input_api.AffectedFiles(file_filter=file_filter):
132 for line_num, line
in f.ChangedContents():
133 for func_name, message, error, excluded_paths
in _BANNED_CPP_FUNCTIONS:
134 if IsExcludedFile(f, excluded_paths):
136 CheckForMatch(f, line_num, line, func_name, message, error)
141 output_api.PresubmitPromptWarning(
'Banned functions were used.\n' +
142 '\n'.join(warnings)))
145 output_api.PresubmitError(
'Banned functions were used.\n' +
151 """Runs checkdeps on #include statements added in this
152 change. Breaking - rules is an error, breaking ! rules is a
159 original_sys_path = sys.path
161 def GenerateCheckdepsPath(base_path):
162 return input_api.os_path.join(base_path,
'buildtools',
'checkdeps')
164 presubmit_path = input_api.PresubmitLocalPath()
165 presubmit_parent_path = input_api.os_path.dirname(presubmit_path)
166 not_standalone_pdfium = \
167 input_api.os_path.basename(presubmit_parent_path) ==
"third_party" and \
168 input_api.os_path.basename(presubmit_path) ==
"pdfium"
170 sys.path.append(GenerateCheckdepsPath(presubmit_path))
171 if not_standalone_pdfium:
172 presubmit_grandparent_path = input_api.os_path.dirname(
173 presubmit_parent_path)
174 sys.path.append(GenerateCheckdepsPath(presubmit_grandparent_path))
177 from cpp_checker
import CppChecker
178 from rules
import Rule
180 return [output_api.PresubmitError(
181 'Unable to run checkdeps, does pdfium/buildtools/checkdeps exist?')]
184 sys.path = original_sys_path
187 for f
in input_api.AffectedFiles():
188 if not CppChecker.IsCppFile(f.LocalPath()):
191 changed_lines = [line
for line_num, line
in f.ChangedContents()]
192 added_includes.append([f.LocalPath(), changed_lines])
194 deps_checker = checkdeps.DepsChecker(input_api.PresubmitLocalPath())
196 error_descriptions = []
197 warning_descriptions = []
198 for path, rule_type, rule_description
in deps_checker.CheckAddedCppIncludes(
200 description_with_path =
'%s\n %s' % (path, rule_description)
201 if rule_type == Rule.DISALLOW:
202 error_descriptions.append(description_with_path)
204 warning_descriptions.append(description_with_path)
207 if error_descriptions:
208 results.append(output_api.PresubmitError(
209 'You added one or more #includes that violate checkdeps rules.',
211 if warning_descriptions:
212 results.append(output_api.PresubmitPromptOrNotify(
213 'You added one or more #includes of files that are temporarily\n'
214 'allowed but being removed. Can you avoid introducing the\n'
215 '#include? See relevant DEPS file(s) for details and contacts.',
216 warning_descriptions))
221 """Checks that the lines in scope occur in the right order.
223 1. C system files in alphabetical order
224 2. C++ system files in alphabetical order
225 3. Project's .h files
228 c_system_include_pattern = input_api.re.compile(
r'\s*#include <.*\.h>')
229 cpp_system_include_pattern = input_api.re.compile(
r'\s*#include <.*>')
230 custom_include_pattern = input_api.re.compile(
r'\s*#include ".*')
232 C_SYSTEM_INCLUDES, CPP_SYSTEM_INCLUDES, CUSTOM_INCLUDES = range(3)
234 state = C_SYSTEM_INCLUDES
237 previous_line_num = 0
238 problem_linenums = []
239 out_of_order =
" - line belongs before previous line"
240 for line_num, line
in scope:
241 if c_system_include_pattern.match(line):
242 if state != C_SYSTEM_INCLUDES:
243 problem_linenums.append((line_num, previous_line_num,
244 " - C system include file in wrong block"))
245 elif previous_line
and previous_line > line:
246 problem_linenums.append((line_num, previous_line_num,
248 elif cpp_system_include_pattern.match(line):
249 if state == C_SYSTEM_INCLUDES:
250 state = CPP_SYSTEM_INCLUDES
251 elif state == CUSTOM_INCLUDES:
252 problem_linenums.append((line_num, previous_line_num,
253 " - c++ system include file in wrong block"))
254 elif previous_line
and previous_line > line:
255 problem_linenums.append((line_num, previous_line_num, out_of_order))
256 elif custom_include_pattern.match(line):
257 if state != CUSTOM_INCLUDES:
258 state = CUSTOM_INCLUDES
259 elif previous_line
and previous_line > line:
260 problem_linenums.append((line_num, previous_line_num, out_of_order))
262 problem_linenums.append((line_num, previous_line_num,
263 "Unknown include type"))
265 previous_line_num = line_num
268 for (line_num, previous_line_num, failure_type)
in problem_linenums:
269 if line_num
in changed_linenums
or previous_line_num
in changed_linenums:
270 warnings.append(
' %s:%d:%s' % (file_path, line_num, failure_type))
275 """Checks the #include order for the given file f."""
277 system_include_pattern = input_api.re.compile(
r'\s*#include <.*')
282 excluded_include_pattern = input_api.re.compile(
283 r'\s*#include (<.*/.*|<atlbase\.h>|"build/build_config.h")')
284 custom_include_pattern = input_api.re.compile(
r'\s*#include "(?P<FILE>.*)"')
287 test_file_tag_pattern = input_api.re.compile(
288 r'_[a-z]+test(?=(_[a-zA-Z0-9]+)?\.)')
289 if_pattern = input_api.re.compile(
290 r'\s*#\s*(if|elif|else|endif|define|undef).*')
293 uncheckable_includes_pattern = input_api.re.compile(
295 '("ipc/.*macros\.h"|<windows\.h>|".*gl.*autogen.h")\s*')
297 contents = f.NewContents()
307 including_file_base_name = test_file_tag_pattern.sub(
308 '', input_api.os_path.basename(f.LocalPath()))
310 for line
in contents:
312 if system_include_pattern.match(line):
317 match = custom_include_pattern.match(line)
319 match_dict = match.groupdict()
320 header_basename = test_file_tag_pattern.sub(
321 '', input_api.os_path.basename(match_dict[
'FILE'])).replace(
'.h',
'')
323 if header_basename
not in including_file_base_name:
332 for line
in contents[line_num:]:
334 if uncheckable_includes_pattern.match(line):
336 if if_pattern.match(line):
337 scopes.append(current_scope)
339 elif ((system_include_pattern.match(line)
or
340 custom_include_pattern.match(line))
and
341 not excluded_include_pattern.match(line)):
342 current_scope.append((line_num, line))
343 scopes.append(current_scope)
352 """Checks that the #include order is correct.
354 1. The corresponding header for source files.
355 2. C system files in alphabetical order
356 3. C++ system files in alphabetical order
357 4. Project's .h files in alphabetical order
359 Each region separated by #if, #elif, #else, #endif, #define and #undef follows
360 these rules separately.
363 for f
in input_api.AffectedFiles(file_filter=input_api.FilterSourceFile):
364 if f.LocalPath().endswith((
'.cc',
'.cpp',
'.h',
'.mm')):
365 changed_linenums =
set(line_num
for line_num, _
in f.ChangedContents())
370 results.append(output_api.PresubmitPromptOrNotify(_INCLUDE_ORDER_WARNING,
376 """Makes sure that libcxx_revision is set correctly."""
377 if 'DEPS' not in [f.LocalPath()
for f
in input_api.AffectedFiles()]:
380 script_path = input_api.os_path.join(
'testing',
'tools',
'libcxx_check.py')
381 buildtools_deps_path = input_api.os_path.join(
'buildtools',
382 'deps_revisions.gni')
385 errors = input_api.subprocess.check_output(
386 [script_path,
'DEPS', buildtools_deps_path])
387 except input_api.subprocess.CalledProcessError
as error:
388 msg =
'libcxx_check.py failed:'
389 long_text = error.output.decode(
'utf-8',
'ignore')
390 return [output_api.PresubmitError(msg, long_text=long_text)]
393 return [output_api.PresubmitError(errors)]
398 """Checks that pixel and javascript tests don't contain duplicates.
399 We use .in and .pdf files, having both can cause race conditions on the bots,
400 which run the tests in parallel.
404 for f
in input_api.AffectedFiles():
405 if f.Action() ==
'D':
407 if not f.LocalPath().startswith((
'testing/resources/pixel/',
408 'testing/resources/javascript/')):
411 if f.LocalPath().endswith(
'.in'):
413 elif f.LocalPath().endswith(
'.pdf'):
417 path = f.LocalPath()[:-end_len]
418 if path
in tests_added:
419 results.append(output_api.PresubmitError(
420 'Remove %s to prevent shadowing %s' % (path +
'.pdf',
423 tests_added.append(path)
428 """Checks that .png files have the right file name format, which must be in
431 NAME_expected(_gdi)?(_(agg|skia))?(_(linux|mac|win))?.pdf.\d+.png
433 This must be the same format as the one in testing/corpus's PRESUBMIT.py.
435 expected_pattern = input_api.re.compile(
436 r'.+_expected(_gdi)?(_(agg|skia))?(_(linux|mac|win))?\.pdf\.\d+.png')
438 for f
in input_api.AffectedFiles(include_deletes=
False):
439 if not f.LocalPath().endswith(
'.png'):
441 if expected_pattern.match(f.LocalPath()):
444 output_api.PresubmitError(
445 'PNG file %s does not have the correct format' % f.LocalPath()))
450 """Checks that added or removed lines in non third party affected
451 header files do not lead to new useless class or struct forward
455 class_pattern = input_api.re.compile(
r'^class\s+(\w+);$',
456 input_api.re.MULTILINE)
457 struct_pattern = input_api.re.compile(
r'^struct\s+(\w+);$',
458 input_api.re.MULTILINE)
459 for f
in input_api.AffectedFiles(include_deletes=
False):
460 if f.LocalPath().startswith(
'third_party'):
463 if not f.LocalPath().endswith(
'.h'):
466 contents = input_api.ReadFile(f)
467 fwd_decls = input_api.re.findall(class_pattern, contents)
468 fwd_decls.extend(input_api.re.findall(struct_pattern, contents))
470 useless_fwd_decls = []
471 for decl
in fwd_decls:
474 for _
in input_api.re.finditer(
r'\b%s\b' %
475 input_api.re.escape(decl), contents))
477 useless_fwd_decls.append(decl)
479 if not useless_fwd_decls:
482 for line
in f.GenerateScmDiff().splitlines():
483 if (line.startswith(
'-')
and not line.startswith(
'--')
or
484 line.startswith(
'+')
and not line.startswith(
'++')):
485 for decl
in useless_fwd_decls:
486 if input_api.re.search(
r'\b%s\b' % decl, line[1:]):
488 output_api.PresubmitPromptWarning(
489 '%s: %s forward declaration is no longer needed' %
490 (f.LocalPath(), decl)))
491 useless_fwd_decls.remove(decl)
500 input_api.canned_checks.PanProjectChecks(
501 input_api, output_api, project_name=
'PDFium'))
509 input_api.canned_checks.CheckLicense(
512 project_name=
'PDFium',
513 source_file_filter=
lambda x: input_api.FilterSourceFile(
514 x, files_to_check=files_to_check)))
524 input_api.canned_checks.CheckPatchFormatted(input_api, output_api))
526 input_api.canned_checks.CheckChangeLintsClean(
527 input_api, output_api, lint_filters=LINT_FILTERS))
534 author = input_api.change.author_email
535 if author
and author
not in _KNOWN_ROBOTS:
537 input_api.canned_checks.CheckAuthorizedAuthor(input_api, output_api))
539 for f
in input_api.AffectedFiles():
540 path, name = input_api.os_path.split(f.LocalPath())
541 if name ==
'PRESUBMIT.py':
542 full_path = input_api.os_path.join(input_api.PresubmitLocalPath(), path)
543 test_file = input_api.os_path.join(path,
'PRESUBMIT_test.py')
544 if f.Action() !=
'D' and input_api.os_path.exists(test_file):
549 input_api.canned_checks.RunUnitTestsInDirectory(
553 files_to_check=[
r'^PRESUBMIT_test\.py$'],
554 run_on_python2=
not USE_PYTHON3,
555 run_on_python3=USE_PYTHON3,
556 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
[10]