xref: /aosp_15_r20/external/chromium-trace/catapult/devil/devil/devil_env.py (revision 1fa4b3da657c0e9ad43c0220bacf9731820715a5)
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