1#!/usr/bin/env python 2# Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. 3# 4# Use of this source code is governed by a BSD-style license 5# that can be found in the LICENSE file in the root of the source 6# tree. An additional intellectual property rights grant can be found 7# in the file PATENTS. All contributing project authors may 8# be found in the AUTHORS file in the root of the source tree. 9 10import os 11import re 12import string 13 14 15# TARGET_RE matches a GN target, and extracts the target name and the contents. 16TARGET_RE = re.compile(r'(?P<indent>\s*)\w+\("(?P<target_name>\w+)"\) {' 17 r'(?P<target_contents>.*?)' 18 r'(?P=indent)}', 19 re.MULTILINE | re.DOTALL) 20 21# SOURCES_RE matches a block of sources inside a GN target. 22SOURCES_RE = re.compile( 23 r'(sources|public|common_objc_headers) \+?= \[(?P<sources>.*?)\]', 24 re.MULTILINE | re.DOTALL) 25 26SOURCE_FILE_RE = re.compile(r'.*\"(?P<source_file>.*)\"') 27 28 29class NoBuildGnFoundError(Exception): 30 pass 31 32 33class WrongFileTypeError(Exception): 34 pass 35 36 37def _ReadFile(file_path): 38 """Returns the content of file_path in a string. 39 40 Args: 41 file_path: the path of the file to read. 42 Returns: 43 A string with the content of the file. 44 """ 45 with open(file_path) as f: 46 return f.read() 47 48 49def GetBuildGnPathFromFilePath(file_path, file_exists_check, root_dir_path): 50 """Returns the BUILD.gn file responsible for file_path. 51 52 Args: 53 file_path: the absolute path to the .h file to check. 54 file_exists_check: a function that defines how to check if a file exists 55 on the file system. 56 root_dir_path: the absolute path of the root of project. 57 58 Returns: 59 A string with the absolute path to the BUILD.gn file responsible to include 60 file_path in a target. 61 """ 62 if not file_path.endswith('.h'): 63 raise WrongFileTypeError( 64 'File {} is not an header file (.h)'.format(file_path)) 65 candidate_dir = os.path.dirname(file_path) 66 while candidate_dir.startswith(root_dir_path): 67 candidate_build_gn_path = os.path.join(candidate_dir, 'BUILD.gn') 68 if file_exists_check(candidate_build_gn_path): 69 return candidate_build_gn_path 70 else: 71 candidate_dir = os.path.abspath(os.path.join(candidate_dir, 72 os.pardir)) 73 raise NoBuildGnFoundError( 74 'No BUILD.gn file found for file: `{}`'.format(file_path)) 75 76 77def IsHeaderInBuildGn(header_path, build_gn_path): 78 """Returns True if the header is listed in the BUILD.gn file. 79 80 Args: 81 header_path: the absolute path to the header to check. 82 build_gn_path: the absolute path to the header to check. 83 84 Returns: 85 bool: True if the header_path is an header that is listed in 86 at least one GN target in the BUILD.gn file specified by 87 the argument build_gn_path. 88 """ 89 target_abs_path = os.path.dirname(build_gn_path) 90 build_gn_content = _ReadFile(build_gn_path) 91 headers_in_build_gn = GetHeadersInBuildGnFileSources(build_gn_content, 92 target_abs_path) 93 return header_path in headers_in_build_gn 94 95 96def GetHeadersInBuildGnFileSources(file_content, target_abs_path): 97 """Returns a set with all the .h files in the file_content. 98 99 Args: 100 file_content: a string with the content of the BUILD.gn file. 101 target_abs_path: the absolute path to the directory where the 102 BUILD.gn file lives. 103 104 Returns: 105 A set with all the headers (.h file) in the file_content. 106 The set contains absolute paths. 107 """ 108 headers_in_sources = set([]) 109 for target_match in TARGET_RE.finditer(file_content): 110 target_contents = target_match.group('target_contents') 111 for sources_match in SOURCES_RE.finditer(target_contents): 112 sources = sources_match.group('sources') 113 for source_file_match in SOURCE_FILE_RE.finditer(sources): 114 source_file = source_file_match.group('source_file') 115 if source_file.endswith('.h'): 116 source_file_tokens = string.split(source_file, '/') 117 headers_in_sources.add(os.path.join(target_abs_path, 118 *source_file_tokens)) 119 return headers_in_sources 120