| #!/usr/bin/env python2.7 |
| # |
| # Copyright 2018 The Chromium Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| """Generate owners (.owners file) by looking at commit author for |
| libfuzzer test. |
| |
| Invoked by GN from fuzzer_test.gni. |
| """ |
| |
| import argparse |
| import os |
| import re |
| import subprocess |
| import sys |
| |
| AUTHOR_REGEX = re.compile('author-mail <(.+)>') |
| CHROMIUM_SRC_DIR = os.path.dirname( |
| os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) |
| OWNERS_FILENAME = 'OWNERS' |
| THIRD_PARTY_SEARCH_STRING = 'third_party' + os.sep |
| |
| |
| def GetAuthorFromGitBlame(blame_output): |
| """Return author from git blame output.""" |
| for line in blame_output.splitlines(): |
| m = AUTHOR_REGEX.match(line) |
| if m: |
| return m.group(1) |
| |
| return None |
| |
| |
| def GetOwnersIfThirdParty(source): |
| """Return owners using OWNERS file if in third_party.""" |
| match_index = source.find(THIRD_PARTY_SEARCH_STRING) |
| if match_index == -1: |
| # Not in third_party, skip. |
| return None |
| |
| match_index_with_library = source.find( |
| os.sep, match_index + len(THIRD_PARTY_SEARCH_STRING)) |
| if match_index_with_library == -1: |
| # Unable to determine library name, skip. |
| return None |
| |
| owners_file_path = os.path.join(source[:match_index_with_library], |
| OWNERS_FILENAME) |
| if not os.path.exists(owners_file_path): |
| return None |
| |
| return open(owners_file_path).read() |
| |
| |
| def GetOwnersForFuzzer(sources): |
| """Return owners given a list of sources as input.""" |
| if not sources: |
| return |
| |
| for source in sources: |
| if not os.path.exists(source): |
| continue |
| |
| with open(source, 'r') as source_file_handle: |
| source_content = source_file_handle.read() |
| |
| if SubStringExistsIn( |
| ['FuzzOneInput', 'LLVMFuzzerTestOneInput', 'PROTO_FUZZER'], |
| source_content): |
| # Found the fuzzer source (and not dependency of fuzzer). |
| |
| is_git_file = bool(subprocess.check_output(['git', 'ls-files', source])) |
| if not is_git_file: |
| # File is not in working tree. Return owners for third_party. |
| return GetOwnersIfThirdParty(source) |
| |
| # git log --follow and --reverse don't work together and using just |
| # --follow is too slow. Make a best estimate with an assumption that |
| # the original author has authored line 1 which is usually the |
| # copyright line and does not change even with file rename / move. |
| blame_output = subprocess.check_output( |
| ['git', 'blame', '--porcelain', '-L1,1', source]) |
| return GetAuthorFromGitBlame(blame_output) |
| |
| return None |
| |
| |
| def GetSourcesFromDeps(deps_list, build_dir): |
| """Return list of sources from parsing deps.""" |
| if not deps_list: |
| return None |
| |
| all_sources = [] |
| for deps in deps_list: |
| output = subprocess.check_output( |
| [GNPath(), 'desc', build_dir, deps, 'sources']) |
| for source in output.splitlines(): |
| if source.startswith('//'): |
| source = source[2:] |
| actual_source = os.path.join(CHROMIUM_SRC_DIR, source) |
| all_sources.append(actual_source) |
| |
| return all_sources |
| |
| |
| def GNPath(): |
| if sys.platform.startswith('linux'): |
| subdir, exe = 'linux64', 'gn' |
| elif sys.platform == 'darwin': |
| subdir, exe = 'mac', 'gn' |
| else: |
| subdir, exe = 'win', 'gn.exe' |
| |
| return os.path.join(CHROMIUM_SRC_DIR, 'buildtools', subdir, exe) |
| |
| |
| def SubStringExistsIn(substring_list, string): |
| """Return true if one of the substring in the list is found in |string|.""" |
| for substring in substring_list: |
| if substring in string: |
| return True |
| |
| return False |
| |
| |
| def main(): |
| parser = argparse.ArgumentParser(description='Generate fuzzer owners file.') |
| parser.add_argument('--owners', required=True) |
| parser.add_argument('--build-dir') |
| parser.add_argument('--deps', nargs='+') |
| parser.add_argument('--sources', nargs='+') |
| args = parser.parse_args() |
| |
| # Generate owners file. |
| with open(args.owners, 'w') as owners_file: |
| # If we found an owner, then write it to file. |
| # Otherwise, leave empty file to keep ninja happy. |
| owners = GetOwnersForFuzzer(args.sources) |
| if owners: |
| owners_file.write(owners) |
| return |
| |
| # Could not determine owners from |args.sources|. |
| # So, try parsing sources from |args.deps|. |
| deps_sources = GetSourcesFromDeps(args.deps, args.build_dir) |
| owners = GetOwnersForFuzzer(deps_sources) |
| if owners: |
| owners_file.write(owners) |
| |
| |
| if __name__ == '__main__': |
| main() |