"""Python module to find feature names in source code.
These functions are declared in a separate module to allow multiprocessing to
correctly unpickle the called functions again.
"""
import glob
import itertools
import multiprocessing
import pathlib
import re
from typing import List, Set
BASE_FEATURE_PATTERN = br'BASE_FEATURE\((.*?)\);'
BASE_FEATURE_RE = re.compile(BASE_FEATURE_PATTERN,
flags=re.MULTILINE + re.DOTALL)
DIRECTORIES_TO_SEARCH = [
'android_webview',
'apps',
'ash',
'base',
'cc',
'chrome',
'chromecast',
'chromeos',
'clank',
'components',
'content',
'courgette',
'crypto',
'dbus',
'device',
'extensions',
'fuchsia_web',
'gin',
'google_apis',
'gpu',
'headless',
'infra',
'internal',
'ios',
'ipc',
'media',
'mojo',
'net',
'pdf',
'ppapi',
'printing',
'remoting',
'rlz',
'sandbox',
'services',
'skia',
'sql',
'storage',
'ui',
'url',
'v8',
'webkit',
'weblayer',
]
def _FindFeaturesInFile(filepath: str) -> List[str]:
file_contents = pathlib.Path(filepath).read_bytes()
matches = BASE_FEATURE_RE.finditer(file_contents)
feature_names = []
for m in matches:
args = [arg.strip() for arg in m.group(1).split(b',')]
if len(args) == 3:
feature_name = args[1].strip(b'"')
elif len(args) == 2:
feature_name = args[0]
if not feature_name.startswith(b'k'):
continue
feature_name = feature_name[1:]
else:
continue
feature_names.append(feature_name.decode('utf-8'))
return feature_names
def _FindDeclaredFeaturesImpl(repository_root: pathlib.Path) -> Set[str]:
root = pathlib.Path(repository_root)
glob_patterns = [
str(p / pathlib.Path('**/*.cc')) for p in root.iterdir()
if p.is_dir() and p.name in DIRECTORIES_TO_SEARCH
]
blink_glob = str(root / pathlib.Path('third_party/blink/**/*.cc'))
glob_patterns.append(blink_glob)
mm_glob = str(root / pathlib.Path('ios/**/*.mm'))
glob_patterns.append(mm_glob)
glob_iterators = [
glob.iglob(pattern, recursive=True) for pattern in glob_patterns
]
pool = multiprocessing.Pool(4)
found_features = pool.imap_unordered(_FindFeaturesInFile,
itertools.chain(*glob_iterators), 1000)
pool.close()
pool.join()
feature_names = set()
for feature_list in found_features:
feature_names.update(feature_list)
return feature_names
def FindDeclaredFeatures(input_api) -> Set[str]:
"""Finds all declared feature names in the source code.
This function will scan all *.cc and *.mm files and look for features
defined with the BASE_FEATURE macro. It will extract the feature names.
Args:
input_api: InputApi instance for opening files
Returns:
Set of defined feature names in the source tree.
"""
return _FindDeclaredFeaturesImpl(input_api.change.RepositoryRoot())
if __name__ == '__main__':
print(_FindDeclaredFeaturesImpl(pathlib.Path('.')))