| # Copyright 2015 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. |
| |
| import contextlib |
| import json |
| import logging |
| import os |
| import platform |
| import sys |
| import tempfile |
| import threading |
| |
| CATAPULT_ROOT_PATH = os.path.abspath(os.path.join( |
| os.path.dirname(__file__), '..', '..')) |
| DEPENDENCY_MANAGER_PATH = os.path.join( |
| CATAPULT_ROOT_PATH, 'dependency_manager') |
| PYMOCK_PATH = os.path.join( |
| CATAPULT_ROOT_PATH, 'third_party', 'mock') |
| |
| |
| @contextlib.contextmanager |
| def SysPath(path): |
| sys.path.append(path) |
| yield |
| if sys.path[-1] != path: |
| sys.path.remove(path) |
| else: |
| sys.path.pop() |
| |
| with SysPath(DEPENDENCY_MANAGER_PATH): |
| import dependency_manager # pylint: disable=import-error |
| |
| _ANDROID_BUILD_TOOLS = {'aapt', 'dexdump', 'split-select'} |
| |
| _DEVIL_DEFAULT_CONFIG = os.path.abspath(os.path.join( |
| os.path.dirname(__file__), 'devil_dependencies.json')) |
| |
| _LEGACY_ENVIRONMENT_VARIABLES = { |
| 'ADB_PATH': { |
| 'dependency_name': 'adb', |
| 'platform': 'linux2_x86_64', |
| }, |
| 'ANDROID_SDK_ROOT': { |
| 'dependency_name': 'android_sdk', |
| 'platform': 'linux2_x86_64', |
| }, |
| } |
| |
| |
| def EmptyConfig(): |
| return { |
| 'config_type': 'BaseConfig', |
| 'dependencies': {} |
| } |
| |
| |
| def LocalConfigItem(dependency_name, dependency_platform, dependency_path): |
| if isinstance(dependency_path, basestring): |
| dependency_path = [dependency_path] |
| return { |
| dependency_name: { |
| 'file_info': { |
| dependency_platform: { |
| 'local_paths': dependency_path |
| }, |
| }, |
| }, |
| } |
| |
| |
| def _GetEnvironmentVariableConfig(): |
| env_config = EmptyConfig() |
| path_config = ( |
| (os.environ.get(k), v) |
| for k, v in _LEGACY_ENVIRONMENT_VARIABLES.iteritems()) |
| path_config = ((p, c) for p, c in path_config if p) |
| for p, c in path_config: |
| env_config['dependencies'].update( |
| LocalConfigItem(c['dependency_name'], c['platform'], p)) |
| return env_config |
| |
| |
| class _Environment(object): |
| |
| def __init__(self): |
| self._dm_init_lock = threading.Lock() |
| self._dm = None |
| self._logging_init_lock = threading.Lock() |
| self._logging_initialized = False |
| |
| def Initialize(self, configs=None, config_files=None): |
| """Initialize devil's environment from configuration files. |
| |
| This uses all configurations provided via |configs| and |config_files| |
| to determine the locations of devil's dependencies. Configurations should |
| all take the form described by py_utils.dependency_manager.BaseConfig. |
| If no configurations are provided, a default one will be used if available. |
| |
| Args: |
| configs: An optional list of dict configurations. |
| config_files: An optional list of files to load |
| """ |
| |
| # Make sure we only initialize self._dm once. |
| with self._dm_init_lock: |
| if self._dm is None: |
| if configs is None: |
| configs = [] |
| |
| env_config = _GetEnvironmentVariableConfig() |
| if env_config: |
| configs.insert(0, env_config) |
| self._InitializeRecursive( |
| configs=configs, |
| config_files=config_files) |
| assert self._dm is not None, 'Failed to create dependency manager.' |
| |
| def _InitializeRecursive(self, configs=None, config_files=None): |
| # This recurses through configs to create temporary files for each and |
| # take advantage of context managers to appropriately close those files. |
| # TODO(jbudorick): Remove this recursion if/when dependency_manager |
| # supports loading configurations directly from a dict. |
| if configs: |
| with tempfile.NamedTemporaryFile(delete=False) as next_config_file: |
| try: |
| next_config_file.write(json.dumps(configs[0])) |
| next_config_file.close() |
| self._InitializeRecursive( |
| configs=configs[1:], |
| config_files=[next_config_file.name] + (config_files or [])) |
| finally: |
| if os.path.exists(next_config_file.name): |
| os.remove(next_config_file.name) |
| else: |
| config_files = config_files or [] |
| if 'DEVIL_ENV_CONFIG' in os.environ: |
| config_files.append(os.environ.get('DEVIL_ENV_CONFIG')) |
| config_files.append(_DEVIL_DEFAULT_CONFIG) |
| |
| self._dm = dependency_manager.DependencyManager( |
| [dependency_manager.BaseConfig(c) for c in config_files]) |
| |
| def InitializeLogging(self, log_level, formatter=None, handler=None): |
| if self._logging_initialized: |
| return |
| |
| with self._logging_init_lock: |
| if self._logging_initialized: |
| return |
| |
| formatter = formatter or logging.Formatter( |
| '%(threadName)-4s %(message)s') |
| handler = handler or logging.StreamHandler(sys.stdout) |
| handler.setFormatter(formatter) |
| |
| devil_logger = logging.getLogger('devil') |
| devil_logger.setLevel(log_level) |
| devil_logger.propagate = False |
| devil_logger.addHandler(handler) |
| |
| import py_utils.cloud_storage |
| lock_logger = py_utils.cloud_storage.logger |
| lock_logger.setLevel(log_level) |
| lock_logger.propagate = False |
| lock_logger.addHandler(handler) |
| |
| self._logging_initialized = True |
| |
| def FetchPath(self, dependency, arch=None, device=None): |
| if self._dm is None: |
| self.Initialize() |
| if dependency in _ANDROID_BUILD_TOOLS: |
| self.FetchPath('android_build_tools_libc++', arch=arch, device=device) |
| return self._dm.FetchPath(dependency, GetPlatform(arch, device)) |
| |
| def LocalPath(self, dependency, arch=None, device=None): |
| if self._dm is None: |
| self.Initialize() |
| return self._dm.LocalPath(dependency, GetPlatform(arch, device)) |
| |
| def PrefetchPaths(self, dependencies=None, arch=None, device=None): |
| return self._dm.PrefetchPaths( |
| GetPlatform(arch, device), dependencies=dependencies) |
| |
| |
| def GetPlatform(arch=None, device=None): |
| if arch or device: |
| return 'android_%s' % (arch or device.product_cpu_abi) |
| return '%s_%s' % (sys.platform, platform.machine()) |
| |
| |
| config = _Environment() |
| |