blob: df565bd58d16a35a632beeb6cc73a5778b0fead9 [file] [log] [blame]
# Copyright 2014 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.
"""Finds and loads config.py files scattered through the source code tree."""
import imp
import os
import sys
from src.build import build_common
from src.build.build_options import OPTIONS
def _find_config_py(base_path):
"""Finds config.py files under |base_path| and its descendants.
Args:
base_path: The root path for the searching, relative to ARC_ROOT.
Yields:
Paths to the found config.py files, relative to ARC_ROOT.
"""
arc_root = build_common.get_arc_root()
base_abs_path = os.path.join(arc_root, base_path)
for dirpath, dirnames, filenames in os.walk(base_abs_path):
for name in filenames:
if name == 'config.py':
# Returns the path relative to ARC_ROOT.
# Note that, as |base_abs_path| is an absolute path, dirpath is
# also an absolute path.
yield os.path.relpath(os.path.join(dirpath, name), arc_root)
# "third_party" directories are out of our focus.
if 'third_party' in dirnames:
dirnames.remove('third_party')
def _import_package(package_path, filepath):
"""Import the package at given |package_path|.
This is almost same to __import__. The only difference is; when it is not
found, creates an empty package, and registers it. Practically, this happens
when __init__.py is not found.
Args:
package_path: The path of the package being imported.
filepath: The path on the file system corresponding to the package.
Returns:
Module instance.
"""
try:
return __import__(package_path)
except ImportError:
# Here, we assume __init__.py is missing. Create a new empty module
# instance.
package = imp.new_module(package_path)
package.__path__ = [filepath]
sys.modules[package_path] = package
if '.' in package_path:
parent, name = package_path.rsplit('.', 1)
parent_package = _import_package(parent, os.path.dirname(filepath))
setattr(parent_package, name, package)
return package
def _load_internal(path_list):
"""Loads all files in |path_list|.
The files are loaded as an appropriately named submodule.
Note that the modules will be loaded even if parent modules are not found,
like because of missing __init__.py. In such a case, empty modules will be
created.
Args:
path_list: a list of files to be loaded, relative to ARC_ROOT.
Returns:
A list of loaded modules.
"""
# For safety, acquire the import lock.
imp.acquire_lock()
try:
result = []
for path in path_list:
path = os.path.normpath(path)
abs_path = os.path.join(build_common.get_arc_root(), path)
module_name = os.path.splitext(path)[0].replace(os.sep, '.')
# Ensure ancestor packages.
if '.' in module_name:
_import_package(module_name.rsplit('.', 1)[0],
os.path.dirname(abs_path))
with open(abs_path, 'rb') as config_file:
result.append(imp.load_source(module_name, abs_path, config_file))
return result
finally:
imp.release_lock()
class ConfigLoader(object):
def __init__(self):
self._config_modules = []
def find_config_modules(self, attribute_name):
"""Finds the loaded config modules that have the specified attribute name.
Args:
attribute_name: the name of a field or method to be found.
Yields:
Modules that have the given |attribute_name|.
"""
for module in self._config_modules:
if hasattr(module, attribute_name):
yield module
def load(self):
"""Loads all config.py files in the project."""
search_root_list = [
os.path.join('mods', 'android'),
os.path.join('mods', 'chromium-ppapi'),
os.path.join('mods', 'examples'),
os.path.join('mods', 'graphics_translation'),
'src',
]
if OPTIONS.internal_apks_source_is_internal():
search_root_list.append('internal')
config_file_list = []
for search_root in search_root_list:
config_file_list.extend(_find_config_py(search_root))
self._config_modules = _load_internal(config_file_list)