 Chromium Code Reviews
 Chromium Code Reviews Issue 2872493002:
  Adding PRESUBMIT check on orphan headers files.  (Closed)
    
  
    Issue 2872493002:
  Adding PRESUBMIT check on orphan headers files.  (Closed) 
  | Index: presubmit_checks_lib/check_orphan_headers.py | 
| diff --git a/presubmit_checks_lib/check_orphan_headers.py b/presubmit_checks_lib/check_orphan_headers.py | 
| new file mode 100755 | 
| index 0000000000000000000000000000000000000000..9b45075175a75b845381ad78ae4e85d5d094f55b | 
| --- /dev/null | 
| +++ b/presubmit_checks_lib/check_orphan_headers.py | 
| @@ -0,0 +1,113 @@ | 
| +# Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. | 
| +# | 
| +# Use of this source code is governed by a BSD-style license | 
| +# that can be found in the LICENSE file in the root of the source | 
| +# tree. An additional intellectual property rights grant can be found | 
| +# in the file PATENTS. All contributing project authors may | 
| +# be found in the AUTHORS file in the root of the source tree. | 
| + | 
| +import os | 
| +import re | 
| + | 
| + | 
| +# TARGET_RE matches a GN target, and extracts the target name and the contents. | 
| +TARGET_RE = re.compile(r'(?P<indent>\s*)\w+\("(?P<target_name>\w+)"\) {' | 
| + r'(?P<target_contents>.*?)' | 
| + r'(?P=indent)}', | 
| + re.MULTILINE | re.DOTALL) | 
| + | 
| +# SOURCES_RE matches a block of sources inside a GN target. | 
| +SOURCES_RE = re.compile(r'sources \+?= \[(?P<sources>.*?)\]', | 
| + re.MULTILINE | re.DOTALL) | 
| + | 
| + | 
| +class NoBuildGnFoundError(Exception): | 
| + pass | 
| + | 
| + | 
| +class WrongFileTypeError(Exception): | 
| + pass | 
| + | 
| + | 
| +def _ReadFile(file_path): | 
| + """Returns the content of file_path in a string. | 
| + | 
| + Args: | 
| + file_path: the path of the file to read. | 
| + Returns: | 
| + A string with the content of the file. | 
| + """ | 
| + with open(file_path) as f: | 
| + return f.read() | 
| + | 
| + | 
| +def GetBuildGnPathFromFilePath(file_path, file_exists_check, root_dir_path): | 
| + """Returns the BUILD.gn file responsible for file_path. | 
| + | 
| + Args: | 
| + file_path: the absolute path to the .h file to check. | 
| + file_exists_check: a function that defines how to check if a file exists | 
| + on the file system. | 
| + root_dir_path: the absolute path of the root of project. | 
| + | 
| + Returns: | 
| + A string with the absolute path to the BUILD.gn file responsible to include | 
| + file_path in a target. | 
| + """ | 
| + if not file_path.endswith('.h'): | 
| + raise WrongFileTypeError( | 
| + 'File {} is not an header file (.h)'.format(file_path)) | 
| + candidate_dir = os.path.dirname(file_path) | 
| + while candidate_dir.startswith(root_dir_path): | 
| + candidate_build_gn_path = os.path.join(candidate_dir, 'BUILD.gn') | 
| + if file_exists_check(candidate_build_gn_path): | 
| + return candidate_build_gn_path | 
| + else: | 
| + candidate_dir = os.path.abspath(os.path.join(candidate_dir, | 
| + os.pardir)) | 
| 
kjellander_webrtc
2017/05/08 18:52:00
-5 spaces indent
 
mbonadei
2017/05/09 07:31:19
Done.
 | 
| + raise NoBuildGnFoundError( | 
| + 'No BUILD.gn file found for file: `{}`'.format(file_path)) | 
| + | 
| + | 
| +def IsHeaderInBuildGn(header_path, build_gn_path): | 
| + """Returns True if the header is listed in the BUILD.gn file | 
| 
kjellander_webrtc
2017/05/08 18:52:00
nit: end with punctation.
 
mbonadei
2017/05/09 07:31:19
Done.
 | 
| + | 
| + Args: | 
| + header_path: the absolute path to the header to check. | 
| + build_gn_path: the absolute path to the header to check. | 
| + | 
| + Returns: | 
| + bool: True if the header_path is an header that is listed in | 
| + at least one GN target in the BUILD.gn file specified by | 
| + the argument build_gn_path. | 
| + """ | 
| + target_abs_path = os.path.dirname(build_gn_path) | 
| + build_gn_content = _ReadFile(build_gn_path) | 
| + headers_in_build_gn = GetHeadersInBuildGnFileSources(build_gn_content, | 
| + target_abs_path) | 
| + return header_path in headers_in_build_gn | 
| + | 
| + | 
| +def GetHeadersInBuildGnFileSources(file_content, target_abs_path): | 
| + """Returns a set with all the .h files in the file_content. | 
| + | 
| + Args: | 
| + file_content: a string with the content of the BUILD.gn file. | 
| + target_abs_path: the absolute path to the directory where the | 
| + BUILD.gn file lives. | 
| + | 
| + Returns: | 
| + A set with all the headers (.h file) in the file_content. | 
| + The set contains absolute paths. | 
| + """ | 
| + headers_in_sources = set([]) | 
| + source_file_re = re.compile(r'.*\"(?P<source_file>.*)\"') | 
| 
kjellander_webrtc
2017/05/08 18:51:59
Let's make this regex a constant like the other on
 
mbonadei
2017/05/09 07:31:19
Done.
 | 
| + for target_match in TARGET_RE.finditer(file_content): | 
| + target_contents = target_match.group('target_contents') | 
| + for sources_match in SOURCES_RE.finditer(target_contents): | 
| + sources = sources_match.group('sources') | 
| + for source_file_match in source_file_re.finditer(sources): | 
| + source_file = source_file_match.group('source_file') | 
| + if source_file.endswith('.h'): | 
| + headers_in_sources.add(os.path.join(target_abs_path, source_file)) | 
| + return headers_in_sources |