Movatterモバイル変換


[0]ホーム

URL:


Google Git
Sign in
chromium /chromium /src /refs/heads/main /. /ios /PRESUBMIT.py
blob: 8984e045942017ed543d78b70c30074fc593c613 [file] [log] [blame] [edit]
# Copyright 2017 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Presubmit script for ios.
See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
for more details about the presubmit API built into depot_tools.
"""
import os
import xml.etree.ElementTreeasElementTree
NULLABILITY_PATTERN= r'(nonnull|nullable|_Nullable|_Nonnull)'
TODO_PATTERN= r'TO[D]O\(([^\)]*)\)'
BUG_PATTERN= r'^(crbug\.com|b)/\d+$'
DEPRECATED_BUG_PATTERN= r'^b/\d+$'
INCLUDE_PATTERN= r'^#include'
PIPE_IN_COMMENT_PATTERN= r'//.*[^|]\|(?!\|)'
IOS_PACKAGE_PATTERN= r'^ios'
BOXED_BOOL_PATTERN= r'@\((YES|NO)\)'
USER_DEFAULTS_PATTERN= r'\[NSUserDefaults standardUserDefaults]'
# Color management constants
COLOR_SHARED_DIR='ios/chrome/common/ui/colors/'
COLOR_FILE_PATTERN='.colorset/Contents.json'
defFormatMessageWithFiles(message, errors):
"""Helper to format warning/error messages with affected files."""
ifnot errors:
return message
return'\n'.join([message+'\n\nAffected file(s):']+ errors)+'\n'
defIsSubListOf(needle, hay):
"""Returns whether there is a slice of |hay| equal to |needle|."""
for i, linein enumerate(hay):
if line== needle[0]:
if needle== hay[i:i+ len(needle)]:
returnTrue
returnFalse
def_CheckNullabilityAnnotations(input_api, output_api):
""" Checks whether there are nullability annotations in ios code.
They are accepted in ios/web_view/public since it tries to mimic
the platform library but not anywhere else.
"""
nullability_regex= input_api.re.compile(NULLABILITY_PATTERN)
errors=[]
for fin input_api.AffectedFiles():
if f.LocalPath().startswith('ios/web_view/public/'):
# ios/web_view/public tries to mimic an existing API that
# might have nullability in it and that is acceptable.
continue
for line_num, linein f.ChangedContents():
if nullability_regex.search(line):
errors.append('%s:%s'%(f.LocalPath(), line_num))
ifnot errors:
return[]
plural_suffix=''if len(errors)==1else's'
warning_message=('Found Nullability annotation%(plural)s. '
'Prefer DCHECKs in ios code to check for nullness:'%{
'plural': plural_suffix
})
return[output_api.PresubmitPromptWarning(warning_message, items=errors)]
def_CheckBugInToDo(input_api, output_api):
""" Checks whether TODOs in ios code are identified by a bug number."""
errors=[]
warnings=[]
for fin input_api.AffectedFiles():
for line_num, linein f.ChangedContents():
if_HasToDoWithNoBug(input_api, line):
errors.append('%s:%s'%(f.LocalPath(), line_num))
if_HasToDoWithDeprecatedBug(input_api, line):
warnings.append('%s:%s'%(f.LocalPath(), line_num))
ifnot errorsandnot warnings:
return[]
output=[]
if errors:
singular_article='a 'if len(errors)==1else''
plural_suffix=''if len(errors)==1else's'
error_message='\n'.join([
'Found TO'
'DO%(plural)s without %(a)sbug number%(plural)s (expected format '
'is \"TO'
'DO(crbug.com/######)\"):'%{
'plural': plural_suffix,
'a': singular_article
}
]+ errors)+'\n'
output.append(output_api.PresubmitError(error_message))
if warnings:
singular_article='a 'if len(warnings)==1else''
plural_suffix=''if len(warnings)==1else's'
warning_message='\n'.join([
'Found TO'
'DO%(plural)s with %(a)sdeprecated bug link%(plural)s (found '
'"b/#####\", expected format is \"crbug.com/######"):'%{
'plural': plural_suffix,
'a': singular_article
}
]+ warnings)+'\n'
output.append(output_api.PresubmitPromptWarning(warning_message))
return output
def_CheckHasNoIncludeDirectives(input_api, output_api):
""" Checks that #include preprocessor directives are not present."""
errors=[]
for fin input_api.AffectedFiles():
ifnot_IsInIosPackage(input_api, f.LocalPath()):
continue
_, ext= os.path.splitext(f.LocalPath())
if ext!='.mm':
continue
for line_num, linein f.ChangedContents():
if_HasIncludeDirective(input_api, line):
errors.append('%s:%s'%(f.LocalPath(), line_num))
ifnot errors:
return[]
singular_plural='it'if len(errors)==1else'them'
plural_suffix=''if len(errors)==1else's'
error_message='\n'.join([
'Found usage of `#include` preprocessor directive%(plural)s! Please, '
'replace %(singular_plural)s with `#import` preprocessor '
'directive%(plural)s instead. '
'Consider replacing all existing `#include` with `#import` (if any) in '
'this file for the code clean up. See '
'https://chromium.googlesource.com/chromium/src.git/+/refs/heads/main'
'/styleguide/objective-c/objective-c.md'
'#import-and-include-in-the-directory for more details. '
'\n\nAffected file%(plural)s:'%{
'plural': plural_suffix,
'singular_plural': singular_plural
}
]+ errors)+'\n'
return[output_api.PresubmitError(error_message)]
def_CheckHasNoPipeInComment(input_api, output_api):
""" Checks that comments don't contain pipes."""
pipe_regex= input_api.re.compile(PIPE_IN_COMMENT_PATTERN)
errors=[]
for fin input_api.AffectedFiles():
ifnot_IsInIosPackage(input_api, f.LocalPath()):
continue
for line_num, linein f.ChangedContents():
if pipe_regex.search(line):
errors.append('%s:%s'%(f.LocalPath(), line_num))
ifnot errors:
return[]
warning_message='\n'.join([
'Please use backticks "`" instead of pipes "|" if you need to quote'
' variable names and symbols in comments.\n'
'Found potential uses of pipes in:'
]+ errors)+'\n'
return[output_api.PresubmitPromptWarning(warning_message)]
def_CheckCanImproveTestUsingExpectNSEQ(input_api, output_api):
""" Checks that test files use EXPECT_NSEQ when possible."""
errors=[]
# Substrings that should not be used together with EXPECT_TRUE or
# EXPECT_FALSE in tests.
wrong_patterns=["isEqualToString:","isEqualToData:","isEqualToArray:"]
for fin input_api.AffectedFiles():
ifnot'_unittest.'in f.LocalPath():
continue
for line_num, linein f.ChangedContents():
if line.startswith(("EXPECT_TRUE","EXPECT_FALSE")):
# Condition is in one line.
if any(xin linefor xin wrong_patterns):
errors.append('%s:%s'%(f.LocalPath(), line_num))
# Condition is split on multiple lines.
elifnot line.endswith(";"):
# Check this is not the last line.
if line_num< len(f.NewContents()):
next_line= f.NewContents()[line_num]
if any(xin next_linefor xin wrong_patterns):
errors.append('%s:%s'%(f.LocalPath(), line_num))
ifnot errors:
return[]
plural_suffix=''if len(errors)==1else's'
warning_message='\n'.join([
'Found possible improvement in unittest. Prefer using'
' EXPECT_NSEQ() or EXPECT_NSNE() when possible.'
'\n\nAffected file%(plural)s:'%{
'plural': plural_suffix,
}
]+ errors)+'\n'
return[output_api.PresubmitPromptWarning(warning_message)]
def_IsInIosPackage(input_api, path):
""" Returns True if path is within ios package"""
ios_package_regex= input_api.re.compile(IOS_PACKAGE_PATTERN)
return ios_package_regex.search(path)
def_HasIncludeDirective(input_api, line):
""" Returns True if #include is found in the line"""
include_regex= input_api.re.compile(INCLUDE_PATTERN)
return include_regex.search(line)
def_HasToDoWithNoBug(input_api, line):
""" Returns True if TODO is not identified by a bug number."""
todo_regex= input_api.re.compile(TODO_PATTERN)
bug_regex= input_api.re.compile(BUG_PATTERN)
todo_match= todo_regex.search(line)
ifnot todo_match:
returnFalse
returnnot bug_regex.match(todo_match.group(1))
def_HasToDoWithDeprecatedBug(input_api, line):
""" Returns True if TODO is identified by a deprecated bug number format."""
todo_regex= input_api.re.compile(TODO_PATTERN)
deprecated_bug_regex= input_api.re.compile(DEPRECATED_BUG_PATTERN)
todo_match= todo_regex.search(line)
ifnot todo_match:
returnFalse
return deprecated_bug_regex.match(todo_match.group(1))
def_CheckHasNoBoxedBOOL(input_api, output_api):
""" Checks that there are no @(YES) or @(NO)."""
boxed_BOOL_regex= input_api.re.compile(BOXED_BOOL_PATTERN)
errors=[]
for fin input_api.AffectedFiles():
for line_num, linein f.ChangedContents():
if boxed_BOOL_regex.search(line):
errors.append('%s:%s'%(f.LocalPath(), line_num))
ifnot errors:
return[]
plural_suffix=''if len(errors)==1else's'
warning_message=('Found boxed BOOL%(plural)s. '
'Prefer @YES or @NO in ios code:'%{
'plural': plural_suffix
})
return[output_api.PresubmitPromptWarning(warning_message, items=errors)]
def_CheckNoTearDownEGTest(input_api, output_api):
""" Checks that `- (void)tearDown {` is not present in an egtest.mm"""
errors=[]
for fin input_api.AffectedFiles():
ifnot'_egtest.'in f.LocalPath():
continue
for line_num, linein f.ChangedContents():
if line.startswith("- (void)tearDown {"):
errors.append('%s:%s'%(f.LocalPath(), line_num))
ifnot errors:
return[]
warning_message='\n'.join([
'To support hermetic EarlGrey test cases, tearDown has been renamed '
'to tearDownHelper, and will soon be removed. If tearDown is really '
'necessary for this test, please use addTeardownBlock'
]+ errors)+'\n'
return[output_api.PresubmitError(warning_message)]
def_IsAlphabeticallySortedXML(file):
"""Check that the `file` is alphabetically sorted"""
parser=ElementTree.XMLParser(target=ElementTree.TreeBuilder(
insert_comments=True))
with open(file,'r', encoding='utf8')as xml_file:
tree=ElementTree.parse(xml_file, parser)
root= tree.getroot()
original_tree_string=ElementTree.tostring(root, encoding='utf8')
messages_element= tree.findall('.//messages')[0]
messages= messages_element.findall('message')
messages.sort(key=lambda message: message.attrib["name"])
for messagein messages:
messages_element.remove(message)
for messagein messages:
messages_element.append(message)
ordered_tree_string=ElementTree.tostring(root, encoding='utf8')
return ordered_tree_string== original_tree_string
def_CheckOrderedStringFile(input_api, output_api):
""" Checks that the string files are alphabetically ordered"""
errors=[]
for fin input_api.AffectedFiles(include_deletes=False):
ifnot f.LocalPath().endswith("_strings.grd"):
continue
ifnot_IsAlphabeticallySortedXML(f.AbsoluteLocalPath()):
errors.append(' python3 ios/tools/order_string_file.py '+
f.LocalPath())
ifnot errors:
return[]
warning_message='\n'.join(
['Files not alphabetically sorted, try running:']+ errors)+'\n'
return[output_api.PresubmitPromptWarning(warning_message)]
def_CheckNotUsingNSUserDefaults(input_api, output_api):
""" Checks the added code to limit new usage of NSUserDefaults """
user_defaults_regex= input_api.re.compile(USER_DEFAULTS_PATTERN)
errors=[]
for fin input_api.AffectedFiles():
if(not f.LocalPath().endswith('.mm')):
continue
for line_num, linein f.ChangedContents():
if user_defaults_regex.search(line):
errors.append('%s:%s'%(f.LocalPath(), line_num))
ifnot errors:
return[]
warning_message='\n'.join([
'A new use of NSUserDefaults was added. If this is a newly added key '
'consider storing it to PrefService instead.'
]+ errors)+'\n'
return[output_api.PresubmitPromptWarning(warning_message)]
def_CheckNewColorIntroduction(input_api, output_api):
"""Checks for new or modified colorset files.
Ensures colors are properly added to the shared directory.
"""
results=[]
affected_files=[
ffor fin input_api.AffectedFiles()
if f.LocalPath().endswith(COLOR_FILE_PATTERN)
]
warnings={
'shared_added':[],
'shared_modified':[],
'other_modified':[]
}
errors=[]
for affected_filein affected_files:
action= affected_file.Action()
local_path= affected_file.LocalPath()
file_path_error='%s'%(affected_file.LocalPath())
if COLOR_SHARED_DIRin local_path:
if action=='A':
warnings['shared_added'].append(file_path_error)
elif action=='M':
warnings['shared_modified'].append(file_path_error)
else:
if action=='A':
errors.append(file_path_error)
elif action=='M':
warnings['other_modified'].append(file_path_error)
output=[]
if errors:
error_message=('New color(s) must be added to the %s directory.'%
COLOR_SHARED_DIR)
output.append(
output_api.PresubmitError(
FormatMessageWithFiles(error_message, errors)))
warning_message=('Please ensure the color does not already exist in the '
'shared %s directory.'% COLOR_SHARED_DIR)
if warnings['shared_added']:
shared_added_message=('New color(s) added in %s. %s'%
(COLOR_SHARED_DIR, warning_message))
output.append(
output_api.PresubmitPromptWarning(
FormatMessageWithFiles(shared_added_message,
warnings['shared_added'])))
if warnings['shared_modified']:
shared_modified_message=('Color(s) modified in %s. %s'%
(COLOR_SHARED_DIR, warning_message))
output.append(
output_api.PresubmitPromptWarning(
FormatMessageWithFiles(shared_modified_message,
warnings['shared_modified'])))
if warnings['other_modified']:
modified_message=('Color(s) modified. %s'% warning_message)
output.append(
output_api.PresubmitPromptWarning(
FormatMessageWithFiles(modified_message,
warnings['other_modified'])))
return output
def_CheckStyleESLint(input_api, output_api):
results=[]
try:
import sys
old_sys_path= sys.path[:]
cwd= input_api.PresubmitLocalPath()
sys.path+=[input_api.os_path.join(cwd,'..','tools')]
from web_dev_styleimport presubmit_support
results+= presubmit_support.CheckStyleESLint(input_api, output_api)
finally:
sys.path= old_sys_path
return results
def_CheckUIGraphicsBeginImageContextWithOptions(input_api, output_api):
""" Checks that UIGraphicsBeginImageContextWithOptions is not used"""
deprecated_regex= input_api.re.compile(
r'UIGraphicsBeginImageContextWithOptions\(')
errors=[]
for fin input_api.AffectedFiles():
if(not f.LocalPath().endswith('.mm')):
continue
for line_num, linein f.ChangedContents():
if deprecated_regex.search(line):
errors.append('%s:%s'%(f.LocalPath(), line_num))
ifnot errors:
return[]
error_message='\n'.join([
'UIGraphicsBeginImageContextWithOptions is deprecated, use '
'UIGraphicsImageRenderer instead.'
]+ errors)+'\n'
return[output_api.PresubmitError(error_message)]
defCheckChange(input_api, output_api):
results=[]
results.extend(_CheckBugInToDo(input_api, output_api))
results.extend(_CheckNullabilityAnnotations(input_api, output_api))
results.extend(_CheckHasNoIncludeDirectives(input_api, output_api))
results.extend(_CheckHasNoPipeInComment(input_api, output_api))
results.extend(_CheckHasNoBoxedBOOL(input_api, output_api))
results.extend(_CheckNoTearDownEGTest(input_api, output_api))
results.extend(_CheckCanImproveTestUsingExpectNSEQ(input_api, output_api))
results.extend(_CheckOrderedStringFile(input_api, output_api))
results.extend(_CheckNotUsingNSUserDefaults(input_api, output_api))
results.extend(_CheckNewColorIntroduction(input_api, output_api))
results.extend(_CheckStyleESLint(input_api, output_api))
results.extend(
_CheckUIGraphicsBeginImageContextWithOptions(input_api, output_api))
return results
defCheckChangeOnUpload(input_api, output_api):
returnCheckChange(input_api, output_api)
defCheckChangeOnCommit(input_api, output_api):
returnCheckChange(input_api, output_api)

[8]ページ先頭

©2009-2025 Movatter.jp