| Index: PRESUBMIT.py
|
| diff --git a/PRESUBMIT.py b/PRESUBMIT.py
|
| index 111abb03b0dfdaa9500b66a287ec997a6da0d16c..0f936979877702f33bce811d091149f03586f88f 100644
|
| --- a/PRESUBMIT.py
|
| +++ b/PRESUBMIT.py
|
| @@ -34,6 +34,12 @@ LINT_FILTERS = [
|
| ]
|
|
|
|
|
| +_INCLUDE_ORDER_WARNING = (
|
| + 'Your #include order seems to be broken. Remember to use the right '
|
| + 'collation (LC_COLLATE=C) and check\nhttps://google.github.io/styleguide/'
|
| + 'cppguide.html#Names_and_Order_of_Includes')
|
| +
|
| +
|
| def _CheckUnwantedDependencies(input_api, output_api):
|
| """Runs checkdeps on #include statements added in this
|
| change. Breaking - rules is an error, breaking ! rules is a
|
| @@ -91,11 +97,171 @@ def _CheckUnwantedDependencies(input_api, output_api):
|
| return results
|
|
|
|
|
| +def _CheckIncludeOrderForScope(scope, input_api, file_path, changed_linenums):
|
| + """Checks that the lines in scope occur in the right order.
|
| +
|
| + 1. C system files in alphabetical order
|
| + 2. C++ system files in alphabetical order
|
| + 3. Project's .h files
|
| + """
|
| +
|
| + c_system_include_pattern = input_api.re.compile(r'\s*#include <.*\.h>')
|
| + cpp_system_include_pattern = input_api.re.compile(r'\s*#include <.*>')
|
| + custom_include_pattern = input_api.re.compile(r'\s*#include ".*')
|
| +
|
| + C_SYSTEM_INCLUDES, CPP_SYSTEM_INCLUDES, CUSTOM_INCLUDES = range(3)
|
| +
|
| + state = C_SYSTEM_INCLUDES
|
| +
|
| + previous_line = ''
|
| + previous_line_num = 0
|
| + problem_linenums = []
|
| + out_of_order = " - line belongs before previous line"
|
| + for line_num, line in scope:
|
| + if c_system_include_pattern.match(line):
|
| + if state != C_SYSTEM_INCLUDES:
|
| + problem_linenums.append((line_num, previous_line_num,
|
| + " - C system include file in wrong block"))
|
| + elif previous_line and previous_line > line:
|
| + problem_linenums.append((line_num, previous_line_num,
|
| + out_of_order))
|
| + elif cpp_system_include_pattern.match(line):
|
| + if state == C_SYSTEM_INCLUDES:
|
| + state = CPP_SYSTEM_INCLUDES
|
| + elif state == CUSTOM_INCLUDES:
|
| + problem_linenums.append((line_num, previous_line_num,
|
| + " - c++ system include file in wrong block"))
|
| + elif previous_line and previous_line > line:
|
| + problem_linenums.append((line_num, previous_line_num, out_of_order))
|
| + elif custom_include_pattern.match(line):
|
| + if state != CUSTOM_INCLUDES:
|
| + state = CUSTOM_INCLUDES
|
| + elif previous_line and previous_line > line:
|
| + problem_linenums.append((line_num, previous_line_num, out_of_order))
|
| + else:
|
| + problem_linenums.append((line_num, previous_line_num,
|
| + "Unknown include type"))
|
| + previous_line = line
|
| + previous_line_num = line_num
|
| +
|
| + warnings = []
|
| + for (line_num, previous_line_num, failure_type) in problem_linenums:
|
| + if line_num in changed_linenums or previous_line_num in changed_linenums:
|
| + warnings.append(' %s:%d:%s' % (file_path, line_num, failure_type))
|
| + return warnings
|
| +
|
| +
|
| +def _CheckIncludeOrderInFile(input_api, f, changed_linenums):
|
| + """Checks the #include order for the given file f."""
|
| +
|
| + system_include_pattern = input_api.re.compile(r'\s*#include \<.*')
|
| + # Exclude the following includes from the check:
|
| + # 1) #include <.../...>, e.g., <sys/...> includes often need to appear in a
|
| + # specific order.
|
| + # 2) <atlbase.h>, "build/build_config.h"
|
| + excluded_include_pattern = input_api.re.compile(
|
| + r'\s*#include (\<.*/.*|\<atlbase\.h\>|"build/build_config.h")')
|
| + custom_include_pattern = input_api.re.compile(r'\s*#include "(?P<FILE>.*)"')
|
| + # Match the final or penultimate token if it is xxxtest so we can ignore it
|
| + # when considering the special first include.
|
| + test_file_tag_pattern = input_api.re.compile(
|
| + r'_[a-z]+test(?=(_[a-zA-Z0-9]+)?\.)')
|
| + if_pattern = input_api.re.compile(
|
| + r'\s*#\s*(if|elif|else|endif|define|undef).*')
|
| + # Some files need specialized order of includes; exclude such files from this
|
| + # check.
|
| + uncheckable_includes_pattern = input_api.re.compile(
|
| + r'\s*#include '
|
| + '("ipc/.*macros\.h"|<windows\.h>|".*gl.*autogen.h")\s*')
|
| +
|
| + contents = f.NewContents()
|
| + warnings = []
|
| + line_num = 0
|
| +
|
| + # Handle the special first include. If the first include file is
|
| + # some/path/file.h, the corresponding including file can be some/path/file.cc,
|
| + # some/other/path/file.cc, some/path/file_platform.cc, some/path/file-suffix.h
|
| + # etc. It's also possible that no special first include exists.
|
| + # If the included file is some/path/file_platform.h the including file could
|
| + # also be some/path/file_xxxtest_platform.h.
|
| + including_file_base_name = test_file_tag_pattern.sub(
|
| + '', input_api.os_path.basename(f.LocalPath()))
|
| +
|
| + for line in contents:
|
| + line_num += 1
|
| + if system_include_pattern.match(line):
|
| + # No special first include -> process the line again along with normal
|
| + # includes.
|
| + line_num -= 1
|
| + break
|
| + match = custom_include_pattern.match(line)
|
| + if match:
|
| + match_dict = match.groupdict()
|
| + header_basename = test_file_tag_pattern.sub(
|
| + '', input_api.os_path.basename(match_dict['FILE'])).replace('.h', '')
|
| +
|
| + if header_basename not in including_file_base_name:
|
| + # No special first include -> process the line again along with normal
|
| + # includes.
|
| + line_num -= 1
|
| + break
|
| +
|
| + # Split into scopes: Each region between #if and #endif is its own scope.
|
| + scopes = []
|
| + current_scope = []
|
| + for line in contents[line_num:]:
|
| + line_num += 1
|
| + if uncheckable_includes_pattern.match(line):
|
| + continue
|
| + if if_pattern.match(line):
|
| + scopes.append(current_scope)
|
| + current_scope = []
|
| + elif ((system_include_pattern.match(line) or
|
| + custom_include_pattern.match(line)) and
|
| + not excluded_include_pattern.match(line)):
|
| + current_scope.append((line_num, line))
|
| + scopes.append(current_scope)
|
| +
|
| + for scope in scopes:
|
| + warnings.extend(_CheckIncludeOrderForScope(scope, input_api, f.LocalPath(),
|
| + changed_linenums))
|
| + return warnings
|
| +
|
| +
|
| +def _CheckIncludeOrder(input_api, output_api):
|
| + """Checks that the #include order is correct.
|
| +
|
| + 1. The corresponding header for source files.
|
| + 2. C system files in alphabetical order
|
| + 3. C++ system files in alphabetical order
|
| + 4. Project's .h files in alphabetical order
|
| +
|
| + Each region separated by #if, #elif, #else, #endif, #define and #undef follows
|
| + these rules separately.
|
| + """
|
| + def FileFilterIncludeOrder(affected_file):
|
| + black_list = (input_api.DEFAULT_BLACK_LIST)
|
| + return input_api.FilterSourceFile(affected_file, black_list=black_list)
|
| +
|
| + warnings = []
|
| + for f in input_api.AffectedFiles(file_filter=FileFilterIncludeOrder):
|
| + if f.LocalPath().endswith(('.cc', '.h', '.mm')):
|
| + changed_linenums = set(line_num for line_num, _ in f.ChangedContents())
|
| + warnings.extend(_CheckIncludeOrderInFile(input_api, f, changed_linenums))
|
| +
|
| + results = []
|
| + if warnings:
|
| + results.append(output_api.PresubmitPromptOrNotify(_INCLUDE_ORDER_WARNING,
|
| + warnings))
|
| + return results
|
| +
|
| +
|
| def CheckChangeOnUpload(input_api, output_api):
|
| results = []
|
| results += _CheckUnwantedDependencies(input_api, output_api)
|
| results += input_api.canned_checks.CheckPatchFormatted(input_api, output_api)
|
| results += input_api.canned_checks.CheckChangeLintsClean(
|
| input_api, output_api, None, LINT_FILTERS)
|
| + results += _CheckIncludeOrder(input_api, output_api)
|
|
|
| return results
|
|
|