blob: 8fba3082a93917a11ebd49b25e0d4d755ade0686 [file] [log] [blame]
# Copyright 2021 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.
"""Helpers for determining Component of directories."""
import os
import re
_METADATA_COMPONENT_REGEX = re.compile(r'^\s*component:\s*"(.*?)"',
_METADATA_MIXINS_REGEX = re.compile(r'^\s*mixins:\s*"(.*?)"', re.MULTILINE)
# Paths that are missing metadata, and where it's hard to add (e.g. code in
# other repositories.
os.path.join('third_party', 'webrtc'): 'Blink>WebRTC',
os.path.join('logging', 'rtc_event_log'): 'Blink>WebRTC', # Generated files
os.path.join('modules'): 'Blink>WebRTC', # Generated files
def _SafeRead(path):
with open(path) as f:
except IOError:
# Need to catch both FileNotFoundError and NotADirectoryError since
# source_paths for .aar files look like: /path/to/lib.aar/path/within/zip
return ''
class _ComponentLookupContext:
def __init__(self, source_directory):
self._mixins_cache = {}
self._dir_cache = _COMPONENT_DEFAULTS.copy()
self._source_directory = source_directory
def ComponentForSourcePath(self, source_path):
return self._ComponentForDirectory(os.path.dirname(source_path))
def _ParseComponentFromMetadata(self, path):
"""Extracts Component from DIR_METADATA."""
result = self._mixins_cache.get(path)
if result is not None:
return result
data = _SafeRead(path)
m =
if m:
result =
# Recurse on mixins.
self._mixins_cache[path] = '' # Guard against cycles.
result = ''
for mixin_path in _METADATA_MIXINS_REGEX.findall(data):
if mixin_path.startswith('//'):
mixin_path = os.path.join(self._source_directory, mixin_path[2:])
logging.warning('Found non-ablsolute mixin path in %s', path)
result = self._ParseComponentFromMetadata(mixin_path)
if result:
self._mixins_cache[path] = result
return result
def _ComponentForDirectory(self, directory):
"""Searches all parent directories for COMPONENT in OWNERS files.
directory: Path of directory to start searching from relative to
COMPONENT belonging to |path|, or empty string if not found.
assert not os.path.isabs(directory)
component = self._dir_cache.get(directory)
if component is not None:
return component
metadata_path = os.path.join(self._source_directory, directory,
result = self._ParseComponentFromMetadata(metadata_path)
if not result:
parent_directory = os.path.dirname(directory)
if parent_directory:
result = self._ComponentForDirectory(parent_directory)
self._dir_cache[directory] = result
return result
def PopulateComponents(raw_symbols, source_directory, default_component):
"""Populates the |component| field based on |source_path|.
Symbols without a |source_path| are skipped.
raw_symbols: list of Symbol objects.
source_directory: Directory to use as the root.
default_component: Component to use when none was found.
context = _ComponentLookupContext(source_directory)
for symbol in raw_symbols:
if symbol.source_path:
found_component = context.ComponentForSourcePath(symbol.source_path)
symbol.component = found_component or default_component