1# Copyright 2015 The Chromium Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5import contextlib 6import json 7import os 8import platform 9import sys 10import tempfile 11import threading 12 13CATAPULT_ROOT_PATH = os.path.abspath(os.path.join( 14 os.path.dirname(__file__), '..', '..')) 15DEPENDENCY_MANAGER_PATH = os.path.join( 16 CATAPULT_ROOT_PATH, 'dependency_manager') 17PYMOCK_PATH = os.path.join( 18 CATAPULT_ROOT_PATH, 'third_party', 'mock') 19 20 21@contextlib.contextmanager 22def SysPath(path): 23 sys.path.append(path) 24 yield 25 if sys.path[-1] != path: 26 sys.path.remove(path) 27 else: 28 sys.path.pop() 29 30with SysPath(DEPENDENCY_MANAGER_PATH): 31 import dependency_manager # pylint: disable=import-error 32 33_ANDROID_BUILD_TOOLS = {'aapt', 'dexdump', 'split-select'} 34 35_DEVIL_DEFAULT_CONFIG = os.path.abspath(os.path.join( 36 os.path.dirname(__file__), 'devil_dependencies.json')) 37 38_LEGACY_ENVIRONMENT_VARIABLES = { 39 'ADB_PATH': { 40 'dependency_name': 'adb', 41 'platform': 'linux2_x86_64', 42 }, 43 'ANDROID_SDK_ROOT': { 44 'dependency_name': 'android_sdk', 45 'platform': 'linux2_x86_64', 46 }, 47} 48 49 50def EmptyConfig(): 51 return { 52 'config_type': 'BaseConfig', 53 'dependencies': {} 54 } 55 56 57def LocalConfigItem(dependency_name, dependency_platform, dependency_path): 58 if isinstance(dependency_path, basestring): 59 dependency_path = [dependency_path] 60 return { 61 dependency_name: { 62 'file_info': { 63 dependency_platform: { 64 'local_paths': dependency_path 65 }, 66 }, 67 }, 68 } 69 70 71def _GetEnvironmentVariableConfig(): 72 env_config = EmptyConfig() 73 path_config = ( 74 (os.environ.get(k), v) 75 for k, v in _LEGACY_ENVIRONMENT_VARIABLES.iteritems()) 76 path_config = ((p, c) for p, c in path_config if p) 77 for p, c in path_config: 78 env_config['dependencies'].update( 79 LocalConfigItem(c['dependency_name'], c['platform'], p)) 80 return env_config 81 82 83class _Environment(object): 84 85 def __init__(self): 86 self._dm_init_lock = threading.Lock() 87 self._dm = None 88 89 def Initialize(self, configs=None, config_files=None): 90 """Initialize devil's environment from configuration files. 91 92 This uses all configurations provided via |configs| and |config_files| 93 to determine the locations of devil's dependencies. Configurations should 94 all take the form described by py_utils.dependency_manager.BaseConfig. 95 If no configurations are provided, a default one will be used if available. 96 97 Args: 98 configs: An optional list of dict configurations. 99 config_files: An optional list of files to load 100 """ 101 102 # Make sure we only initialize self._dm once. 103 with self._dm_init_lock: 104 if self._dm is None: 105 if configs is None: 106 configs = [] 107 108 env_config = _GetEnvironmentVariableConfig() 109 if env_config: 110 configs.insert(0, env_config) 111 self._InitializeRecursive( 112 configs=configs, 113 config_files=config_files) 114 assert self._dm is not None, 'Failed to create dependency manager.' 115 116 def _InitializeRecursive(self, configs=None, config_files=None): 117 # This recurses through configs to create temporary files for each and 118 # take advantage of context managers to appropriately close those files. 119 # TODO(jbudorick): Remove this recursion if/when dependency_manager 120 # supports loading configurations directly from a dict. 121 if configs: 122 with tempfile.NamedTemporaryFile(delete=False) as next_config_file: 123 try: 124 next_config_file.write(json.dumps(configs[0])) 125 next_config_file.close() 126 self._InitializeRecursive( 127 configs=configs[1:], 128 config_files=[next_config_file.name] + (config_files or [])) 129 finally: 130 if os.path.exists(next_config_file.name): 131 os.remove(next_config_file.name) 132 else: 133 config_files = config_files or [] 134 if 'DEVIL_ENV_CONFIG' in os.environ: 135 config_files.append(os.environ.get('DEVIL_ENV_CONFIG')) 136 config_files.append(_DEVIL_DEFAULT_CONFIG) 137 138 self._dm = dependency_manager.DependencyManager( 139 [dependency_manager.BaseConfig(c) for c in config_files]) 140 141 def FetchPath(self, dependency, arch=None, device=None): 142 if self._dm is None: 143 self.Initialize() 144 if dependency in _ANDROID_BUILD_TOOLS: 145 self.FetchPath('android_build_tools_libc++', arch=arch, device=device) 146 return self._dm.FetchPath(dependency, GetPlatform(arch, device)) 147 148 def LocalPath(self, dependency, arch=None, device=None): 149 if self._dm is None: 150 self.Initialize() 151 return self._dm.LocalPath(dependency, GetPlatform(arch, device)) 152 153 154def GetPlatform(arch=None, device=None): 155 if device: 156 return 'android_%s' % (arch or device.product_cpu_abi) 157 return '%s_%s' % (sys.platform, platform.machine()) 158 159 160config = _Environment() 161 162