Chromium Code Reviews| Index: PRESUBMIT.py |
| diff --git a/PRESUBMIT.py b/PRESUBMIT.py |
| index 3ab9bc268681a4f3fa824f338b6a0388fe8efbe2..c7fe24c9e83a385a2f4f42737183eb5108fcad1e 100644 |
| --- a/PRESUBMIT.py |
| +++ b/PRESUBMIT.py |
| @@ -428,10 +428,10 @@ def _CheckDCHECK_IS_ONHasBraces(input_api, output_api): |
| continue |
| for lnum, line in f.ChangedContents(): |
| if input_api.re.search(pattern, line): |
| - errors.append(output_api.PresubmitError( |
| - ('%s:%d: Use of DCHECK_IS_ON() must be written as "#if ' + |
| - 'DCHECK_IS_ON()", not forgetting the braces.') |
| - % (f.LocalPath(), lnum))) |
| + errors.append(output_api.PresubmitError( |
| + ('%s:%d: Use of DCHECK_IS_ON() must be written as "#if ' + |
| + 'DCHECK_IS_ON()", not forgetting the braces.') |
| + % (f.LocalPath(), lnum))) |
| return errors |
| @@ -590,11 +590,11 @@ def _CheckNoBannedFunctions(input_api, output_api): |
| if input_api.re.search(regex, line): |
| matched = True |
| elif func_name in line: |
| - matched = True |
| + matched = True |
| if matched: |
| - problems = warnings; |
| + problems = warnings |
| if error: |
| - problems = errors; |
| + problems = errors |
| problems.append(' %s:%d:' % (affected_file.LocalPath(), line_num)) |
| for message_line in message: |
| problems.append(' %s' % message_line) |
| @@ -1399,6 +1399,104 @@ def _CheckJavaStyle(input_api, output_api): |
| black_list=_EXCLUDED_PATHS + input_api.DEFAULT_BLACK_LIST) |
| +def _CheckIpcOwners(input_api, output_api): |
| + """Checks that affected files involving IPC have an IPC OWNERS rule. |
| + |
| + Whether or not a file affects IPC is determined by a simple whitelist of |
| + filename patterns.""" |
| + file_patterns = [ |
| + '*_messages.cc', |
| + '*_messages*.h', |
| + '*_param_traits*.*', |
| + '*.mojom', |
| + '*_struct_traits*.*', |
| + '*_type_converters*.*', |
| + ] |
| + |
| + # Dictionary mapping an OWNERS file path to Patterns. |
| + # Patterns is a dictionary mapping glob patterns (suitable for use in per-file |
| + # rules ) to a PatternEntry. |
| + # PatternEntry is a dictionary with two keys: |
| + # - 'files': the files that are matched by this pattern |
| + # - 'rules': the per-file rules needed for this pattern |
| + # For example, if we expect OWNERS file to contain rules for *.mojom and |
| + # *_struct_traits*.*, Patterns might look like this: |
| + # { |
| + # '*.mojom': { |
| + # 'files': ..., |
| + # 'rules': [ |
| + # 'per-file *.mojom=set noparent', |
| + # 'per-file *.mojom=file://ipc/SECURITY_OWNERS', |
| + # ], |
| + # }, |
| + # '*_struct_traits*.*': { |
| + # 'files': ..., |
| + # 'rules': [ |
| + # 'per-file *_struct_traits*.*=set noparent', |
| + # 'per-file *_struct_traits*.*=file://ipc/SECURITY_OWNERS', |
| + # ], |
| + # }, |
| + # } |
| + to_check = {} |
| + |
| + # Iterate through the affected files to see what we actually need to check |
| + # for. We should only nag patch authors about per-file rules if a file in that |
| + # directory would match that pattern. If a directory only contains *.mojom |
| + # files and no *_messages*.h files, we should only nag about rules for |
| + # *.mojom files. |
| + for f in input_api.change.AffectedFiles(): |
| + for pattern in file_patterns: |
| + if input_api.fnmatch.fnmatch( |
| + input_api.os_path.basename(f.LocalPath()), pattern): |
| + owners_file = input_api.os_path.join( |
| + input_api.os_path.dirname(f.LocalPath()), 'OWNERS') |
| + if owners_file not in to_check: |
| + to_check[owners_file] = {} |
| + if pattern not in to_check[owners_file]: |
| + to_check[owners_file][pattern] = { |
| + 'files': [], |
| + 'rules': [ |
| + 'per-file %s=set noparent' % pattern, |
| + 'per-file %s=file://ipc/SECURITY_OWNERS' % pattern, |
| + ] |
| + } |
| + to_check[owners_file][pattern]['files'].append(f) |
| + break |
| + |
| + # Now go through the OWNERS files we collected, filtering out rules that are |
| + # already present in that OWNERS file. |
| + for owners_file, patterns in to_check.iteritems(): |
| + try: |
| + with file(owners_file) as f: |
| + for line in f.read().splitlines(): |
| + for entry in patterns.itervalues(): |
| + entry['rules'] = [rule for rule in entry['rules'] if rule != line] |
|
Dirk Pranke
2016/06/18 20:49:26
Maybe
lines = f.read().splitlines()
for entry
dcheng
2016/06/19 00:03:28
I tweaked this slightly and put the lines in a set
|
| + except IOError: |
| + # No OWNERS file, so all the rules are definitely missing. |
| + continue |
| + |
| + # All the remaining lines weren't found in OWNERS files, so emit an error. |
| + errors = [] |
| + for owners_file, patterns in to_check.iteritems(): |
| + missing_lines = [] |
| + files = [] |
| + for pattern, entry in patterns.iteritems(): |
| + missing_lines.extend(entry['rules']) |
| + files.extend([' %s' % f.LocalPath() for f in entry['files']]) |
| + if missing_lines: |
| + errors.append( |
| + '%s is missing the following lines:\n\n%s\n\nfor changed files:\n%s' % |
| + (owners_file, '\n'.join(missing_lines), '\n'.join(files))) |
| + |
| + results = [] |
| + if errors: |
| + results.append(output_api.PresubmitError( |
| + 'Found changes to IPC files without a security OWNER!', |
| + long_text='\n\n'.join(errors))) |
| + |
| + return results |
| + |
| + |
| def _CheckAndroidToastUsage(input_api, output_api): |
| """Checks that code uses org.chromium.ui.widget.Toast instead of |
| android.widget.Toast (Chromium Toast doesn't force hardware |
| @@ -1862,6 +1960,7 @@ def _CommonChecks(input_api, output_api): |
| results.extend(_CheckNoDeprecatedCompiledResourcesGYP(input_api, output_api)) |
| results.extend(_CheckPydepsNeedsUpdating(input_api, output_api)) |
| results.extend(_CheckJavaStyle(input_api, output_api)) |
| + results.extend(_CheckIpcOwners(input_api, output_api)) |
| if any('PRESUBMIT.py' == f.LocalPath() for f in input_api.AffectedFiles()): |
| results.extend(input_api.canned_checks.RunUnitTestsInDirectory( |
| @@ -1875,9 +1974,6 @@ def _CheckAuthorizedAuthor(input_api, output_api): |
| """For non-googler/chromites committers, verify the author's email address is |
| in AUTHORS. |
| """ |
| - # TODO(maruel): Add it to input_api? |
| - import fnmatch |
| - |
| author = input_api.change.author_email |
| if not author: |
| input_api.logging.info('No author, skipping AUTHOR check') |
| @@ -1888,7 +1984,8 @@ def _CheckAuthorizedAuthor(input_api, output_api): |
| input_api.re.match(r'[^#]+\s+\<(.+?)\>\s*$', line) |
| for line in open(authors_path)) |
| valid_authors = [item.group(1).lower() for item in valid_authors if item] |
| - if not any(fnmatch.fnmatch(author.lower(), valid) for valid in valid_authors): |
| + if not any(input_api.fnmatch.fnmatch(author.lower(), valid) |
| + for valid in valid_authors): |
| input_api.logging.info('Valid authors are %s', ', '.join(valid_authors)) |
| return [output_api.PresubmitPromptWarning( |
| ('%s is not in AUTHORS file. If you are a new contributor, please visit' |