Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/angle
Path: blob/main_old/PRESUBMIT.py
1691 views
1
# Copyright 2019 The ANGLE Project 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
"""Top-level presubmit script for code generation.
5
6
See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
7
for more details on the presubmit API built into depot_tools.
8
"""
9
10
import os
11
import re
12
import shutil
13
import subprocess
14
import sys
15
import tempfile
16
17
# Fragment of a regular expression that matches C/C++ and Objective-C++ implementation files and headers.
18
_IMPLEMENTATION_AND_HEADER_EXTENSIONS = r'\.(c|cc|cpp|cxx|mm|h|hpp|hxx)$'
19
20
# Fragment of a regular expression that matches C++ and Objective-C++ header files.
21
_HEADER_EXTENSIONS = r'\.(h|hpp|hxx)$'
22
23
_PRIMARY_EXPORT_TARGETS = [
24
'//:libEGL',
25
'//:libGLESv1_CM',
26
'//:libGLESv2',
27
'//:translator',
28
]
29
30
31
def _CheckCommitMessageFormatting(input_api, output_api):
32
33
def _IsLineBlank(line):
34
return line.isspace() or line == ""
35
36
def _PopBlankLines(lines, reverse=False):
37
if reverse:
38
while len(lines) > 0 and _IsLineBlank(lines[-1]):
39
lines.pop()
40
else:
41
while len(lines) > 0 and _IsLineBlank(lines[0]):
42
lines.pop(0)
43
44
def _IsTagLine(line):
45
return ":" in line
46
47
def _SplitIntoMultipleCommits(description_text):
48
paragraph_split_pattern = r"((?m)^\s*$\n)"
49
multiple_paragraphs = re.split(paragraph_split_pattern, description_text)
50
multiple_commits = [""]
51
change_id_pattern = re.compile(r"(?m)^Change-Id: [a-zA-Z0-9]*$")
52
for paragraph in multiple_paragraphs:
53
multiple_commits[-1] += paragraph
54
if change_id_pattern.search(paragraph):
55
multiple_commits.append("")
56
if multiple_commits[-1] == "":
57
multiple_commits.pop()
58
return multiple_commits
59
60
def _CheckTabInCommit(lines):
61
return all([line.find("\t") == -1 for line in lines])
62
63
allowlist_strings = ['Revert "', 'Roll ', 'Reland ', 'Re-land ']
64
summary_linelength_warning_lower_limit = 65
65
summary_linelength_warning_upper_limit = 70
66
description_linelength_limit = 72
67
68
git_output = input_api.change.DescriptionText()
69
70
multiple_commits = _SplitIntoMultipleCommits(git_output)
71
errors = []
72
73
for k in range(len(multiple_commits)):
74
commit_msg_lines = multiple_commits[k].splitlines()
75
commit_number = len(multiple_commits) - k
76
commit_tag = "Commit " + str(commit_number) + ":"
77
commit_msg_line_numbers = {}
78
for i in range(len(commit_msg_lines)):
79
commit_msg_line_numbers[commit_msg_lines[i]] = i + 1
80
_PopBlankLines(commit_msg_lines, True)
81
_PopBlankLines(commit_msg_lines, False)
82
allowlisted = False
83
if len(commit_msg_lines) > 0:
84
for allowlist_string in allowlist_strings:
85
if commit_msg_lines[0].startswith(allowlist_string):
86
allowlisted = True
87
break
88
if allowlisted:
89
continue
90
91
if not _CheckTabInCommit(commit_msg_lines):
92
errors.append(
93
output_api.PresubmitError(commit_tag + "Tabs are not allowed in commit message."))
94
95
# the tags paragraph is at the end of the message
96
# the break between the tags paragraph is the first line without ":"
97
# this is sufficient because if a line is blank, it will not have ":"
98
last_paragraph_line_count = 0
99
while len(commit_msg_lines) > 0 and _IsTagLine(commit_msg_lines[-1]):
100
last_paragraph_line_count += 1
101
commit_msg_lines.pop()
102
if last_paragraph_line_count == 0:
103
errors.append(
104
output_api.PresubmitError(
105
commit_tag +
106
"Please ensure that there are tags (e.g., Bug:, Test:) in your description."))
107
if len(commit_msg_lines) > 0:
108
if not _IsLineBlank(commit_msg_lines[-1]):
109
output_api.PresubmitError(commit_tag +
110
"Please ensure that there exists 1 blank line " +
111
"between tags and description body.")
112
else:
113
# pop the blank line between tag paragraph and description body
114
commit_msg_lines.pop()
115
if len(commit_msg_lines) > 0 and _IsLineBlank(commit_msg_lines[-1]):
116
errors.append(
117
output_api.PresubmitError(
118
commit_tag + 'Please ensure that there exists only 1 blank line '
119
'between tags and description body.'))
120
# pop all the remaining blank lines between tag and description body
121
_PopBlankLines(commit_msg_lines, True)
122
if len(commit_msg_lines) == 0:
123
errors.append(
124
output_api.PresubmitError(commit_tag +
125
'Please ensure that your description summary'
126
' and description body are not blank.'))
127
continue
128
129
if summary_linelength_warning_lower_limit <= len(commit_msg_lines[0]) \
130
<= summary_linelength_warning_upper_limit:
131
errors.append(
132
output_api.PresubmitPromptWarning(
133
commit_tag + "Your description summary should be on one line of " +
134
str(summary_linelength_warning_lower_limit - 1) + " or less characters."))
135
elif len(commit_msg_lines[0]) > summary_linelength_warning_upper_limit:
136
errors.append(
137
output_api.PresubmitError(
138
commit_tag + "Please ensure that your description summary is on one line of " +
139
str(summary_linelength_warning_lower_limit - 1) + " or less characters."))
140
commit_msg_lines.pop(0) # get rid of description summary
141
if len(commit_msg_lines) == 0:
142
continue
143
if not _IsLineBlank(commit_msg_lines[0]):
144
errors.append(
145
output_api.PresubmitError(commit_tag +
146
'Please ensure the summary is only 1 line and '
147
'there is 1 blank line between the summary '
148
'and description body.'))
149
else:
150
commit_msg_lines.pop(0) # pop first blank line
151
if len(commit_msg_lines) == 0:
152
continue
153
if _IsLineBlank(commit_msg_lines[0]):
154
errors.append(
155
output_api.PresubmitError(commit_tag +
156
'Please ensure that there exists only 1 blank line '
157
'between description summary and description body.'))
158
# pop all the remaining blank lines between
159
# description summary and description body
160
_PopBlankLines(commit_msg_lines)
161
162
# loop through description body
163
while len(commit_msg_lines) > 0:
164
line = commit_msg_lines.pop(0)
165
# lines starting with 4 spaces or lines without space(urls)
166
# are exempt from length check
167
if line.startswith(" ") or " " not in line:
168
continue
169
if len(line) > description_linelength_limit:
170
errors.append(
171
output_api.PresubmitError(
172
commit_tag + 'Line ' + str(commit_msg_line_numbers[line]) +
173
' is too long.\n' + '"' + line + '"\n' + 'Please wrap it to ' +
174
str(description_linelength_limit) + ' characters. ' +
175
"Lines without spaces or lines starting with 4 spaces are exempt."))
176
break
177
return errors
178
179
180
def _CheckChangeHasBugField(input_api, output_api):
181
"""Requires that the changelist have a Bug: field from a known project."""
182
bugs = input_api.change.BugsFromDescription()
183
if not bugs:
184
return [
185
output_api.PresubmitError('Please ensure that your description contains:\n'
186
'"Bug: angleproject:[bug number]"\n'
187
'directly above the Change-Id tag.')
188
]
189
190
# The bug must be in the form of "project:number". None is also accepted, which is used by
191
# rollers as well as in very minor changes.
192
if len(bugs) == 1 and bugs[0] == 'None':
193
return []
194
195
projects = [
196
'angleproject:', 'chromium:', 'dawn:', 'fuchsia:', 'skia:', 'swiftshader:', 'tint:', 'b/'
197
]
198
bug_regex = re.compile(r"([a-z]+[:/])(\d+)")
199
errors = []
200
extra_help = None
201
202
for bug in bugs:
203
if bug == 'None':
204
errors.append(
205
output_api.PresubmitError('Invalid bug tag "None" in presence of other bug tags.'))
206
continue
207
208
match = re.match(bug_regex, bug)
209
if match == None or bug != match.group(0) or match.group(1) not in projects:
210
errors.append(output_api.PresubmitError('Incorrect bug tag "' + bug + '".'))
211
if not extra_help:
212
extra_help = output_api.PresubmitError('Acceptable format is:\n\n'
213
' Bug: project:bugnumber\n\n'
214
'Acceptable projects are:\n\n ' +
215
'\n '.join(projects))
216
217
if extra_help:
218
errors.append(extra_help)
219
220
return errors
221
222
223
def _CheckCodeGeneration(input_api, output_api):
224
225
class Msg(output_api.PresubmitError):
226
"""Specialized error message"""
227
228
def __init__(self, message):
229
super(output_api.PresubmitError, self).__init__(
230
message,
231
long_text='Please ensure your ANGLE repositiory is synced to tip-of-tree\n'
232
'and all ANGLE DEPS are fully up-to-date by running gclient sync.\n'
233
'\n'
234
'If that fails, run scripts/run_code_generation.py to refresh generated hashes.\n'
235
'\n'
236
'If you are building ANGLE inside Chromium you must bootstrap ANGLE\n'
237
'before gclient sync. See the DevSetup documentation for more details.\n')
238
239
code_gen_path = input_api.os_path.join(input_api.PresubmitLocalPath(),
240
'scripts/run_code_generation.py')
241
cmd_name = 'run_code_generation'
242
cmd = [input_api.python_executable, code_gen_path, '--verify-no-dirty']
243
test_cmd = input_api.Command(name=cmd_name, cmd=cmd, kwargs={}, message=Msg)
244
if input_api.verbose:
245
print('Running ' + cmd_name)
246
return input_api.RunTests([test_cmd])
247
248
249
# Taken directly from Chromium's PRESUBMIT.py
250
def _CheckNewHeaderWithoutGnChange(input_api, output_api):
251
"""Checks that newly added header files have corresponding GN changes.
252
Note that this is only a heuristic. To be precise, run script:
253
build/check_gn_headers.py.
254
"""
255
256
def headers(f):
257
return input_api.FilterSourceFile(f, files_to_check=(r'.+%s' % _HEADER_EXTENSIONS,))
258
259
new_headers = []
260
for f in input_api.AffectedSourceFiles(headers):
261
if f.Action() != 'A':
262
continue
263
new_headers.append(f.LocalPath())
264
265
def gn_files(f):
266
return input_api.FilterSourceFile(f, files_to_check=(r'.+\.gn',))
267
268
all_gn_changed_contents = ''
269
for f in input_api.AffectedSourceFiles(gn_files):
270
for _, line in f.ChangedContents():
271
all_gn_changed_contents += line
272
273
problems = []
274
for header in new_headers:
275
basename = input_api.os_path.basename(header)
276
if basename not in all_gn_changed_contents:
277
problems.append(header)
278
279
if problems:
280
return [
281
output_api.PresubmitPromptWarning(
282
'Missing GN changes for new header files',
283
items=sorted(problems),
284
long_text='Please double check whether newly added header files need '
285
'corresponding changes in gn or gni files.\nThis checking is only a '
286
'heuristic. Run build/check_gn_headers.py to be precise.\n'
287
'Read https://crbug.com/661774 for more info.')
288
]
289
return []
290
291
292
def _CheckExportValidity(input_api, output_api):
293
outdir = tempfile.mkdtemp()
294
# shell=True is necessary on Windows, as otherwise subprocess fails to find
295
# either 'gn' or 'vpython3' even if they are findable via PATH.
296
use_shell = input_api.is_windows
297
try:
298
try:
299
subprocess.check_output(['gn', 'gen', outdir], shell=use_shell)
300
except subprocess.CalledProcessError as e:
301
return [
302
output_api.PresubmitError(
303
'Unable to run gn gen for export_targets.py: %s' % e.output)
304
]
305
export_target_script = os.path.join(input_api.PresubmitLocalPath(), 'scripts',
306
'export_targets.py')
307
try:
308
subprocess.check_output(
309
['vpython3', export_target_script, outdir] + _PRIMARY_EXPORT_TARGETS,
310
stderr=subprocess.STDOUT,
311
shell=use_shell)
312
except subprocess.CalledProcessError as e:
313
if input_api.is_committing:
314
return [output_api.PresubmitError('export_targets.py failed: %s' % e.output)]
315
return [
316
output_api.PresubmitPromptWarning(
317
'export_targets.py failed, this may just be due to your local checkout: %s' %
318
e.output)
319
]
320
return []
321
finally:
322
shutil.rmtree(outdir)
323
324
325
def _CheckTabsInSourceFiles(input_api, output_api):
326
"""Forbids tab characters in source files due to a WebKit repo requirement. """
327
328
def implementation_and_headers_including_third_party(f):
329
# Check third_party files too, because WebKit's checks don't make exceptions.
330
return input_api.FilterSourceFile(
331
f,
332
files_to_check=(r'.+%s' % _IMPLEMENTATION_AND_HEADER_EXTENSIONS,),
333
files_to_skip=[f for f in input_api.DEFAULT_FILES_TO_SKIP if not "third_party" in f])
334
335
files_with_tabs = []
336
for f in input_api.AffectedSourceFiles(implementation_and_headers_including_third_party):
337
for (num, line) in f.ChangedContents():
338
if '\t' in line:
339
files_with_tabs.append(f)
340
break
341
342
if files_with_tabs:
343
return [
344
output_api.PresubmitError(
345
'Tab characters in source files.',
346
items=sorted(files_with_tabs),
347
long_text=
348
'Tab characters are forbidden in ANGLE source files because WebKit\'s Subversion\n'
349
'repository does not allow tab characters in source files.\n'
350
'Please remove tab characters from these files.')
351
]
352
return []
353
354
355
# https://stackoverflow.com/a/196392
356
def is_ascii(s):
357
return all(ord(c) < 128 for c in s)
358
359
360
def _CheckNonAsciiInSourceFiles(input_api, output_api):
361
"""Forbids non-ascii characters in source files. """
362
363
def implementation_and_headers(f):
364
return input_api.FilterSourceFile(
365
f, files_to_check=(r'.+%s' % _IMPLEMENTATION_AND_HEADER_EXTENSIONS,))
366
367
files_with_non_ascii = []
368
for f in input_api.AffectedSourceFiles(implementation_and_headers):
369
for (num, line) in f.ChangedContents():
370
if not is_ascii(line):
371
files_with_non_ascii.append("%s: %s" % (f, line))
372
break
373
374
if files_with_non_ascii:
375
return [
376
output_api.PresubmitError(
377
'Non-ASCII characters in source files.',
378
items=sorted(files_with_non_ascii),
379
long_text='Non-ASCII characters are forbidden in ANGLE source files.\n'
380
'Please remove non-ASCII characters from these files.')
381
]
382
return []
383
384
385
def CheckChangeOnUpload(input_api, output_api):
386
results = []
387
results.extend(_CheckTabsInSourceFiles(input_api, output_api))
388
results.extend(_CheckNonAsciiInSourceFiles(input_api, output_api))
389
results.extend(_CheckCodeGeneration(input_api, output_api))
390
results.extend(_CheckChangeHasBugField(input_api, output_api))
391
results.extend(input_api.canned_checks.CheckChangeHasDescription(input_api, output_api))
392
results.extend(_CheckNewHeaderWithoutGnChange(input_api, output_api))
393
results.extend(_CheckExportValidity(input_api, output_api))
394
results.extend(
395
input_api.canned_checks.CheckPatchFormatted(
396
input_api, output_api, result_factory=output_api.PresubmitError))
397
results.extend(_CheckCommitMessageFormatting(input_api, output_api))
398
return results
399
400
401
def CheckChangeOnCommit(input_api, output_api):
402
return CheckChangeOnUpload(input_api, output_api)
403
404