mirror of
https://github.com/mollyim/webrtc.git
synced 2025-05-12 21:30:45 +01:00

Bug: webrtc:13607 Change-Id: Ib018e43ea977cc24dd71048e68e3343741f7f31b Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/249083 Reviewed-by: Mirko Bonadei <mbonadei@webrtc.org> Reviewed-by: Harald Alvestrand <hta@webrtc.org> Reviewed-by: Jeremy Leconte <jleconte@google.com> Commit-Queue: Christoffer Jansson <jansson@google.com> Cr-Commit-Position: refs/heads/main@{#35953}
133 lines
4.6 KiB
Python
133 lines
4.6 KiB
Python
#!/usr/bin/env vpython3
|
|
|
|
# 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 argparse
|
|
import collections
|
|
import os
|
|
import re
|
|
import sys
|
|
|
|
# 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)
|
|
|
|
ERROR_MESSAGE = ("{build_file_path} in target '{target_name}':\n"
|
|
" Source file '{source_file}'\n"
|
|
" crosses boundary of package '{subpackage}'.")
|
|
|
|
|
|
class PackageBoundaryViolation(
|
|
collections.namedtuple(
|
|
'PackageBoundaryViolation',
|
|
'build_file_path target_name source_file subpackage')):
|
|
def __str__(self):
|
|
return ERROR_MESSAGE.format(**self._asdict())
|
|
|
|
|
|
def _BuildSubpackagesPattern(packages, query):
|
|
"""Returns a regular expression that matches source files inside subpackages
|
|
of the given query."""
|
|
query += os.path.sep
|
|
length = len(query)
|
|
pattern = r'\s*"(?P<source_file>(?P<subpackage>'
|
|
pattern += '|'.join(
|
|
re.escape(package[length:].replace(os.path.sep, '/'))
|
|
for package in packages if package.startswith(query))
|
|
pattern += r')/[\w\./]*)"'
|
|
return re.compile(pattern)
|
|
|
|
|
|
def _ReadFileAndPrependLines(file_path):
|
|
"""Reads the contents of a file."""
|
|
with open(file_path) as f:
|
|
return "".join(f.readlines())
|
|
|
|
|
|
def _CheckBuildFile(build_file_path, packages):
|
|
"""Iterates over all the targets of the given BUILD.gn file, and verifies that
|
|
the source files referenced by it don't belong to any of it's subpackages.
|
|
Returns an iterator over PackageBoundaryViolations for this package.
|
|
"""
|
|
package = os.path.dirname(build_file_path)
|
|
subpackages_re = _BuildSubpackagesPattern(packages, package)
|
|
|
|
build_file_contents = _ReadFileAndPrependLines(build_file_path)
|
|
for target_match in TARGET_RE.finditer(build_file_contents):
|
|
target_name = target_match.group('target_name')
|
|
target_contents = target_match.group('target_contents')
|
|
for sources_match in SOURCES_RE.finditer(target_contents):
|
|
sources = sources_match.group('sources')
|
|
for subpackages_match in subpackages_re.finditer(sources):
|
|
subpackage = subpackages_match.group('subpackage')
|
|
source_file = subpackages_match.group('source_file')
|
|
if subpackage:
|
|
yield PackageBoundaryViolation(build_file_path, target_name,
|
|
source_file, subpackage)
|
|
|
|
|
|
def CheckPackageBoundaries(root_dir, build_files=None):
|
|
packages = [
|
|
root for root, _, files in os.walk(root_dir) if 'BUILD.gn' in files
|
|
]
|
|
|
|
if build_files is not None:
|
|
for build_file_path in build_files:
|
|
assert build_file_path.startswith(root_dir)
|
|
else:
|
|
build_files = [os.path.join(package, 'BUILD.gn') for package in packages]
|
|
|
|
messages = []
|
|
for build_file_path in build_files:
|
|
messages.extend(_CheckBuildFile(build_file_path, packages))
|
|
return messages
|
|
|
|
|
|
def main(argv):
|
|
parser = argparse.ArgumentParser(
|
|
description='Script that checks package boundary violations in GN '
|
|
'build files.')
|
|
|
|
parser.add_argument('root_dir',
|
|
metavar='ROOT_DIR',
|
|
help='The root directory that contains all BUILD.gn '
|
|
'files to be processed.')
|
|
parser.add_argument('build_files',
|
|
metavar='BUILD_FILE',
|
|
nargs='*',
|
|
help='A list of BUILD.gn files to be processed. If no '
|
|
'files are given, all BUILD.gn files under ROOT_DIR '
|
|
'will be processed.')
|
|
parser.add_argument('--max_messages',
|
|
type=int,
|
|
default=None,
|
|
help='If set, the maximum number of violations to be '
|
|
'displayed.')
|
|
|
|
args = parser.parse_args(argv)
|
|
|
|
messages = CheckPackageBoundaries(args.root_dir, args.build_files)
|
|
messages = messages[:args.max_messages]
|
|
|
|
for i, message in enumerate(messages):
|
|
if i > 0:
|
|
print()
|
|
print(message)
|
|
|
|
return bool(messages)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
sys.exit(main(sys.argv[1:]))
|