• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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