Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
Classes | Functions | Variables
PRESUBMIT Namespace Reference

Classes

class  _WarningsAsErrors
 
class  CodeReview
 

Functions

 load_source (modname, filename)
 
 runSmokeTest (input_api, output_api)
 
 CheckChangeOnCommit (input_api, output_api)
 
 CheckChangeOnUpload (input_api, output_api)
 
 EnsurePubspecAndChangelogAltered (input_api, package_name)
 
 CheckChange (input_api, output_api)
 
 CheckNodeTextExpectationsCollectorUpdatingIsDisabled (input_api, output_api)
 
 is_cpp_file (path)
 
 is_dart_file (path)
 
 get_old_contents (input_api, path)
 
 files_to_check_for_format (input_api, extension, exclude_folders)
 
 _CheckFormat (input_api, identification, extension, windows, Callable[[str, list, str], bool] hasFormatErrors, exclude_folders)
 
 _CheckDartFormat (input_api, output_api)
 
 _CheckStatusFiles (input_api, output_api)
 
 _CheckValidHostsInDEPS (input_api, output_api)
 
 _CheckLayering (input_api, output_api)
 
 _CheckClangTidy (input_api, output_api)
 
 _CheckClangFormat (input_api, output_api)
 
 _CheckAnalyzerFiles (input_api, output_api)
 
 _CheckTestMatrixValid (input_api, output_api)
 
 _CheckCopyrightYear (input_api, output_api)
 
 _CheckNoNewObservatoryServiceTests (input_api, output_api)
 
 _CommonChecks (input_api, output_api)
 
 CheckMemcpy (filename)
 
 RunLint (input_api, output_api)
 
 CheckGn (input_api, output_api)
 
 CheckFormatted (input_api, output_api)
 
 _CheckChangeHasEol (input_api, output_api, source_file_filter=None)
 
 _JsonChecks (input_api, output_api)
 
 _IfDefChecks (input_api, output_api)
 
 _CopyrightChecks (input_api, output_api, source_file_filter=None)
 
 _InfraTests (input_api, output_api)
 
 _CheckGNFormatted (input_api, output_api)
 
 _CheckGitConflictMarkers (input_api, output_api)
 
 _CheckIncludesFormatted (input_api, output_api)
 
 _RegenerateAllExamplesCPP (input_api, output_api)
 
 _CheckExamplesForPrivateAPIs (input_api, output_api)
 
 _CheckGeneratedBazelBUILDFiles (input_api, output_api)
 
 _CheckBazelBUILDFiles (input_api, output_api)
 
 _CheckPublicBzl (input_api, output_api)
 
 _RunCommandAndCheckGitDiff (output_api, command)
 
 _CheckGNIGenerated (input_api, output_api)
 
 _CheckBuildifier (input_api, output_api)
 
 _CheckBannedAPIs (input_api, output_api)
 
 _CheckDEPS (input_api, output_api)
 
 _CheckReleaseNotesForPublicAPI (input_api, output_api)
 
 _CheckTopReleaseNotesChanged (input_api, output_api)
 
 PostUploadHook (gerrit, change, output_api)
 

Variables

bool USE_PYTHON3 = True
 
str PRESUBMIT_VERSION = '2.0.0'
 
str RELEASE_NOTES_DIR = 'relnotes'
 
str RELEASE_NOTES_FILE_NAME = 'RELEASE_NOTES.md'
 
str RELEASE_NOTES_README = '//relnotes/README.md'
 
str GOLD_TRYBOT_URL = 'https://gold.skia.org/search?issue='
 
list SERVICE_ACCOUNT_SUFFIX
 

Detailed Description

CFE et al presubmit python script.

See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
for more details about the presubmit API built into gcl.
_macros package presubmit python script.

See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
for more details about the presubmit API built into gcl.
Analyzer specific presubmit script.

See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
for more details about the presubmit API built into gcl.
Top-level presubmit script for Dart.

See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
for more details about the presubmit API built into gcl.
sdk/lib specific presubmit script.

See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
for more details about the presubmit API built into gcl.
Top-level presubmit script for Skia.

See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
for more details about the presubmit API built into gcl.

Function Documentation

◆ _CheckAnalyzerFiles()

PRESUBMIT._CheckAnalyzerFiles (   input_api,
  output_api 
)
protected
Run analyzer checks on source files.

Definition at line 383 of file PRESUBMIT.py.

383def _CheckAnalyzerFiles(input_api, output_api):
384 """Run analyzer checks on source files."""
385
386 # Verify the "error fix status" file.
387 code_files = [
388 "pkg/analyzer/lib/src/error/error_code_values.g.dart",
389 "pkg/linter/lib/src/rules.dart",
390 ]
391
392 if any(f.LocalPath() in code_files for f in input_api.AffectedFiles()):
393 args = [
394 "tools/sdks/dart-sdk/bin/dart",
395 "pkg/analysis_server/tool/presubmit/verify_error_fix_status.dart",
396 ]
397 stdout = input_api.subprocess.check_output(args).strip()
398 if not stdout:
399 return []
400
401 return [
402 output_api.PresubmitError(
403 "The verify_error_fix_status Analyzer tool revealed issues:",
404 long_text=stdout)
405 ]
406
407 # Verify the linter's `example/all.yaml` file.
408 if any(f.LocalPath().startswith('pkg/linter/lib/src/rules')
409 for f in input_api.AffectedFiles()):
410 args = [
411 "tools/sdks/dart-sdk/bin/dart",
412 "pkg/linter/tool/checks/check_all_yaml.dart",
413 ]
414 stdout = input_api.subprocess.check_output(args).strip()
415 if not stdout:
416 return []
417
418 return [
419 output_api.PresubmitError(
420 "The check_all_yaml linter tool revealed issues:",
421 long_text=stdout)
422 ]
423
424 # TODO(srawlins): Check more:
425 # * "verify_sorted" for individual modified (not deleted) files in
426 # Analyzer-team-owned directories.
427 # * "verify_tests" for individual modified (not deleted) test files in
428 # Analyzer-team-owned directories.
429 # * Verify that `messages/generate.dart` does not produce different
430 # content, when `pkg/analyzer/messages.yaml` is modified.
431 # * Verify that `diagnostics/generate.dart` does not produce different
432 # content, when `pkg/analyzer/messages.yaml` is modified.
433 # * Verify that `machine.json` is not outdated, when any
434 # `pkg/linter/lib/src/rules` file is modified.
435 # * Maybe "verify_no_solo" for individual modified (not deleted test files
436 # in Analyzer-team-owned directories.
437
438 # No files are relevant.
439 return []
440
441

◆ _CheckBannedAPIs()

PRESUBMIT._CheckBannedAPIs (   input_api,
  output_api 
)
protected
Check source code for functions and packages that should not be used.

Definition at line 464 of file PRESUBMIT.py.

464def _CheckBannedAPIs(input_api, output_api):
465 """Check source code for functions and packages that should not be used."""
466
467 # A list of tuples of a regex to match an API and a suggested replacement for
468 # that API. There is an optional third parameter for files which *can* use this
469 # API without warning.
470 banned_replacements = [
471 (r'std::stof\‍(', 'std::strtof(), which does not throw'),
472 (r'std::stod\‍(', 'std::strtod(), which does not throw'),
473 (r'std::stold\‍(', 'std::strtold(), which does not throw'),
474 ]
475
476 # These defines are either there or not, and using them with just an #if is a
477 # subtle, frustrating bug.
478 existence_defines = ['SK_GANESH', 'SK_GRAPHITE', 'SK_GL', 'SK_VULKAN', 'SK_DAWN', 'SK_METAL',
479 'SK_DIRECT3D', 'SK_DEBUG', 'GR_TEST_UTILS', 'GRAPHITE_TEST_UTILS']
480 for d in existence_defines:
481 banned_replacements.append(('#if {}'.format(d),
482 '#if defined({})'.format(d)))
483 compiled_replacements = []
484 for rep in banned_replacements:
485 exceptions = []
486 if len(rep) == 3:
487 (re, replacement, exceptions) = rep
488 else:
489 (re, replacement) = rep
490
491 compiled_re = input_api.re.compile(re)
492 compiled_exceptions = [input_api.re.compile(exc) for exc in exceptions]
493 compiled_replacements.append(
494 (compiled_re, replacement, compiled_exceptions))
495
496 errors = []
497 file_filter = lambda x: (x.LocalPath().endswith('.h') or
498 x.LocalPath().endswith('.cpp') or
499 x.LocalPath().endswith('.cc') or
500 x.LocalPath().endswith('.m') or
501 x.LocalPath().endswith('.mm'))
502 for affected_file in input_api.AffectedSourceFiles(file_filter):
503 affected_filepath = affected_file.LocalPath()
504 for (line_num, line) in affected_file.ChangedContents():
505 for (re, replacement, exceptions) in compiled_replacements:
506 match = re.search(line)
507 if match:
508 for exc in exceptions:
509 if exc.search(affected_filepath):
510 break
511 else:
512 errors.append('%s:%s: Instead of %s, please use %s.' % (
513 affected_filepath, line_num, match.group(), replacement))
514
515 if errors:
516 return [output_api.PresubmitError('\n'.join(errors))]
517
518 return []
519
520
uint32_t uint32_t * format

◆ _CheckBazelBUILDFiles()

PRESUBMIT._CheckBazelBUILDFiles (   input_api,
  output_api 
)
protected
Makes sure our BUILD.bazel files are compatible with G3.

Definition at line 287 of file PRESUBMIT.py.

287def _CheckBazelBUILDFiles(input_api, output_api):
288 """Makes sure our BUILD.bazel files are compatible with G3."""
289 results = []
290 for affected_file in input_api.AffectedFiles(include_deletes=False):
291 affected_file_path = affected_file.LocalPath()
292 is_bazel = affected_file_path.endswith('BUILD.bazel')
293 # This list lines up with the one in autoroller_lib.py (see G3).
294 excluded_paths = ["infra/", "bazel/rbe/", "bazel/external/", "bazel/common_config_settings/",
295 "modules/canvaskit/go/", "experimental/", "bazel/platform", "third_party/",
296 "tests/", "resources/", "bazel/deps_parser/", "bazel/exporter_tool/",
297 "tools/gpu/gl/interface/", "bazel/utils/", "include/config/",
298 "bench/", "example/external_client/"]
299 is_excluded = any(affected_file_path.startswith(n) for n in excluded_paths)
300 if is_bazel and not is_excluded:
301 with open(affected_file_path, 'r') as file:
302 contents = file.read()
303 if 'exports_files_legacy(' not in contents:
304 results.append(output_api.PresubmitError(
305 ('%s needs to call exports_files_legacy() to support legacy G3 ' +
306 'rules.\nPut this near the top of the file, beneath ' +
307 'licenses(["notice"]).') % affected_file_path
308 ))
309 if 'licenses(["notice"])' not in contents:
310 results.append(output_api.PresubmitError(
311 ('%s needs to have\nlicenses(["notice"])\nimmediately after ' +
312 'the load() calls to comply with G3 policies.') % affected_file_path
313 ))
314 if 'cc_library(' in contents and '"skia_cc_library"' not in contents:
315 results.append(output_api.PresubmitError(
316 ('%s needs to load skia_cc_library from macros.bzl instead of using the ' +
317 'native one. This allows us to build differently for G3.\n' +
318 'Add "skia_cc_library" to load("//bazel:macros.bzl", ...)')
319 % affected_file_path
320 ))
321 if 'default_applicable_licenses' not in contents:
322 # See https://opensource.google/documentation/reference/thirdparty/new_license_rules
323 results.append(output_api.PresubmitError(
324 ('%s needs to have\npackage(default_applicable_licenses = ["//:license"])\n'+
325 'to comply with G3 policies') % affected_file_path
326 ))
327 return results
328
329

◆ _CheckBuildifier()

PRESUBMIT._CheckBuildifier (   input_api,
  output_api 
)
protected
Runs Buildifier and fails on linting errors, or if it produces any diffs.

This check only runs if the affected files include any WORKSPACE, BUILD,
BUILD.bazel or *.bzl files.

Definition at line 417 of file PRESUBMIT.py.

417def _CheckBuildifier(input_api, output_api):
418 """Runs Buildifier and fails on linting errors, or if it produces any diffs.
419
420 This check only runs if the affected files include any WORKSPACE, BUILD,
421 BUILD.bazel or *.bzl files.
422 """
423 files = []
424 # Please keep the below exclude patterns in sync with those in the //:buildifier rule definition.
425 for affected_file in input_api.AffectedFiles(include_deletes=False):
426 affected_file_path = affected_file.LocalPath()
427 if affected_file_path.endswith('BUILD.bazel') or affected_file_path.endswith('.bzl'):
428 if not affected_file_path.endswith('public.bzl') and \
429 not affected_file_path.endswith('go_repositories.bzl') and \
430 not "bazel/rbe/gce_linux/" in affected_file_path and \
431 not affected_file_path.startswith("third_party/externals/") and \
432 not "node_modules/" in affected_file_path: # Skip generated files.
433 files.append(affected_file_path)
434 if not files:
435 return []
436 try:
437 subprocess.check_output(
438 ['buildifier', '--version'],
439 stderr=subprocess.STDOUT)
440 except:
441 return [output_api.PresubmitNotifyResult(
442 'Skipping buildifier check because it is not on PATH. \n' +
443 'You can download it from https://github.com/bazelbuild/buildtools/releases')]
444
445 return _RunCommandAndCheckGitDiff(
446 # One can change --lint=warn to --lint=fix to have things automatically fixed where possible.
447 # However, --lint=fix will not cause a presubmit error if there are things that require
448 # manual intervention, so we leave --lint=warn on by default.
449 #
450 # Please keep the below arguments in sync with those in the //:buildifier rule definition.
451 output_api, [
452 'buildifier',
453 '--mode=fix',
454 '--lint=warn',
455 '--warnings',
456 ','.join([
457 '-native-android',
458 '-native-cc',
459 '-native-py',
460 ])
461 ] + files)
462
463

◆ _CheckChangeHasEol()

PRESUBMIT._CheckChangeHasEol (   input_api,
  output_api,
  source_file_filter = None 
)
protected
Checks that files end with at least one \n (LF).

Definition at line 35 of file PRESUBMIT.py.

35def _CheckChangeHasEol(input_api, output_api, source_file_filter=None):
36 """Checks that files end with at least one \n (LF)."""
37 eof_files = []
38 for f in input_api.AffectedSourceFiles(source_file_filter):
39 contents = input_api.ReadFile(f, 'rb')
40 # Check that the file ends in at least one newline character.
41 if len(contents) > 1 and contents[-1:] != '\n':
42 eof_files.append(f.LocalPath())
43
44 if eof_files:
45 return [output_api.PresubmitPromptWarning(
46 'These files should end in a newline character:',
47 items=eof_files)]
48 return []
49
50

◆ _CheckClangFormat()

PRESUBMIT._CheckClangFormat (   input_api,
  output_api 
)
protected
Run clang-format on VM changes.

Definition at line 332 of file PRESUBMIT.py.

332def _CheckClangFormat(input_api, output_api):
333 """Run clang-format on VM changes."""
334
335 # Only run clang-format on linux x64.
336 if platform.system() != 'Linux' or platform.machine() != 'x86_64':
337 return []
338
339 # Run only for modified .cc or .h files, except for DEPS changes.
340 files = []
341 is_deps = False
342 for f in input_api.AffectedFiles():
343 path = f.LocalPath()
344 if path == 'DEPS' and any(
345 map(lambda content: 'clang' in content[1],
346 f.ChangedContents())):
347 is_deps = True
348 break
349 if is_cpp_file(path) and os.path.isfile(path):
350 files.append(path)
351
352 if is_deps:
353 find_args = [
354 'find',
355 'runtime/',
356 '-iname',
357 '*.h',
358 '-o',
359 '-iname',
360 '*.cc',
361 ]
362 files = subprocess.check_output(find_args, text=True).split()
363
364 if not files:
365 return []
366
367 args = [
368 'buildtools/linux-x64/clang/bin/clang-format',
369 '--dry-run',
370 '--Werror',
371 ]
372 args.extend(files)
373 stdout = input_api.subprocess.check_output(args).strip()
374 if not stdout:
375 return []
376
377 return [
378 output_api.PresubmitError('The `clang-format` revealed issues:',
379 long_text=stdout)
380 ]
381
382

◆ _CheckClangTidy()

PRESUBMIT._CheckClangTidy (   input_api,
  output_api 
)
protected
Run clang-tidy on VM changes.

Definition at line 300 of file PRESUBMIT.py.

300def _CheckClangTidy(input_api, output_api):
301 """Run clang-tidy on VM changes."""
302
303 # Only run clang-tidy on linux x64.
304 if platform.system() != 'Linux' or platform.machine() != 'x86_64':
305 return []
306
307 # Run only for modified .cc or .h files.
308 files = []
309 for f in input_api.AffectedFiles():
310 path = f.LocalPath()
311 if is_cpp_file(path) and os.path.isfile(path): files.append(path)
312
313 if not files:
314 return []
315
316 args = [
317 'tools/sdks/dart-sdk/bin/dart',
318 'runtime/tools/run_clang_tidy.dart',
319 ]
320 args.extend(files)
321 stdout = input_api.subprocess.check_output(args).strip()
322 if not stdout:
323 return []
324
325 return [
326 output_api.PresubmitError(
327 'The `clang-tidy` linter revealed issues:',
328 long_text=stdout)
329 ]
330
331

◆ _CheckCopyrightYear()

PRESUBMIT._CheckCopyrightYear (   input_api,
  output_api 
)
protected
Check copyright year in new files.

Definition at line 472 of file PRESUBMIT.py.

472def _CheckCopyrightYear(input_api, output_api):
473 """Check copyright year in new files."""
474
475 files = []
476 year = str(datetime.datetime.now().year)
477 for f in input_api.AffectedFiles(include_deletes=False):
478 path = f.LocalPath()
479 if (is_dart_file(path) or is_cpp_file(path)
480 ) and f.Action() == 'A' and os.path.isfile(path):
481 with open(path, encoding='utf-8') as f:
482 first_line = f.readline()
483 if 'Copyright' in first_line and year not in first_line:
484 files.append(path)
485
486 if not files:
487 return []
488
489 return [
490 output_api.PresubmitPromptWarning(
491 'Copyright year for new files should be ' + year + ':\n' +
492 '\n'.join(files))
493 ]
494
495

◆ _CheckDartFormat()

PRESUBMIT._CheckDartFormat (   input_api,
  output_api 
)
protected

Definition at line 106 of file PRESUBMIT.py.

106def _CheckDartFormat(input_api, output_api):
107 local_root = input_api.change.RepositoryRoot()
108 utils = load_source('utils', os.path.join(local_root, 'tools', 'utils.py'))
109
110 dart = os.path.join(utils.CheckedInSdkPath(), 'bin', 'dart')
111
112 windows = utils.GuessOS() == 'win32'
113 if windows:
114 dart += '.exe'
115
116 if not os.path.isfile(dart):
117 print('WARNING: dart not found: %s' % (dart))
118 return []
119
120 dartFixes = [
121 '--fix-named-default-separator',
122 ]
123
124 def HasFormatErrors(filename: str = None,
125 filenames: list = None,
126 contents: str = None):
127 # Don't look for formatting errors in multitests. Since those are very
128 # sensitive to whitespace, many cannot be reformatted without breaking
129 # them.
130 def skip_file(path):
131 if path.endswith('_test.dart'):
132 with open(path, encoding='utf-8') as f:
133 contents = f.read()
134 if '//#' in contents:
135 return True
136 return False
137
138 if filename and skip_file(filename):
139 return False
140
141 args = [
142 dart,
143 'format',
144 ] + dartFixes + [
145 '--set-exit-if-changed',
146 '--output=none',
147 '--summary=none',
148 ]
149
150 # TODO(https://github.com/dart-lang/sdk/issues/46947): Remove this hack.
151 if windows and contents:
152 f = tempfile.NamedTemporaryFile(
153 encoding='utf-8',
154 delete=False,
155 mode='w',
156 suffix='.dart',
157 )
158 try:
159 f.write(contents)
160 f.close()
161 args.append(f.name)
162 process = subprocess.run(args)
163 finally:
164 os.unlink(f.name)
165 elif contents:
166 process = subprocess.run(args, input=contents, text=True)
167 elif filenames:
168 args += [f for f in filenames if not skip_file(f)]
169 process = subprocess.run(args)
170 else:
171 args.append(filename)
172 process = subprocess.run(args)
173
174 # Check for exit code 1 explicitly to distinguish it from a syntax error
175 # in the file (exit code 65). The repo contains many Dart files that are
176 # known to have syntax errors for testing purposes and which can't be
177 # parsed and formatted. Don't treat those as errors.
178 return process.returncode == 1
179
180 unformatted_files = _CheckFormat(input_api, "dart format", ".dart", windows,
181 HasFormatErrors, [])
182
183 if unformatted_files:
184 lineSep = " \\\n"
185 if windows:
186 lineSep = " ^\n"
187 return [
188 output_api.PresubmitError(
189 'File output does not match dart format.\n'
190 'Fix these issues with:\n'
191 '%s format %s%s%s' % (dart, ' '.join(dartFixes), lineSep,
192 lineSep.join(unformatted_files)))
193 ]
194
195 return []
196
197
static SkString load_source(skiatest::Reporter *r, const char *testFile, const char *permutationSuffix)
Definition SkSLTest.cpp:226
void print(void *str)
Definition bridge.cpp:126
CheckedInSdkPath()
Definition utils.py:499
GuessOS()
Definition utils.py:21

◆ _CheckDEPS()

PRESUBMIT._CheckDEPS (   input_api,
  output_api 
)
protected
If DEPS was modified, run the deps_parser to update bazel/deps.bzl

Definition at line 521 of file PRESUBMIT.py.

521def _CheckDEPS(input_api, output_api):
522 """If DEPS was modified, run the deps_parser to update bazel/deps.bzl"""
523 needs_running = False
524 for affected_file in input_api.AffectedFiles(include_deletes=False):
525 affected_file_path = affected_file.LocalPath()
526 if affected_file_path.endswith('DEPS') or affected_file_path.endswith('deps.bzl'):
527 needs_running = True
528 break
529 if not needs_running:
530 return []
531 try:
532 subprocess.check_output(
533 ['bazelisk', '--version'],
534 stderr=subprocess.STDOUT)
535 except:
536 return [output_api.PresubmitNotifyResult(
537 'Skipping DEPS check because bazelisk is not on PATH. \n' +
538 'You can download it from https://github.com/bazelbuild/bazelisk/releases/tag/v1.14.0')]
539
540 return _RunCommandAndCheckGitDiff(
541 output_api, ['bazelisk', 'run', '//bazel/deps_parser'])
542
543

◆ _CheckExamplesForPrivateAPIs()

PRESUBMIT._CheckExamplesForPrivateAPIs (   input_api,
  output_api 
)
protected
We only want our checked-in examples (aka fiddles) to show public API.

Definition at line 248 of file PRESUBMIT.py.

248def _CheckExamplesForPrivateAPIs(input_api, output_api):
249 """We only want our checked-in examples (aka fiddles) to show public API."""
250 banned_includes = [
251 input_api.re.compile(r'#\s*include\s+("src/.*)'),
252 input_api.re.compile(r'#\s*include\s+("include/private/.*)'),
253 ]
254 file_filter = lambda x: (x.LocalPath().startswith('docs/examples/'))
255 errors = []
256 for affected_file in input_api.AffectedSourceFiles(file_filter):
257 affected_filepath = affected_file.LocalPath()
258 for (line_num, line) in affected_file.ChangedContents():
259 for re in banned_includes:
260 match = re.search(line)
261 if match:
262 errors.append('%s:%s: Fiddles should not use private/internal API like %s.' % (
263 affected_filepath, line_num, match.group(1)))
264
265 if errors:
266 return [output_api.PresubmitError('\n'.join(errors))]
267 return []
268
269

◆ _CheckFormat()

PRESUBMIT._CheckFormat (   input_api,
  identification,
  extension,
  windows,
Callable[[str, list, str], bool]  hasFormatErrors,
  exclude_folders 
)
protected

Definition at line 56 of file PRESUBMIT.py.

58 bool], exclude_folders):
59 files = files_to_check_for_format(input_api, extension, exclude_folders)
60 if not files:
61 return []
62
63 # Check for formatting errors in bulk first. This is orders of magnitude
64 # faster than checking file-by-file on large changes with hundreds of files.
65 if not hasFormatErrors(filenames=[f.AbsoluteLocalPath() for f in files]):
66 return []
67
68 print("Formatting errors found, comparing against old versions.")
69 unformatted_files = []
70 for git_file in files:
71 filename = git_file.AbsoluteLocalPath()
72 if hasFormatErrors(filename=filename):
73 old_version_has_errors = False
74 try:
75 path = git_file.LocalPath()
76 if windows:
77 # Git expects a linux style path.
78 path = path.replace(os.sep, '/')
79 if hasFormatErrors(contents=get_old_contents(input_api, path)):
80 old_version_has_errors = True
81 except subprocess.CalledProcessError as e:
82 old_version_has_errors = False
83
84 if old_version_has_errors:
85 print("WARNING: %s has existing and possibly new %s issues" %
86 (git_file.LocalPath(), identification))
87 else:
88 unformatted_files.append(filename)
89
90 return unformatted_files
91
92

◆ _CheckGeneratedBazelBUILDFiles()

PRESUBMIT._CheckGeneratedBazelBUILDFiles (   input_api,
  output_api 
)
protected

Definition at line 270 of file PRESUBMIT.py.

270def _CheckGeneratedBazelBUILDFiles(input_api, output_api):
271 if 'win32' in sys.platform:
272 # TODO(crbug.com/skia/12541): Remove when Bazel builds work on Windows.
273 # Note: `make` is not installed on Windows by default.
274 return []
275 if 'darwin' in sys.platform:
276 # This takes too long on Mac with default settings. Probably due to sandboxing.
277 return []
278 for affected_file in input_api.AffectedFiles(include_deletes=True):
279 affected_file_path = affected_file.LocalPath()
280 if (affected_file_path.endswith('.go') or
281 affected_file_path.endswith('BUILD.bazel')):
282 return _RunCommandAndCheckGitDiff(output_api,
283 ['make', '-C', 'bazel', 'generate_go'])
284 return [] # No modified Go source files.
285
286

◆ _CheckGitConflictMarkers()

PRESUBMIT._CheckGitConflictMarkers (   input_api,
  output_api 
)
protected

Definition at line 184 of file PRESUBMIT.py.

184def _CheckGitConflictMarkers(input_api, output_api):
185 pattern = input_api.re.compile('^(?:<<<<<<<|>>>>>>>) |^=======$')
186 results = []
187 for f in input_api.AffectedFiles():
188 for line_num, line in f.ChangedContents():
189 if f.LocalPath().endswith('.md'):
190 # First-level headers in markdown look a lot like version control
191 # conflict markers. http://daringfireball.net/projects/markdown/basics
192 continue
193 if pattern.match(line):
194 results.append(
195 output_api.PresubmitError(
196 'Git conflict markers found in %s:%d %s' % (
197 f.LocalPath(), line_num, line)))
198 return results
199
200

◆ _CheckGNFormatted()

PRESUBMIT._CheckGNFormatted (   input_api,
  output_api 
)
protected
Make sure any .gn files we're changing have been formatted.

Definition at line 153 of file PRESUBMIT.py.

153def _CheckGNFormatted(input_api, output_api):
154 """Make sure any .gn files we're changing have been formatted."""
155 files = []
156 for f in input_api.AffectedFiles(include_deletes=False):
157 if (f.LocalPath().endswith('.gn') or
158 f.LocalPath().endswith('.gni')):
159 files.append(f)
160 if not files:
161 return []
162
163 cmd = ['python3', os.path.join('bin', 'fetch-gn')]
164 try:
165 subprocess.check_output(cmd)
166 except subprocess.CalledProcessError as e:
167 return [output_api.PresubmitError(
168 '`%s` failed:\n%s' % (' '.join(cmd), e.output))]
169
170 results = []
171 for f in files:
172 gn = 'gn.exe' if 'win32' in sys.platform else 'gn'
173 gn = os.path.join(input_api.PresubmitLocalPath(), 'bin', gn)
174 cmd = [gn, 'format', '--dry-run', f.LocalPath()]
175 try:
176 subprocess.check_output(cmd)
177 except subprocess.CalledProcessError:
178 fix = 'bin/gn format ' + f.LocalPath()
179 results.append(output_api.PresubmitError(
180 '`%s` failed, try\n\t%s' % (' '.join(cmd), fix)))
181 return results
182
183

◆ _CheckGNIGenerated()

PRESUBMIT._CheckGNIGenerated (   input_api,
  output_api 
)
protected
Ensures that the generated *.gni files are current.

The Bazel project files are authoritative and some *.gni files are
generated from them using the exporter_tool. This check ensures they
are still current.

Definition at line 385 of file PRESUBMIT.py.

385def _CheckGNIGenerated(input_api, output_api):
386 """Ensures that the generated *.gni files are current.
387
388 The Bazel project files are authoritative and some *.gni files are
389 generated from them using the exporter_tool. This check ensures they
390 are still current.
391 """
392 if 'win32' in sys.platform:
393 # TODO(crbug.com/skia/12541): Remove when Bazel builds work on Windows.
394 # Note: `make` is not installed on Windows by default.
395 return [
396 output_api.PresubmitPromptWarning(
397 'Skipping Bazel=>GNI export check on Windows (unsupported platform).'
398 )
399 ]
400 if 'darwin' in sys.platform:
401 # This takes too long on Mac with default settings. Probably due to sandboxing.
402 return []
403 should_run = False
404 for affected_file in input_api.AffectedFiles(include_deletes=True):
405 affected_file_path = affected_file.LocalPath()
406 if affected_file_path.endswith('BUILD.bazel') or affected_file_path.endswith('.gni'):
407 should_run = True
408 # Generate GNI files and verify no changes.
409 if should_run:
410 return _RunCommandAndCheckGitDiff(output_api,
411 ['make', '-C', 'bazel', 'generate_gni'])
412
413 # No Bazel build files changed.
414 return []
415
416

◆ _CheckIncludesFormatted()

PRESUBMIT._CheckIncludesFormatted (   input_api,
  output_api 
)
protected
Make sure #includes in files we're changing have been formatted.

Definition at line 201 of file PRESUBMIT.py.

201def _CheckIncludesFormatted(input_api, output_api):
202 """Make sure #includes in files we're changing have been formatted."""
203 files = [str(f) for f in input_api.AffectedFiles() if f.Action() != 'D']
204 cmd = ['python3',
205 'tools/rewrite_includes.py',
206 '--dry-run'] + files
207 if 0 != subprocess.call(cmd):
208 return [output_api.PresubmitError('`%s` failed' % ' '.join(cmd))]
209 return []
210
211

◆ _CheckLayering()

PRESUBMIT._CheckLayering (   input_api,
  output_api 
)
protected
Run VM layering check.

This check validates that sources from one layer do not reference sources
from another layer accidentally.

Definition at line 268 of file PRESUBMIT.py.

268def _CheckLayering(input_api, output_api):
269 """Run VM layering check.
270
271 This check validates that sources from one layer do not reference sources
272 from another layer accidentally.
273 """
274
275 # Run only if .cc or .h file was modified.
276 if all(not is_cpp_file(f.LocalPath()) for f in input_api.AffectedFiles()):
277 return []
278
279 local_root = input_api.change.RepositoryRoot()
280 compiler_layering_check = load_source(
281 'compiler_layering_check',
282 os.path.join(local_root, 'runtime', 'tools',
283 'compiler_layering_check.py'))
284 errors = compiler_layering_check.DoCheck(local_root)
285 embedder_layering_check = load_source(
286 'embedder_layering_check',
287 os.path.join(local_root, 'runtime', 'tools',
288 'embedder_layering_check.py'))
289 errors += embedder_layering_check.DoCheck(local_root)
290 if errors:
291 return [
292 output_api.PresubmitError(
293 'Layering check violation for C++ sources.',
294 long_text='\n'.join(errors))
295 ]
296
297 return []
298
299

◆ _CheckNoNewObservatoryServiceTests()

PRESUBMIT._CheckNoNewObservatoryServiceTests (   input_api,
  output_api 
)
protected
Ensures that no new tests are added to the Observatory test suite.

Definition at line 496 of file PRESUBMIT.py.

496def _CheckNoNewObservatoryServiceTests(input_api, output_api):
497 """Ensures that no new tests are added to the Observatory test suite."""
498 files = []
499
500 for f in input_api.AffectedFiles(include_deletes=False):
501 path = f.LocalPath()
502 if is_dart_file(path) and path.startswith(
503 "runtime/observatory/tests/service/") and f.Action(
504 ) == 'A' and os.path.isfile(path):
505 files.append(path)
506
507 if not files:
508 return []
509
510 return [
511 output_api.PresubmitError(
512 'New VM service tests should be added to pkg/vm_service/test, ' +
513 'not runtime/observatory/tests/service:\n' + '\n'.join(files))
514 ]
515

◆ _CheckPublicBzl()

PRESUBMIT._CheckPublicBzl (   input_api,
  output_api 
)
protected
Reminds devs to add/remove files from public.bzl.

Definition at line 330 of file PRESUBMIT.py.

330def _CheckPublicBzl(input_api, output_api):
331 """Reminds devs to add/remove files from public.bzl."""
332 results = []
333 public_bzl = ''
334 with open('public.bzl', 'r', encoding='utf-8') as f:
335 public_bzl = f.read().strip()
336 for affected_file in input_api.AffectedFiles(include_deletes=True):
337 # action is A for newly added, D for newly deleted, M for modified
338 action = affected_file.Action()
339 affected_file_path = affected_file.LocalPath()
340 if ((affected_file_path.startswith("include") or affected_file_path.startswith("src")) and
341 (affected_file_path.endswith(".cpp") or affected_file_path.endswith(".h") or
342 affected_file_path.endswith(".mm"))):
343 affected_file_path = '"' + affected_file_path + '"'
344 if action == "D" and affected_file_path in public_bzl:
345 results.append(output_api.PresubmitError(
346 "Need to delete %s from public.bzl (or rename it)" % affected_file_path))
347 elif action == "A" and affected_file_path not in public_bzl:
348 results.append(output_api.PresubmitPromptWarning(
349 "You may need to add %s to public.bzl" % affected_file_path))
350 return results
351
352

◆ _CheckReleaseNotesForPublicAPI()

PRESUBMIT._CheckReleaseNotesForPublicAPI (   input_api,
  output_api 
)
protected
Checks to see if a release notes file is added or edited with public API changes.

Definition at line 630 of file PRESUBMIT.py.

630def _CheckReleaseNotesForPublicAPI(input_api, output_api):
631 """Checks to see if a release notes file is added or edited with public API changes."""
632 results = []
633 public_api_changed = False
634 release_file_changed = False
635 for affected_file in input_api.AffectedFiles():
636 affected_file_path = affected_file.LocalPath()
637 file_path, file_ext = os.path.splitext(affected_file_path)
638 # We only care about files that end in .h and are under the top-level
639 # include dir, but not include/private.
640 if (file_ext == '.h' and
641 file_path.split(os.path.sep)[0] == 'include' and
642 'private' not in file_path):
643 public_api_changed = True
644 elif os.path.dirname(file_path) == RELEASE_NOTES_DIR:
645 release_file_changed = True
646
647 if public_api_changed and not release_file_changed:
648 results.append(output_api.PresubmitPromptWarning(
649 'If this change affects a client API, please add a new summary '
650 'file in the %s directory. More information can be found in '
651 '%s.' % (RELEASE_NOTES_DIR, RELEASE_NOTES_README)))
652 return results
653
654

◆ _CheckStatusFiles()

PRESUBMIT._CheckStatusFiles (   input_api,
  output_api 
)
protected

Definition at line 198 of file PRESUBMIT.py.

198def _CheckStatusFiles(input_api, output_api):
199 local_root = input_api.change.RepositoryRoot()
200 utils = load_source('utils', os.path.join(local_root, 'tools', 'utils.py'))
201
202 dart = os.path.join(utils.CheckedInSdkPath(), 'bin', 'dart')
203 lint = os.path.join(local_root, 'pkg', 'status_file', 'bin', 'lint.dart')
204
205 windows = utils.GuessOS() == 'win32'
206 if windows:
207 dart += '.exe'
208
209 if not os.path.isfile(dart):
210 print('WARNING: dart not found: %s' % dart)
211 return []
212
213 if not os.path.isfile(lint):
214 print('WARNING: Status file linter not found: %s' % lint)
215 return []
216
217 def HasFormatErrors(filename=None, filenames=None, contents=None):
218 if filenames:
219 # The status file linter doesn't support checking files in bulk.
220 # Returning `True` causes `_CheckFormat` to fallback to check
221 # formatting file by file below.
222 return True
223 args = [dart, lint] + (['-t'] if contents else [filename])
224 process = subprocess.run(args, input=contents, text=True)
225 return process.returncode != 0
226
227 exclude_folders = [
228 "pkg/status_file/test/data/",
229 "pkg/front_end/",
230 ]
231 unformatted_files = _CheckFormat(input_api, "status file", ".status",
232 windows, HasFormatErrors, exclude_folders)
233
234 if unformatted_files:
235 normalize = os.path.join(local_root, 'pkg', 'status_file', 'bin',
236 'normalize.dart')
237 lineSep = " \\\n"
238 if windows:
239 lineSep = " ^\n"
240 return [
241 output_api.PresubmitError(
242 'Status files are not normalized.\n'
243 'Fix these issues with:\n'
244 '%s %s -w%s%s' % (dart, normalize, lineSep,
245 lineSep.join(unformatted_files)))
246 ]
247
248 return []
249
250

◆ _CheckTestMatrixValid()

PRESUBMIT._CheckTestMatrixValid (   input_api,
  output_api 
)
protected
Run script to check that the test matrix has no errors.

Definition at line 442 of file PRESUBMIT.py.

442def _CheckTestMatrixValid(input_api, output_api):
443 """Run script to check that the test matrix has no errors."""
444
445 def test_matrix_filter(affected_file):
446 """Only run test if either the test matrix or the code that
447 validates it was modified."""
448 path = affected_file.LocalPath()
449 return (path == 'tools/bots/test_matrix.json' or
450 path == 'tools/validate_test_matrix.dart' or
451 path.startswith('pkg/smith/'))
452
453 if len(
454 input_api.AffectedFiles(
455 include_deletes=False, file_filter=test_matrix_filter)) == 0:
456 return []
457
458 command = [
459 'tools/sdks/dart-sdk/bin/dart',
460 'tools/validate_test_matrix.dart',
461 ]
462 stdout = input_api.subprocess.check_output(command).strip()
463 if not stdout:
464 return []
465 else:
466 return [
467 output_api.PresubmitError(
468 'The test matrix is not valid:', long_text=stdout)
469 ]
470
471

◆ _CheckTopReleaseNotesChanged()

PRESUBMIT._CheckTopReleaseNotesChanged (   input_api,
  output_api 
)
protected
Warns if the top level release notes file was changed.

The top level file is now auto-edited, and new release notes should
be added to the RELEASE_NOTES_DIR directory

Definition at line 655 of file PRESUBMIT.py.

655def _CheckTopReleaseNotesChanged(input_api, output_api):
656 """Warns if the top level release notes file was changed.
657
658 The top level file is now auto-edited, and new release notes should
659 be added to the RELEASE_NOTES_DIR directory"""
660 results = []
661 top_relnotes_changed = False
662 release_file_changed = False
663 for affected_file in input_api.AffectedFiles():
664 affected_file_path = affected_file.LocalPath()
665 file_path, file_ext = os.path.splitext(affected_file_path)
666 if affected_file_path == RELEASE_NOTES_FILE_NAME:
667 top_relnotes_changed = True
668 elif os.path.dirname(file_path) == RELEASE_NOTES_DIR:
669 release_file_changed = True
670 # When relnotes_util is run it will modify RELEASE_NOTES_FILE_NAME
671 # and delete the individual note files in RELEASE_NOTES_DIR.
672 # So, if both paths are modified do not emit a warning.
673 if top_relnotes_changed and not release_file_changed:
674 results.append(output_api.PresubmitPromptWarning(
675 'Do not edit %s directly. %s is automatically edited during the '
676 'release process. Release notes should be added as new files in '
677 'the %s directory. More information can be found in %s.' % (RELEASE_NOTES_FILE_NAME,
678 RELEASE_NOTES_FILE_NAME,
679 RELEASE_NOTES_DIR,
680 RELEASE_NOTES_README)))
681 return results
682
683

◆ _CheckValidHostsInDEPS()

PRESUBMIT._CheckValidHostsInDEPS (   input_api,
  output_api 
)
protected
Checks that DEPS file deps are from allowed_hosts.

Definition at line 251 of file PRESUBMIT.py.

251def _CheckValidHostsInDEPS(input_api, output_api):
252 """Checks that DEPS file deps are from allowed_hosts."""
253 # Run only if DEPS file has been modified to annoy fewer bystanders.
254 if all(f.LocalPath() != 'DEPS' for f in input_api.AffectedFiles()):
255 return []
256 # Outsource work to gclient verify
257 try:
258 input_api.subprocess.check_output(['gclient', 'verify'])
259 return []
260 except input_api.subprocess.CalledProcessError as error:
261 return [
262 output_api.PresubmitError(
263 'DEPS file must have only dependencies from allowed hosts.',
264 long_text=error.output)
265 ]
266
267

◆ _CommonChecks()

PRESUBMIT._CommonChecks ( input_api  ,
output_api   
)
protected
Presubmit checks common to upload and commit.

Definition at line 516 of file PRESUBMIT.py.

516def _CommonChecks(input_api, output_api):
517 results = []
518 results.extend(_CheckValidHostsInDEPS(input_api, output_api))
519 results.extend(_CheckDartFormat(input_api, output_api))
520 results.extend(_CheckStatusFiles(input_api, output_api))
521 results.extend(_CheckLayering(input_api, output_api))
522 results.extend(_CheckClangTidy(input_api, output_api))
523 results.extend(_CheckClangFormat(input_api, output_api))
524 results.extend(_CheckTestMatrixValid(input_api, output_api))
525 results.extend(
526 input_api.canned_checks.CheckPatchFormatted(input_api, output_api))
527 results.extend(_CheckCopyrightYear(input_api, output_api))
528 results.extend(_CheckAnalyzerFiles(input_api, output_api))
529 results.extend(_CheckNoNewObservatoryServiceTests(input_api, output_api))
530 return results
531
532

◆ _CopyrightChecks()

PRESUBMIT._CopyrightChecks (   input_api,
  output_api,
  source_file_filter = None 
)
protected

Definition at line 115 of file PRESUBMIT.py.

115def _CopyrightChecks(input_api, output_api, source_file_filter=None):
116 results = []
117 year_pattern = r'\d{4}'
118 year_range_pattern = r'%s(-%s)?' % (year_pattern, year_pattern)
119 years_pattern = r'%s(,%s)*,?' % (year_range_pattern, year_range_pattern)
120 copyright_pattern = (
121 r'Copyright (\‍([cC]\‍) )?%s \w+' % years_pattern)
122
123 for affected_file in input_api.AffectedSourceFiles(source_file_filter):
124 if ('third_party/' in affected_file.LocalPath() or
125 'tests/sksl/' in affected_file.LocalPath() or
126 'bazel/rbe/' in affected_file.LocalPath() or
127 'bazel/external/' in affected_file.LocalPath() or
128 'bazel/exporter/interfaces/mocks/' in affected_file.LocalPath()):
129 continue
130 contents = input_api.ReadFile(affected_file, 'rb')
131 if not re.search(copyright_pattern, contents):
132 results.append(output_api.PresubmitError(
133 '%s is missing a correct copyright header.' % affected_file))
134 return results
135
136

◆ _IfDefChecks()

PRESUBMIT._IfDefChecks (   input_api,
  output_api 
)
protected
Ensures if/ifdef are not before includes. See skbug/3362 for details.

Definition at line 74 of file PRESUBMIT.py.

74def _IfDefChecks(input_api, output_api):
75 """Ensures if/ifdef are not before includes. See skbug/3362 for details."""
76 comment_block_start_pattern = re.compile('^\s*\/\*.*$')
77 comment_block_middle_pattern = re.compile('^\s+\*.*')
78 comment_block_end_pattern = re.compile('^\s+\*\/.*$')
79 single_line_comment_pattern = re.compile('^\s*//.*$')
80 def is_comment(line):
81 return (comment_block_start_pattern.match(line) or
82 comment_block_middle_pattern.match(line) or
83 comment_block_end_pattern.match(line) or
84 single_line_comment_pattern.match(line))
85
86 empty_line_pattern = re.compile('^\s*$')
87 def is_empty_line(line):
88 return empty_line_pattern.match(line)
89
90 failing_files = []
91 for affected_file in input_api.AffectedSourceFiles(None):
92 affected_file_path = affected_file.LocalPath()
93 if affected_file_path.endswith('.cpp') or affected_file_path.endswith('.h'):
94 f = open(affected_file_path)
95 for line in f:
96 if is_comment(line) or is_empty_line(line):
97 continue
98 # The below will be the first real line after comments and newlines.
99 if line.startswith('#if 0 '):
100 pass
101 elif line.startswith('#if ') or line.startswith('#ifdef '):
102 failing_files.append(affected_file_path)
103 break
104
105 results = []
106 if failing_files:
107 results.append(
108 output_api.PresubmitError(
109 'The following files have #if or #ifdef before includes:\n%s\n\n'
110 'See https://bug.skia.org/3362 for why this should be fixed.' %
111 '\n'.join(failing_files)))
112 return results
113
114

◆ _InfraTests()

PRESUBMIT._InfraTests (   input_api,
  output_api 
)
protected
Run the infra tests.

Definition at line 137 of file PRESUBMIT.py.

137def _InfraTests(input_api, output_api):
138 """Run the infra tests."""
139 results = []
140 if not any(f.LocalPath().startswith('infra')
141 for f in input_api.AffectedFiles()):
142 return results
143
144 cmd = ['python3', os.path.join('infra', 'bots', 'infra_tests.py')]
145 try:
146 subprocess.check_output(cmd)
147 except subprocess.CalledProcessError as e:
148 results.append(output_api.PresubmitError(
149 '`%s` failed:\n%s' % (' '.join(cmd), e.output)))
150 return results
151
152

◆ _JsonChecks()

PRESUBMIT._JsonChecks (   input_api,
  output_api 
)
protected
Run checks on any modified json files.

Definition at line 51 of file PRESUBMIT.py.

51def _JsonChecks(input_api, output_api):
52 """Run checks on any modified json files."""
53 failing_files = []
54 for affected_file in input_api.AffectedFiles(None):
55 affected_file_path = affected_file.LocalPath()
56 is_json = affected_file_path.endswith('.json')
57 is_metadata = (affected_file_path.startswith('site/') and
58 affected_file_path.endswith('/METADATA'))
59 if is_json or is_metadata:
60 try:
61 input_api.json.load(open(affected_file_path, 'r'))
62 except ValueError:
63 failing_files.append(affected_file_path)
64
65 results = []
66 if failing_files:
67 results.append(
68 output_api.PresubmitError(
69 'The following files contain invalid json:\n%s\n\n' %
70 '\n'.join(failing_files)))
71 return results
72
73

◆ _RegenerateAllExamplesCPP()

PRESUBMIT._RegenerateAllExamplesCPP (   input_api,
  output_api 
)
protected
Regenerates all_examples.cpp if an example was added or deleted.

Definition at line 224 of file PRESUBMIT.py.

224def _RegenerateAllExamplesCPP(input_api, output_api):
225 """Regenerates all_examples.cpp if an example was added or deleted."""
226 if not any(f.LocalPath().startswith('docs/examples/')
227 for f in input_api.AffectedFiles()):
228 return []
229 command_str = 'tools/fiddle/make_all_examples_cpp.py'
230 cmd = ['python3', command_str]
231 if 0 != subprocess.call(cmd):
232 return [output_api.PresubmitError('`%s` failed' % ' '.join(cmd))]
233
234 results = []
235 git_diff_output = input_api.subprocess.check_output(
236 ['git', 'diff', '--no-ext-diff'])
237 if git_diff_output:
238 results += [output_api.PresubmitError(
239 'Diffs found after running "%s":\n\n%s\n'
240 'Please commit or discard the above changes.' % (
241 command_str,
242 git_diff_output,
243 )
244 )]
245 return results
246
247

◆ _RunCommandAndCheckGitDiff()

PRESUBMIT._RunCommandAndCheckGitDiff (   output_api,
  command 
)
protected
Run an arbitrary command. Fail if it produces any diffs.

Definition at line 353 of file PRESUBMIT.py.

353def _RunCommandAndCheckGitDiff(output_api, command):
354 """Run an arbitrary command. Fail if it produces any diffs."""
355 command_str = ' '.join(command)
356 results = []
357
358 try:
359 output = subprocess.check_output(
360 command,
361 stderr=subprocess.STDOUT, encoding='utf-8')
362 except subprocess.CalledProcessError as e:
363 results += [output_api.PresubmitError(
364 'Command "%s" returned non-zero exit code %d. Output: \n\n%s' % (
365 command_str,
366 e.returncode,
367 e.output,
368 )
369 )]
370
371 git_diff_output = subprocess.check_output(
372 ['git', 'diff', '--no-ext-diff'], encoding='utf-8')
373 if git_diff_output:
374 results += [output_api.PresubmitError(
375 'Diffs found after running "%s":\n\n%s\n'
376 'Please commit or discard the above changes.' % (
377 command_str,
378 git_diff_output,
379 )
380 )]
381
382 return results
383
384

◆ CheckChange()

PRESUBMIT.CheckChange (   input_api,
  output_api 
)

Definition at line 41 of file PRESUBMIT.py.

41def CheckChange(input_api, output_api):
42 errors = []
43
44 # If the `lib` dir is altered, we also require a change to the pubspec.yaml
45 # of both this package and the `macros` package.
46 lib_changed = any(file.LocalPath().startswith('pkg/_macros/lib')
47 for file in input_api.AffectedFiles())
48 if lib_changed:
49 errors += EnsurePubspecAndChangelogAltered(input_api, '_macros')
50 errors += EnsurePubspecAndChangelogAltered(input_api, 'macros')
51
52 if errors:
53 return [
54 output_api.PresubmitError(
55 'pkg/_macros presubmit/PRESUBMIT.py failure(s):',
56 long_text='\n\n'.join(errors))
57 ]
58
59 return []

◆ CheckChangeOnCommit()

PRESUBMIT.CheckChangeOnCommit ( input_api  ,
output_api   
)
Presubmit checks for the change on commit.

Definition at line 66 of file PRESUBMIT.py.

66def CheckChangeOnCommit(input_api, output_api):
67 return runSmokeTest(input_api, output_api)
68
69

◆ CheckChangeOnUpload()

PRESUBMIT.CheckChangeOnUpload ( input_api  ,
output_api   
)
Presubmit checks for the change on upload.

Definition at line 70 of file PRESUBMIT.py.

70def CheckChangeOnUpload(input_api, output_api):
71 return runSmokeTest(input_api, output_api)

◆ CheckFormatted()

PRESUBMIT.CheckFormatted (   input_api,
  output_api 
)

Definition at line 62 of file PRESUBMIT.py.

62def CheckFormatted(input_api, output_api):
63
64 def convert_warning_to_error(presubmit_result):
65 if not presubmit_result.fatal:
66 # Convert this warning to an error.
67 result_json = presubmit_result.json_format()
68 return output_api.PresubmitError(
69 message=result_json['message'],
70 items=result_json['items'],
71 long_text=result_json['long_text'])
72 return presubmit_result
73
74 results = input_api.canned_checks.CheckPatchFormatted(input_api, output_api)
75 return [convert_warning_to_error(r) for r in results]
76
77

◆ CheckGn()

PRESUBMIT.CheckGn (   input_api,
  output_api 
)

Definition at line 58 of file PRESUBMIT.py.

58def CheckGn(input_api, output_api):
59 return input_api.canned_checks.CheckGNFormatted(input_api, output_api)
60
61

◆ CheckMemcpy()

PRESUBMIT.CheckMemcpy (   filename)

Definition at line 17 of file PRESUBMIT.py.

17def CheckMemcpy(filename):
18 if filename.endswith(os.path.join('platform', 'globals.h')) or \
19 filename.find('third_party') != -1:
20 return 0
21 fh = open(filename, 'r')
22 content = fh.read()
23 match = re.search('\\bmemcpy\\b', content)
24 if match:
25 offset = match.start()
26 end_of_line = content.index('\n', offset)
27 # We allow explicit use of memcpy with an opt-in via NOLINT
28 if 'NOLINT' not in content[offset:end_of_line]:
29 line_number = content[0:match.start()].count('\n') + 1
30 print("%s:%d: use of memcpy is forbidden" % (filename, line_number))
31 return 1
32 return 0
33
34
int count

◆ CheckNodeTextExpectationsCollectorUpdatingIsDisabled()

PRESUBMIT.CheckNodeTextExpectationsCollectorUpdatingIsDisabled (   input_api,
  output_api 
)

Definition at line 18 of file PRESUBMIT.py.

18def CheckNodeTextExpectationsCollectorUpdatingIsDisabled(input_api, output_api):
19 local_root = input_api.change.RepositoryRoot()
20 node_text_expectations_file = os.path.join(local_root, 'pkg', 'analyzer',
21 'test', 'src', 'dart',
22 'resolution',
23 'node_text_expectations.dart')
24 for git_file in input_api.AffectedTestableFiles():
25 filename = git_file.AbsoluteLocalPath()
26 if (filename == node_text_expectations_file):
27 isEnabledLine = re.compile('static const updatingIsEnabled = (.*);')
28 for line in git_file.NewContents():
29 m = isEnabledLine.search(line)
30 if (m is not None):
31 value = m.group(1)
32 if (value == 'false'):
33 return []
34 else:
35 return [
36 output_api.PresubmitError(
37 'NodeTextExpectationsCollector.updatingIsEnabled '
38 'must be `false`')
39 ]
40 return [
41 output_api.PresubmitError(
42 'Could not validate '
43 'NodeTextExpectationsCollector.updatingIsEnabled')
44 ]
45 return []

◆ EnsurePubspecAndChangelogAltered()

PRESUBMIT.EnsurePubspecAndChangelogAltered (   input_api,
  package_name 
)

Definition at line 20 of file PRESUBMIT.py.

20def EnsurePubspecAndChangelogAltered(input_api, package_name):
21 errors = []
22 package_path = 'pkg/%s' % package_name
23 pubspec_path = '%s/pubspec.yaml' % package_path
24 pubspec_changed = any(file.LocalPath() == pubspec_path
25 for file in input_api.change.AffectedFiles())
26 if not pubspec_changed:
27 errors.append(
28 ('The pkg/_macros/lib dir was altered but the version of %s was '
29 'not bumped. See pkg/_macros/CONTRIBUTING.md' % package_path))
30
31 changelog_path = '%s/CHANGELOG.md' % package_path
32 changelog_changed = any(file.LocalPath() == changelog_path
33 for file in input_api.change.AffectedFiles())
34 if not changelog_changed:
35 errors.append(
36 ('The pkg/_macros/lib dir was altered but the CHANGELOG.md of %s '
37 'was not edited. See pkg/_macros/CONTRIBUTING.md' % package_path))
38 return errors
39
40# Invoked on upload and commit.

◆ files_to_check_for_format()

PRESUBMIT.files_to_check_for_format (   input_api,
  extension,
  exclude_folders 
)

Definition at line 41 of file PRESUBMIT.py.

41def files_to_check_for_format(input_api, extension, exclude_folders):
42 files = []
43 exclude_folders += [
44 "pkg/front_end/testcases/", "pkg/front_end/parser_testcases/"
45 ]
46 for git_file in input_api.AffectedTextFiles():
47 local_path = git_file.LocalPath()
48 if not local_path.endswith(extension):
49 continue
50 if any([local_path.startswith(f) for f in exclude_folders]):
51 continue
52 files.append(git_file)
53 return files
54
55

◆ get_old_contents()

PRESUBMIT.get_old_contents (   input_api,
  path 
)

Definition at line 33 of file PRESUBMIT.py.

33def get_old_contents(input_api, path):
34 local_root = input_api.change.RepositoryRoot()
35 upstream = input_api.change._upstream
36 return scm.GIT.Capture(['show', upstream + ':' + path],
37 cwd=local_root,
38 strip_out=False)
39
40

◆ is_cpp_file()

PRESUBMIT.is_cpp_file (   path)

Definition at line 25 of file PRESUBMIT.py.

25def is_cpp_file(path):
26 return path.endswith('.cc') or path.endswith('.h')
27
28

◆ is_dart_file()

PRESUBMIT.is_dart_file (   path)

Definition at line 29 of file PRESUBMIT.py.

29def is_dart_file(path):
30 return path.endswith('.dart')
31
32

◆ load_source()

PRESUBMIT.load_source (   modname,
  filename 
)

Definition at line 19 of file PRESUBMIT.py.

19def load_source(modname, filename):
20 loader = importlib.machinery.SourceFileLoader(modname, filename)
21 spec = importlib.util.spec_from_file_location(modname,
22 filename,
23 loader=loader)
24 module = importlib.util.module_from_spec(spec)
25 # The module is always executed and not cached in sys.modules.
26 # Uncomment the following line to cache the module.
27 # sys.modules[module.__name__] = module
28 loader.exec_module(module)
29 return module
30
31

◆ PostUploadHook()

PRESUBMIT.PostUploadHook (   gerrit,
  change,
  output_api 
)
git cl upload will call this hook after the issue is created/modified.

This hook does the following:
* Adds a link to preview docs changes if there are any docs changes in the CL.
* Adds 'No-Try: true' if the CL contains only docs changes.

Definition at line 684 of file PRESUBMIT.py.

684def PostUploadHook(gerrit, change, output_api):
685 """git cl upload will call this hook after the issue is created/modified.
686
687 This hook does the following:
688 * Adds a link to preview docs changes if there are any docs changes in the CL.
689 * Adds 'No-Try: true' if the CL contains only docs changes.
690 """
691 if not change.issue:
692 return []
693
694 # Skip PostUploadHooks for all auto-commit service account bots. New
695 # patchsets (caused due to PostUploadHooks) invalidates the CQ+2 vote from
696 # the "--use-commit-queue" flag to "git cl upload".
697 for suffix in SERVICE_ACCOUNT_SUFFIX:
698 if change.author_email.endswith(suffix):
699 return []
700
701 results = []
702 at_least_one_docs_change = False
703 all_docs_changes = True
704 for affected_file in change.AffectedFiles():
705 affected_file_path = affected_file.LocalPath()
706 file_path, _ = os.path.splitext(affected_file_path)
707 if 'site' == file_path.split(os.path.sep)[0]:
708 at_least_one_docs_change = True
709 else:
710 all_docs_changes = False
711 if at_least_one_docs_change and not all_docs_changes:
712 break
713
714 footers = change.GitFootersFromDescription()
715 description_changed = False
716
717 # If the change includes only doc changes then add No-Try: true in the
718 # CL's description if it does not exist yet.
719 if all_docs_changes and 'true' not in footers.get('No-Try', []):
720 description_changed = True
721 change.AddDescriptionFooter('No-Try', 'true')
722 results.append(
723 output_api.PresubmitNotifyResult(
724 'This change has only doc changes. Automatically added '
725 '\'No-Try: true\' to the CL\'s description'))
726
727 # If the description has changed update it.
728 if description_changed:
729 gerrit.UpdateDescription(
730 change.FullDescriptionText(), change.issue)
731
732 return results
733
734

◆ RunLint()

PRESUBMIT.RunLint (   input_api,
  output_api 
)

Definition at line 35 of file PRESUBMIT.py.

35def RunLint(input_api, output_api):
36 result = []
37 cpplint._cpplint_state.ResetErrorCounts()
38 memcpy_match_count = 0
39 # Find all .cc and .h files in the change list.
40 for git_file in input_api.AffectedTextFiles():
41 filename = git_file.AbsoluteLocalPath()
42 if filename.endswith('.cc') or (
43 # cpplint complains about the style of #ifndefs in our .pbzero.h
44 # files, but they are generated by the protozero compiler, so we
45 # can't fix this.
46 not filename.endswith('.pbzero.h') and filename.endswith('.h')):
47 # Run cpplint on the file.
48 cpplint.ProcessFile(filename, 1)
49 # Check for memcpy use.
50 memcpy_match_count += CheckMemcpy(filename)
51
52 # Report a presubmit error if any of the files had an error.
53 if cpplint._cpplint_state.error_count > 0 or memcpy_match_count > 0:
54 result = [output_api.PresubmitError('Failed cpplint check.')]
55 return result
56
57

◆ runSmokeTest()

PRESUBMIT.runSmokeTest (   input_api,
  output_api 
)

Definition at line 32 of file PRESUBMIT.py.

32def runSmokeTest(input_api, output_api):
33 local_root = input_api.change.RepositoryRoot()
34 utils = load_source('utils', os.path.join(local_root, 'tools', 'utils.py'))
35 dart = os.path.join(utils.CheckedInSdkPath(), 'bin', 'dart')
36 test_helper = os.path.join(local_root, 'pkg', 'front_end',
37 'presubmit_helper.dart')
38
39 windows = utils.GuessOS() == 'win32'
40 if windows:
41 dart += '.exe'
42
43 if not os.path.isfile(dart):
44 print('WARNING: dart not found: %s' % dart)
45 return []
46
47 if not os.path.isfile(test_helper):
48 print('WARNING: CFE et al presubmit_helper not found: %s' % test_helper)
49 return []
50
51 args = [dart, test_helper, input_api.PresubmitLocalPath()]
52 process = subprocess.Popen(args,
53 stdout=subprocess.PIPE,
54 stdin=subprocess.PIPE)
55 outs, _ = process.communicate()
56
57 if process.returncode != 0:
58 return [
59 output_api.PresubmitError('CFE et al presubmit script failure(s):',
60 long_text=outs)
61 ]
62
63 return []
64
65

Variable Documentation

◆ GOLD_TRYBOT_URL

str PRESUBMIT.GOLD_TRYBOT_URL = 'https://gold.skia.org/search?issue='

Definition at line 25 of file PRESUBMIT.py.

◆ PRESUBMIT_VERSION

str PRESUBMIT.PRESUBMIT_VERSION = '2.0.0'

Definition at line 11 of file PRESUBMIT.py.

◆ RELEASE_NOTES_DIR

str PRESUBMIT.RELEASE_NOTES_DIR = 'relnotes'

Definition at line 21 of file PRESUBMIT.py.

◆ RELEASE_NOTES_FILE_NAME

str PRESUBMIT.RELEASE_NOTES_FILE_NAME = 'RELEASE_NOTES.md'

Definition at line 22 of file PRESUBMIT.py.

◆ RELEASE_NOTES_README

str PRESUBMIT.RELEASE_NOTES_README = '//relnotes/README.md'

Definition at line 23 of file PRESUBMIT.py.

◆ SERVICE_ACCOUNT_SUFFIX

list PRESUBMIT.SERVICE_ACCOUNT_SUFFIX
Initial value:
1= [
2 '@%s.iam.gserviceaccount.com' % project for project in [
3 'skia-buildbots.google.com', 'skia-swarming-bots', 'skia-public',
4 'skia-corp.google.com', 'chops-service-accounts']]

Definition at line 27 of file PRESUBMIT.py.

◆ USE_PYTHON3

bool PRESUBMIT.USE_PYTHON3 = True

Definition at line 16 of file PRESUBMIT.py.