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