1645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez# Copyright 2015 The Chromium Authors. All rights reserved.
2645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez# Use of this source code is governed by a BSD-style license that can be
3645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez# found in the LICENSE file.
4645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez
5645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavezimport contextlib
6645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavezimport json
7645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavezimport os
8645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavezimport platform
9645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavezimport sys
10645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavezimport tempfile
11645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavezimport threading
12645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez
13645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector ChavezCATAPULT_ROOT_PATH = os.path.abspath(os.path.join(
14645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez    os.path.dirname(__file__), '..', '..'))
15645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector ChavezDEPENDENCY_MANAGER_PATH = os.path.join(
16645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez    CATAPULT_ROOT_PATH, 'dependency_manager')
17645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector ChavezPYMOCK_PATH = os.path.join(
18645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez    CATAPULT_ROOT_PATH, 'third_party', 'mock')
19645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez
20645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez
21645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez@contextlib.contextmanager
22645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavezdef SysPath(path):
23645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez  sys.path.append(path)
24645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez  yield
25645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez  if sys.path[-1] != path:
26645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez    sys.path.remove(path)
27645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez  else:
28645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez    sys.path.pop()
29645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez
30645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavezwith SysPath(DEPENDENCY_MANAGER_PATH):
31645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez  import dependency_manager  # pylint: disable=import-error
32645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez
33645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez_ANDROID_BUILD_TOOLS = {'aapt', 'dexdump', 'split-select'}
34645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez
35645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez_DEVIL_DEFAULT_CONFIG = os.path.abspath(os.path.join(
36645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez    os.path.dirname(__file__), 'devil_dependencies.json'))
37645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez
38645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez_LEGACY_ENVIRONMENT_VARIABLES = {
39645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez  'ADB_PATH': {
40645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez    'dependency_name': 'adb',
41645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez    'platform': 'linux2_x86_64',
42645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez  },
43645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez  'ANDROID_SDK_ROOT': {
44645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez    'dependency_name': 'android_sdk',
45645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez    'platform': 'linux2_x86_64',
46645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez  },
47645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez}
48645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez
49645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez
50645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavezdef _GetEnvironmentVariableConfig():
51645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez  path_config = (
52645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez      (os.environ.get(k), v)
53645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez      for k, v in _LEGACY_ENVIRONMENT_VARIABLES.iteritems())
54645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez  return {
55645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez    'config_type': 'BaseConfig',
56645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez    'dependencies': {
57645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez      c['dependency_name']: {
58645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez        'file_info': {
59645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez          c['platform']: {
60645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez            'local_paths': [p],
61645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez          },
62645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez        },
63645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez      } for p, c in path_config if p
64645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez    },
65645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez  }
66645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez
67645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez
68645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavezclass _Environment(object):
69645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez
70645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez  def __init__(self):
71645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez    self._dm_init_lock = threading.Lock()
72645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez    self._dm = None
73645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez
74645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez  def Initialize(self, configs=None, config_files=None):
75645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez    """Initialize devil's environment from configuration files.
76645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez
77645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez    This uses all configurations provided via |configs| and |config_files|
78645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez    to determine the locations of devil's dependencies. Configurations should
79645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez    all take the form described by catapult_base.dependency_manager.BaseConfig.
80645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez    If no configurations are provided, a default one will be used if available.
81645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez
82645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez    Args:
83645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez      configs: An optional list of dict configurations.
84645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez      config_files: An optional list of files to load
85645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez    """
86645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez
87645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez    # Make sure we only initialize self._dm once.
88645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez    with self._dm_init_lock:
89645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez      if self._dm is None:
90645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez        if configs is None:
91645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez          configs = []
92645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez
93645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez        env_config = _GetEnvironmentVariableConfig()
94645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez        if env_config:
95645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez          configs.insert(0, env_config)
96645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez        self._InitializeRecursive(
97645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez            configs=configs,
98645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez            config_files=config_files)
99645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez        assert self._dm is not None, 'Failed to create dependency manager.'
100645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez
101645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez  def _InitializeRecursive(self, configs=None, config_files=None):
102645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez    # This recurses through configs to create temporary files for each and
103645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez    # take advantage of context managers to appropriately close those files.
104645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez    # TODO(jbudorick): Remove this recursion if/when dependency_manager
105645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez    # supports loading configurations directly from a dict.
106645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez    if configs:
107645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez      with tempfile.NamedTemporaryFile(delete=False) as next_config_file:
108645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez        try:
109645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez          next_config_file.write(json.dumps(configs[0]))
110645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez          next_config_file.close()
111645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez          self._InitializeRecursive(
112645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez              configs=configs[1:],
113645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez              config_files=[next_config_file.name] + (config_files or []))
114645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez        finally:
115645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez          if os.path.exists(next_config_file.name):
116645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez            os.remove(next_config_file.name)
117645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez    else:
118645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez      config_files = config_files or []
119645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez      if 'DEVIL_ENV_CONFIG' in os.environ:
120645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez        config_files.append(os.environ.get('DEVIL_ENV_CONFIG'))
121645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez      config_files.append(_DEVIL_DEFAULT_CONFIG)
122645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez
123645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez      self._dm = dependency_manager.DependencyManager(
124645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez          [dependency_manager.BaseConfig(c) for c in config_files])
125645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez
126645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez  def FetchPath(self, dependency, arch=None, device=None):
127645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez    if self._dm is None:
128645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez      self.Initialize()
129645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez    if dependency in _ANDROID_BUILD_TOOLS:
130645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez      self.FetchPath('android_build_tools_libc++', arch=arch, device=device)
131645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez    return self._dm.FetchPath(dependency, GetPlatform(arch, device))
132645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez
133645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez  def LocalPath(self, dependency, arch=None, device=None):
134645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez    if self._dm is None:
135645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez      self.Initialize()
136645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez    return self._dm.LocalPath(dependency, GetPlatform(arch, device))
137645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez
138645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez
139645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavezdef GetPlatform(arch=None, device=None):
140645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez  if device:
141645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez    return 'android_%s' % (arch or device.product_cpu_abi)
142645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez  return '%s_%s' % (sys.platform, platform.machine())
143645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez
144645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez
145645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavezconfig = _Environment()
146645501c2ab19a559ce82a1d5a29ced159a4c30fbLuis Hector Chavez
147