blob: 8f2c59a836ede12da86e459e151f2aff4fb3a672 [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
Lei Zhangac238df2021-06-15 21:44:05 +000011USE_PYTHON3 = True
12
Dan Sinclair22d66072016-02-22 11:56:05 -050013LINT_FILTERS = [
Dan Sinclair3ebd1212016-03-09 09:59:23 -050014 # Rvalue ref checks are unreliable.
dan sinclaird2019df2016-02-22 22:32:03 -050015 '-build/c++11',
Dan Sinclair3ebd1212016-03-09 09:59:23 -050016 # Need to fix header names not matching cpp names.
dan sinclaird2019df2016-02-22 22:32:03 -050017 '-build/include_order',
Dan Sinclair3ebd1212016-03-09 09:59:23 -050018 # Too many to fix at the moment.
dan sinclaird2019df2016-02-22 22:32:03 -050019 '-readability/casting',
Dan Sinclair3ebd1212016-03-09 09:59:23 -050020 # Need to refactor large methods to fix.
dan sinclaird2019df2016-02-22 22:32:03 -050021 '-readability/fn_size',
Dan Sinclair3ebd1212016-03-09 09:59:23 -050022 # Lots of usage to fix first.
dan sinclaird2019df2016-02-22 22:32:03 -050023 '-runtime/int',
Dan Sinclair3ebd1212016-03-09 09:59:23 -050024 # Lots of non-const references need to be fixed
dan sinclaird2019df2016-02-22 22:32:03 -050025 '-runtime/references',
Dan Sinclair3ebd1212016-03-09 09:59:23 -050026 # We are not thread safe, so this will never pass.
dan sinclaird2019df2016-02-22 22:32:03 -050027 '-runtime/threadsafe_fn',
Dan Sinclair3ebd1212016-03-09 09:59:23 -050028 # Figure out how to deal with #defines that git cl format creates.
dan sinclaird2019df2016-02-22 22:32:03 -050029 '-whitespace/indent',
Dan Sinclair22d66072016-02-22 11:56:05 -050030]
31
Dan Sinclair544bbc62016-03-14 15:07:39 -040032
dsinclair2ca2da52016-09-13 18:10:34 -070033_INCLUDE_ORDER_WARNING = (
34 'Your #include order seems to be broken. Remember to use the right '
35 'collation (LC_COLLATE=C) and check\nhttps://google.github.io/styleguide/'
36 'cppguide.html#Names_and_Order_of_Includes')
37
38
Lei Zhang71e26732020-05-21 21:03:05 +000039# Bypass the AUTHORS check for these accounts.
40_KNOWN_ROBOTS = set() | set(
41 '%s@skia-public.iam.gserviceaccount.com' % s for s in ('pdfium-autoroll',))
42
Daniel Hosseinianb3bdbd42021-10-22 17:01:14 +000043_THIRD_PARTY = 'third_party/'
44
45# Format: Sequence of tuples containing:
46# * String pattern or, if starting with a slash, a regular expression.
47# * Sequence of strings to show when the pattern matches.
48# * Error flag. True if a match is a presubmit error, otherwise it's a warning.
49# * Sequence of paths to *not* check (regexps).
50_BANNED_CPP_FUNCTIONS = (
51 (
52 r'/\busing namespace ',
53 (
54 'Using directives ("using namespace x") are banned by the Google Style',
55 'Guide ( https://google.github.io/styleguide/cppguide.html#Namespaces ).',
56 'Explicitly qualify symbols or use using declarations ("using x::foo").',
57 ),
58 True,
59 [_THIRD_PARTY],
60 ),
61 (
Daniel Hosseiniand7655382021-11-30 18:11:39 +000062 r'/v8::Isolate::(?:|Try)GetCurrent()',
Daniel Hosseinianb3bdbd42021-10-22 17:01:14 +000063 (
Daniel Hosseiniand7655382021-11-30 18:11:39 +000064 'v8::Isolate::GetCurrent() and v8::Isolate::TryGetCurrent() are banned. Hold',
65 'a pointer to the v8::Isolate that was entered. Use v8::Isolate::IsCurrent()',
66 'to check whether a given v8::Isolate is entered.',
Daniel Hosseinianb3bdbd42021-10-22 17:01:14 +000067 ),
Daniel Hosseiniand7655382021-11-30 18:11:39 +000068 True,
Daniel Hosseinianb3bdbd42021-10-22 17:01:14 +000069 (),
70 ),
71)
72
73
74def _CheckNoBannedFunctions(input_api, output_api):
75 """Makes sure that banned functions are not used."""
76 warnings = []
77 errors = []
78
79 def _GetMessageForMatchingType(input_api, affected_file, line_number, line,
80 type_name, message):
81 """Returns an string composed of the name of the file, the line number where
82 the match has been found and the additional text passed as `message` in case
83 the target type name matches the text inside the line passed as parameter.
84 """
85 result = []
86
87 if input_api.re.search(r"^ *//",
88 line): # Ignore comments about banned types.
89 return result
90 if line.endswith(
91 " nocheck"): # A // nocheck comment will bypass this error.
92 return result
93
94 matched = False
95 if type_name[0:1] == '/':
96 regex = type_name[1:]
97 if input_api.re.search(regex, line):
98 matched = True
99 elif type_name in line:
100 matched = True
101
102 if matched:
103 result.append(' %s:%d:' % (affected_file.LocalPath(), line_number))
104 for message_line in message:
105 result.append(' %s' % message_line)
106
107 return result
108
109 def IsExcludedFile(affected_file, excluded_paths):
110 local_path = affected_file.LocalPath()
111 for item in excluded_paths:
112 if input_api.re.match(item, local_path):
113 return True
114 return False
115
116 def CheckForMatch(affected_file, line_num, line, func_name, message, error):
117 problems = _GetMessageForMatchingType(input_api, f, line_num, line,
118 func_name, message)
119 if problems:
120 if error:
121 errors.extend(problems)
122 else:
123 warnings.extend(problems)
124
125 file_filter = lambda f: f.LocalPath().endswith(('.cc', '.cpp', '.h'))
126 for f in input_api.AffectedFiles(file_filter=file_filter):
127 for line_num, line in f.ChangedContents():
128 for func_name, message, error, excluded_paths in _BANNED_CPP_FUNCTIONS:
129 if IsExcludedFile(f, excluded_paths):
130 continue
131 CheckForMatch(f, line_num, line, func_name, message, error)
132
133 result = []
134 if (warnings):
135 result.append(
136 output_api.PresubmitPromptWarning('Banned functions were used.\n' +
137 '\n'.join(warnings)))
138 if (errors):
139 result.append(
140 output_api.PresubmitError('Banned functions were used.\n' +
141 '\n'.join(errors)))
142 return result
143
Lei Zhang71e26732020-05-21 21:03:05 +0000144
Dan Sinclair544bbc62016-03-14 15:07:39 -0400145def _CheckUnwantedDependencies(input_api, output_api):
146 """Runs checkdeps on #include statements added in this
147 change. Breaking - rules is an error, breaking ! rules is a
148 warning.
149 """
150 import sys
151 # We need to wait until we have an input_api object and use this
152 # roundabout construct to import checkdeps because this file is
153 # eval-ed and thus doesn't have __file__.
154 original_sys_path = sys.path
155 try:
Lei Zhangb1d78722018-02-02 22:17:58 +0000156 def GenerateCheckdepsPath(base_path):
157 return input_api.os_path.join(base_path, 'buildtools', 'checkdeps')
158
159 presubmit_path = input_api.PresubmitLocalPath()
160 presubmit_parent_path = input_api.os_path.dirname(presubmit_path)
161 not_standalone_pdfium = \
162 input_api.os_path.basename(presubmit_parent_path) == "third_party" and \
163 input_api.os_path.basename(presubmit_path) == "pdfium"
164
165 sys.path.append(GenerateCheckdepsPath(presubmit_path))
166 if not_standalone_pdfium:
167 presubmit_grandparent_path = input_api.os_path.dirname(
168 presubmit_parent_path)
169 sys.path.append(GenerateCheckdepsPath(presubmit_grandparent_path))
170
Dan Sinclair544bbc62016-03-14 15:07:39 -0400171 import checkdeps
172 from cpp_checker import CppChecker
173 from rules import Rule
dsinclair4cd49e12016-04-05 10:28:48 -0700174 except ImportError:
175 return [output_api.PresubmitError(
176 'Unable to run checkdeps, does pdfium/buildtools/checkdeps exist?')]
Dan Sinclair544bbc62016-03-14 15:07:39 -0400177 finally:
178 # Restore sys.path to what it was before.
179 sys.path = original_sys_path
180
181 added_includes = []
182 for f in input_api.AffectedFiles():
183 if not CppChecker.IsCppFile(f.LocalPath()):
184 continue
185
186 changed_lines = [line for line_num, line in f.ChangedContents()]
187 added_includes.append([f.LocalPath(), changed_lines])
188
189 deps_checker = checkdeps.DepsChecker(input_api.PresubmitLocalPath())
190
191 error_descriptions = []
192 warning_descriptions = []
193 for path, rule_type, rule_description in deps_checker.CheckAddedCppIncludes(
194 added_includes):
195 description_with_path = '%s\n %s' % (path, rule_description)
196 if rule_type == Rule.DISALLOW:
197 error_descriptions.append(description_with_path)
198 else:
199 warning_descriptions.append(description_with_path)
200
201 results = []
202 if error_descriptions:
203 results.append(output_api.PresubmitError(
204 'You added one or more #includes that violate checkdeps rules.',
205 error_descriptions))
206 if warning_descriptions:
207 results.append(output_api.PresubmitPromptOrNotify(
208 'You added one or more #includes of files that are temporarily\n'
209 'allowed but being removed. Can you avoid introducing the\n'
210 '#include? See relevant DEPS file(s) for details and contacts.',
211 warning_descriptions))
212 return results
213
214
dsinclair2ca2da52016-09-13 18:10:34 -0700215def _CheckIncludeOrderForScope(scope, input_api, file_path, changed_linenums):
216 """Checks that the lines in scope occur in the right order.
217
218 1. C system files in alphabetical order
219 2. C++ system files in alphabetical order
220 3. Project's .h files
221 """
222
223 c_system_include_pattern = input_api.re.compile(r'\s*#include <.*\.h>')
224 cpp_system_include_pattern = input_api.re.compile(r'\s*#include <.*>')
225 custom_include_pattern = input_api.re.compile(r'\s*#include ".*')
226
227 C_SYSTEM_INCLUDES, CPP_SYSTEM_INCLUDES, CUSTOM_INCLUDES = range(3)
228
229 state = C_SYSTEM_INCLUDES
230
231 previous_line = ''
232 previous_line_num = 0
233 problem_linenums = []
234 out_of_order = " - line belongs before previous line"
235 for line_num, line in scope:
236 if c_system_include_pattern.match(line):
237 if state != C_SYSTEM_INCLUDES:
238 problem_linenums.append((line_num, previous_line_num,
239 " - C system include file in wrong block"))
240 elif previous_line and previous_line > line:
241 problem_linenums.append((line_num, previous_line_num,
242 out_of_order))
243 elif cpp_system_include_pattern.match(line):
244 if state == C_SYSTEM_INCLUDES:
245 state = CPP_SYSTEM_INCLUDES
246 elif state == CUSTOM_INCLUDES:
247 problem_linenums.append((line_num, previous_line_num,
248 " - c++ system include file in wrong block"))
249 elif previous_line and previous_line > line:
250 problem_linenums.append((line_num, previous_line_num, out_of_order))
251 elif custom_include_pattern.match(line):
252 if state != CUSTOM_INCLUDES:
253 state = CUSTOM_INCLUDES
254 elif previous_line and previous_line > line:
255 problem_linenums.append((line_num, previous_line_num, out_of_order))
256 else:
257 problem_linenums.append((line_num, previous_line_num,
258 "Unknown include type"))
259 previous_line = line
260 previous_line_num = line_num
261
262 warnings = []
263 for (line_num, previous_line_num, failure_type) in problem_linenums:
264 if line_num in changed_linenums or previous_line_num in changed_linenums:
265 warnings.append(' %s:%d:%s' % (file_path, line_num, failure_type))
266 return warnings
267
268
269def _CheckIncludeOrderInFile(input_api, f, changed_linenums):
270 """Checks the #include order for the given file f."""
271
272 system_include_pattern = input_api.re.compile(r'\s*#include \<.*')
273 # Exclude the following includes from the check:
274 # 1) #include <.../...>, e.g., <sys/...> includes often need to appear in a
275 # specific order.
276 # 2) <atlbase.h>, "build/build_config.h"
277 excluded_include_pattern = input_api.re.compile(
278 r'\s*#include (\<.*/.*|\<atlbase\.h\>|"build/build_config.h")')
279 custom_include_pattern = input_api.re.compile(r'\s*#include "(?P<FILE>.*)"')
280 # Match the final or penultimate token if it is xxxtest so we can ignore it
281 # when considering the special first include.
282 test_file_tag_pattern = input_api.re.compile(
283 r'_[a-z]+test(?=(_[a-zA-Z0-9]+)?\.)')
284 if_pattern = input_api.re.compile(
285 r'\s*#\s*(if|elif|else|endif|define|undef).*')
286 # Some files need specialized order of includes; exclude such files from this
287 # check.
288 uncheckable_includes_pattern = input_api.re.compile(
289 r'\s*#include '
290 '("ipc/.*macros\.h"|<windows\.h>|".*gl.*autogen.h")\s*')
291
292 contents = f.NewContents()
293 warnings = []
294 line_num = 0
295
296 # Handle the special first include. If the first include file is
297 # some/path/file.h, the corresponding including file can be some/path/file.cc,
298 # some/other/path/file.cc, some/path/file_platform.cc, some/path/file-suffix.h
299 # etc. It's also possible that no special first include exists.
300 # If the included file is some/path/file_platform.h the including file could
301 # also be some/path/file_xxxtest_platform.h.
302 including_file_base_name = test_file_tag_pattern.sub(
303 '', input_api.os_path.basename(f.LocalPath()))
304
305 for line in contents:
306 line_num += 1
307 if system_include_pattern.match(line):
308 # No special first include -> process the line again along with normal
309 # includes.
310 line_num -= 1
311 break
312 match = custom_include_pattern.match(line)
313 if match:
314 match_dict = match.groupdict()
315 header_basename = test_file_tag_pattern.sub(
316 '', input_api.os_path.basename(match_dict['FILE'])).replace('.h', '')
317
318 if header_basename not in including_file_base_name:
319 # No special first include -> process the line again along with normal
320 # includes.
321 line_num -= 1
322 break
323
324 # Split into scopes: Each region between #if and #endif is its own scope.
325 scopes = []
326 current_scope = []
327 for line in contents[line_num:]:
328 line_num += 1
329 if uncheckable_includes_pattern.match(line):
330 continue
331 if if_pattern.match(line):
332 scopes.append(current_scope)
333 current_scope = []
334 elif ((system_include_pattern.match(line) or
335 custom_include_pattern.match(line)) and
336 not excluded_include_pattern.match(line)):
337 current_scope.append((line_num, line))
338 scopes.append(current_scope)
339
340 for scope in scopes:
341 warnings.extend(_CheckIncludeOrderForScope(scope, input_api, f.LocalPath(),
342 changed_linenums))
343 return warnings
344
345
346def _CheckIncludeOrder(input_api, output_api):
347 """Checks that the #include order is correct.
348
349 1. The corresponding header for source files.
350 2. C system files in alphabetical order
351 3. C++ system files in alphabetical order
352 4. Project's .h files in alphabetical order
353
354 Each region separated by #if, #elif, #else, #endif, #define and #undef follows
355 these rules separately.
356 """
dsinclair2ca2da52016-09-13 18:10:34 -0700357 warnings = []
Lei Zhangf98bc4a2020-08-24 23:47:16 +0000358 for f in input_api.AffectedFiles(file_filter=input_api.FilterSourceFile):
dsinclaird33c8e32016-11-21 13:31:16 -0800359 if f.LocalPath().endswith(('.cc', '.cpp', '.h', '.mm')):
dsinclair2ca2da52016-09-13 18:10:34 -0700360 changed_linenums = set(line_num for line_num, _ in f.ChangedContents())
361 warnings.extend(_CheckIncludeOrderInFile(input_api, f, changed_linenums))
362
363 results = []
364 if warnings:
365 results.append(output_api.PresubmitPromptOrNotify(_INCLUDE_ORDER_WARNING,
366 warnings))
367 return results
368
Lei Zhangf98bc4a2020-08-24 23:47:16 +0000369
Lei Zhang07ef29a2022-09-10 06:52:25 +0000370def _CheckLibcxxRevision(input_api, output_api):
371 """Makes sure that libcxx_revision is set correctly."""
372 if 'DEPS' not in [f.LocalPath() for f in input_api.AffectedFiles()]:
373 return []
374
375 script_path = input_api.os_path.join('testing', 'tools', 'libcxx_check.py')
376 buildtools_deps_path = input_api.os_path.join('buildtools',
377 'deps_revisions.gni')
378
379 try:
380 errors = input_api.subprocess.check_output(
381 [script_path, 'DEPS', buildtools_deps_path])
382 except input_api.subprocess.CalledProcessError as error:
383 msg = 'libcxx_check.py failed:'
384 long_text = error.output.decode('utf-8', 'ignore')
385 return [output_api.PresubmitError(msg, long_text=long_text)]
386
387 if errors:
388 return [output_api.PresubmitError(errors)]
389 return []
390
391
Nicolas Penad824a902017-05-19 15:59:49 -0400392def _CheckTestDuplicates(input_api, output_api):
393 """Checks that pixel and javascript tests don't contain duplicates.
394 We use .in and .pdf files, having both can cause race conditions on the bots,
395 which run the tests in parallel.
396 """
397 tests_added = []
398 results = []
399 for f in input_api.AffectedFiles():
Lei Zhang567039b2019-09-17 00:45:23 +0000400 if f.Action() == 'D':
401 continue
Nicolas Penad824a902017-05-19 15:59:49 -0400402 if not f.LocalPath().startswith(('testing/resources/pixel/',
403 'testing/resources/javascript/')):
404 continue
405 end_len = 0
406 if f.LocalPath().endswith('.in'):
407 end_len = 3
408 elif f.LocalPath().endswith('.pdf'):
409 end_len = 4
410 else:
411 continue
Nicolas Penac4479c52017-06-26 11:45:06 -0400412 path = f.LocalPath()[:-end_len]
Nicolas Penad824a902017-05-19 15:59:49 -0400413 if path in tests_added:
414 results.append(output_api.PresubmitError(
415 'Remove %s to prevent shadowing %s' % (path + '.pdf',
416 path + '.in')))
417 else:
418 tests_added.append(path)
419 return results
420
Lei Zhang624fba82021-06-15 20:11:39 +0000421
Nicolas Penac4479c52017-06-26 11:45:06 -0400422def _CheckPNGFormat(input_api, output_api):
423 """Checks that .png files have a format that will be considered valid by our
424 test runners. If a file ends with .png, then it must be of the form
Hui Yingst5d5273f2021-10-01 17:58:33 +0000425 NAME_expected(_(skia|skiapaths))?(_(win|mac|linux))?.pdf.#.png
426 The expected format used by _CheckPngNames() in testing/corpus/PRESUBMIT.py
427 must be the same as this one.
428 """
Nicolas Penac4479c52017-06-26 11:45:06 -0400429 expected_pattern = input_api.re.compile(
Hui Yingst2aa0a4a2020-04-09 19:04:21 +0000430 r'.+_expected(_(skia|skiapaths))?(_(win|mac|linux))?\.pdf\.\d+.png')
Nicolas Penac4479c52017-06-26 11:45:06 -0400431 results = []
Nicolas Penad261b062017-06-26 16:54:43 -0400432 for f in input_api.AffectedFiles(include_deletes=False):
Nicolas Penac4479c52017-06-26 11:45:06 -0400433 if not f.LocalPath().endswith('.png'):
434 continue
435 if expected_pattern.match(f.LocalPath()):
436 continue
437 results.append(output_api.PresubmitError(
438 'PNG file %s does not have the correct format' % f.LocalPath()))
439 return results
dsinclair2ca2da52016-09-13 18:10:34 -0700440
Lei Zhang624fba82021-06-15 20:11:39 +0000441
442def _CheckUselessForwardDeclarations(input_api, output_api):
443 """Checks that added or removed lines in non third party affected
444 header files do not lead to new useless class or struct forward
445 declaration.
446 """
447 results = []
448 class_pattern = input_api.re.compile(r'^class\s+(\w+);$',
449 input_api.re.MULTILINE)
450 struct_pattern = input_api.re.compile(r'^struct\s+(\w+);$',
451 input_api.re.MULTILINE)
452 for f in input_api.AffectedFiles(include_deletes=False):
453 if f.LocalPath().startswith('third_party'):
454 continue
455
456 if not f.LocalPath().endswith('.h'):
457 continue
458
459 contents = input_api.ReadFile(f)
460 fwd_decls = input_api.re.findall(class_pattern, contents)
461 fwd_decls.extend(input_api.re.findall(struct_pattern, contents))
462
463 useless_fwd_decls = []
464 for decl in fwd_decls:
465 count = sum(
466 1
467 for _ in input_api.re.finditer(r'\b%s\b' %
468 input_api.re.escape(decl), contents))
469 if count == 1:
470 useless_fwd_decls.append(decl)
471
472 if not useless_fwd_decls:
473 continue
474
475 for line in f.GenerateScmDiff().splitlines():
476 if (line.startswith('-') and not line.startswith('--') or
477 line.startswith('+') and not line.startswith('++')):
478 for decl in useless_fwd_decls:
479 if input_api.re.search(r'\b%s\b' % decl, line[1:]):
480 results.append(
481 output_api.PresubmitPromptWarning(
482 '%s: %s forward declaration is no longer needed' %
483 (f.LocalPath(), decl)))
484 useless_fwd_decls.remove(decl)
485
486 return results
487
488
Nico Weber077f1a32015-08-06 15:08:57 -0700489def CheckChangeOnUpload(input_api, output_api):
490 results = []
Daniel Hosseinianb3bdbd42021-10-22 17:01:14 +0000491 results.extend(_CheckNoBannedFunctions(input_api, output_api))
Lei Zhang804a0e32020-05-21 20:57:16 +0000492 results.extend(_CheckUnwantedDependencies(input_api, output_api))
493 results.extend(
494 input_api.canned_checks.CheckPatchFormatted(input_api, output_api))
495 results.extend(
Lei Zhang4c472552021-05-24 21:44:47 +0000496 input_api.canned_checks.CheckChangeLintsClean(
497 input_api, output_api, lint_filters=LINT_FILTERS))
Lei Zhang804a0e32020-05-21 20:57:16 +0000498 results.extend(_CheckIncludeOrder(input_api, output_api))
Lei Zhang07ef29a2022-09-10 06:52:25 +0000499 results.extend(_CheckLibcxxRevision(input_api, output_api))
Lei Zhang804a0e32020-05-21 20:57:16 +0000500 results.extend(_CheckTestDuplicates(input_api, output_api))
501 results.extend(_CheckPNGFormat(input_api, output_api))
Lei Zhang624fba82021-06-15 20:11:39 +0000502 results.extend(_CheckUselessForwardDeclarations(input_api, output_api))
Dan Sinclair544bbc62016-03-14 15:07:39 -0400503
Lei Zhang71e26732020-05-21 21:03:05 +0000504 author = input_api.change.author_email
505 if author and author not in _KNOWN_ROBOTS:
506 results.extend(
507 input_api.canned_checks.CheckAuthorizedAuthor(input_api, output_api))
508
Hui Yingst2aa0a4a2020-04-09 19:04:21 +0000509 for f in input_api.AffectedFiles():
510 path, name = input_api.os_path.split(f.LocalPath())
511 if name == 'PRESUBMIT.py':
512 full_path = input_api.os_path.join(input_api.PresubmitLocalPath(), path)
513 test_file = input_api.os_path.join(path, 'PRESUBMIT_test.py')
514 if f.Action() != 'D' and input_api.os_path.exists(test_file):
515 # The PRESUBMIT.py file (and the directory containing it) might
516 # have been affected by being moved or removed, so only try to
517 # run the tests if they still exist.
518 results.extend(
519 input_api.canned_checks.RunUnitTestsInDirectory(
520 input_api,
521 output_api,
522 full_path,
Lingqi Chied293672021-11-24 20:53:01 +0000523 files_to_check=[r'^PRESUBMIT_test\.py$'],
524 run_on_python2=not USE_PYTHON3,
525 run_on_python3=USE_PYTHON3,
526 skip_shebang_check=True))
Hui Yingst2aa0a4a2020-04-09 19:04:21 +0000527
Nico Weber077f1a32015-08-06 15:08:57 -0700528 return results