Index: tools_webrtc/presubmit_checks_lib/check_orphan_headers.py |
diff --git a/tools_webrtc/presubmit_checks_lib/check_orphan_headers.py b/tools_webrtc/presubmit_checks_lib/check_orphan_headers.py |
new file mode 100644 |
index 0000000000000000000000000000000000000000..707c49fcc5347a3bc83257b0ff7d31cb9b90a34c |
--- /dev/null |
+++ b/tools_webrtc/presubmit_checks_lib/check_orphan_headers.py |
@@ -0,0 +1,115 @@ |
+#!/usr/bin/env python |
+# 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) |
+ |
+SOURCE_FILE_RE = re.compile(r'.*\"(?P<source_file>.*)\"') |
+ |
+ |
+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)) |
+ 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. |
+ |
+ 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([]) |
+ 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 |