• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2023 The Chromium Authors
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4"""Python module to find feature names in source code.
5
6These functions are declared in a separate module to allow multiprocessing to
7correctly unpickle the called functions again.
8"""
9
10import glob
11import itertools
12import multiprocessing
13import pathlib
14import re
15
16BASE_FEATURE_PATTERN = br'BASE_FEATURE\((.*?),(.*?),(.*?)\);'
17BASE_FEATURE_RE = re.compile(BASE_FEATURE_PATTERN,
18                             flags=re.MULTILINE + re.DOTALL)
19
20# Only search these directories for flags. If your flag is outside these root
21# directories, then add the directory here.
22DIRECTORIES_TO_SEARCH = [
23    'android_webview',
24    'apps',
25    'ash',
26    'base',
27    'cc',
28    'chrome',
29    'chromecast',
30    'chromeos',
31    'clank',
32    'components',
33    'content',
34    'courgette',
35    'crypto',
36    'dbus',
37    'device',
38    'extensions',
39    'fuchsia_web',
40    'gin',
41    'google_apis',
42    'gpu',
43    'headless',
44    'infra',
45    'internal',
46    'ios',
47    'ipc',
48    'media',
49    'mojo',
50    'native_client',
51    'native_client_sdk',
52    'net',
53    'pdf',
54    'ppapi',
55    'printing',
56    'remoting',
57    'rlz',
58    'sandbox',
59    'services',
60    'skia',
61    'sql',
62    'storage',
63    # third_party/blink handled separately in FindDeclaredFeatures
64    'ui',
65    'url',
66    'v8',
67    'webkit',
68    'weblayer',
69]
70
71
72def _FindFeaturesInFile(filepath):
73  # Work on bytes to avoid utf-8 decode errors outside feature declarations
74  file_contents = pathlib.Path(filepath).read_bytes()
75  matches = BASE_FEATURE_RE.finditer(file_contents)
76  # Remove whitespace and surrounding " from the second argument
77  # which is the feature name.
78  return [m.group(2).strip().strip(b'"').decode('utf-8') for m in matches]
79
80
81def FindDeclaredFeatures(input_api):
82  """Finds all declared feature names in the source code.
83
84  This function will scan all *.cc and *.mm files and look for features
85  defined with the BASE_FEATURE macro. It will extract the feature names.
86
87  Args:
88    input_api: InputApi instance for opening files
89  Returns:
90    Set of defined feature names in the source tree.
91  """
92  # Features are supposed to be defined in .cc files.
93  # Iterate over the search folders in the root.
94  root = pathlib.Path(input_api.change.RepositoryRoot())
95  glob_patterns = [
96      str(p / pathlib.Path('**/*.cc')) for p in root.iterdir()
97      if p.is_dir() and p.name in DIRECTORIES_TO_SEARCH
98  ]
99
100  # blink is the only directory in third_party that should be searched.
101  blink_glob = str(root / pathlib.Path('third_party/blink/**/*.cc'))
102  glob_patterns.append(blink_glob)
103
104  # Additional features for iOS can be found in mm files in the ios directory.
105  mm_glob = str(root / pathlib.Path('ios/**/*.mm'))
106  glob_patterns.append(mm_glob)
107
108  # Create glob iterators that lazily go over the files to search
109  glob_iterators = [
110      glob.iglob(pattern, recursive=True) for pattern in glob_patterns
111  ]
112
113  # Limit to 4 processes - the disk accesses becomes a bottleneck with just a
114  # few processes, but splitting the searching across multiple CPUs does yield
115  # a benefit of a few seconds.
116  # The exact batch size does not seem to matter much, as long as it is >> 1.
117  pool = multiprocessing.Pool(4)
118  found_features = pool.imap_unordered(_FindFeaturesInFile,
119                                       itertools.chain(*glob_iterators), 1000)
120  pool.close()
121  pool.join()
122
123  feature_names = set()
124  for feature_list in found_features:
125    feature_names.update(feature_list)
126  return feature_names
127