OLD | NEW |
| (Empty) |
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 | |
5 import os | |
6 import sys | |
7 | |
8 sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname( | |
9 os.path.abspath(__file__))))) | |
10 from catapult_base.dependency_manager import base_config | |
11 from catapult_base.dependency_manager import exceptions | |
12 | |
13 | |
14 DEFAULT_TYPE = 'default' | |
15 | |
16 | |
17 class DependencyManager(object): | |
18 def __init__(self, configs, supported_config_types=None): | |
19 """Manages file dependencies found locally or in cloud_storage. | |
20 | |
21 Args: | |
22 configs: A list of instances of BaseConfig or it's subclasses, passed | |
23 in decreasing order of precedence. | |
24 supported_config_types: A list of whitelisted config_types. | |
25 No restrictions if None is specified. | |
26 | |
27 Raises: | |
28 ValueError: If |configs| is not a list of instances of BaseConfig or | |
29 its subclasses. | |
30 UnsupportedConfigFormatError: If supported_config_types is specified and | |
31 configs contains a config not in the supported config_types. | |
32 | |
33 Example: DependencyManager([config1, config2, config3]) | |
34 No requirements on the type of Config, and any dependencies that have | |
35 local files for the same platform will first look in those from | |
36 config1, then those from config2, and finally those from config3. | |
37 """ | |
38 if configs is None or type(configs) != list: | |
39 raise ValueError( | |
40 'Must supply a list of config files to DependencyManager') | |
41 # self._lookup_dict is a dictionary with the following format: | |
42 # { dependency1: {platform1: dependency_info1, | |
43 # platform2: dependency_info2} | |
44 # dependency2: {platform1: dependency_info3, | |
45 # ...} | |
46 # ...} | |
47 # | |
48 # Where the dependencies and platforms are strings, and the | |
49 # dependency_info's are DependencyInfo instances. | |
50 self._lookup_dict = {} | |
51 self.supported_configs = supported_config_types or [] | |
52 for config in configs: | |
53 self._UpdateDependencies(config) | |
54 | |
55 def FetchPath(self, dependency, platform): | |
56 """Get a path to an executable for |dependency|, downloading as needed. | |
57 | |
58 A path to a default executable may be returned if a platform specific | |
59 version is not specified in the config(s). | |
60 | |
61 Args: | |
62 dependency: Name of the desired dependency, as given in the config(s) | |
63 used in this DependencyManager. | |
64 platform: Name of the platform the dependency will run on. Often of the | |
65 form 'os_architecture'. Must match those specified in the config(s) | |
66 used in this DependencyManager. | |
67 Returns: | |
68 A path to an executable of |dependency| that will run on |platform|, | |
69 downloading from cloud storage if needed. | |
70 | |
71 Raises: | |
72 NoPathFoundError: If a local copy of the executable cannot be found and | |
73 a remote path could not be downloaded from cloud_storage. | |
74 CredentialsError: If cloud_storage credentials aren't configured. | |
75 PermissionError: If cloud_storage credentials are configured, but not | |
76 with an account that has permission to download the remote file. | |
77 NotFoundError: If the remote file does not exist where expected in | |
78 cloud_storage. | |
79 ServerError: If an internal server error is hit while downloading the | |
80 remote file. | |
81 CloudStorageError: If another error occured while downloading the remote | |
82 path. | |
83 FileNotFoundError: If an attempted download was otherwise unsuccessful. | |
84 | |
85 """ | |
86 dependency_info = self._GetDependencyInfo(dependency, platform) | |
87 if not dependency_info: | |
88 raise exceptions.NoPathFoundError(dependency, platform) | |
89 path = dependency_info.GetLocalPath() | |
90 if not path or not os.path.exists(path): | |
91 path = dependency_info.GetRemotePath() | |
92 if not path or not os.path.exists(path): | |
93 raise exceptions.NoPathFoundError(dependency, platform) | |
94 return path | |
95 | |
96 def LocalPath(self, dependency, platform): | |
97 """Get a path to a locally stored executable for |dependency|. | |
98 | |
99 A path to a default executable may be returned if a platform specific | |
100 version is not specified in the config(s). | |
101 Will not download the executable. | |
102 | |
103 Args: | |
104 dependency: Name of the desired dependency, as given in the config(s) | |
105 used in this DependencyManager. | |
106 platform: Name of the platform the dependency will run on. Often of the | |
107 form 'os_architecture'. Must match those specified in the config(s) | |
108 used in this DependencyManager. | |
109 Returns: | |
110 A path to an executable for |dependency| that will run on |platform|. | |
111 | |
112 Raises: | |
113 NoPathFoundError: If a local copy of the executable cannot be found. | |
114 """ | |
115 dependency_info = self._GetDependencyInfo(dependency, platform) | |
116 if not dependency_info: | |
117 raise exceptions.NoPathFoundError(dependency, platform) | |
118 local_path = dependency_info.GetLocalPath() | |
119 if not local_path or not os.path.exists(local_path): | |
120 raise exceptions.NoPathFoundError(dependency, platform) | |
121 return local_path | |
122 | |
123 def _UpdateDependencies(self, config): | |
124 """Add the dependency information stored in |config| to this instance. | |
125 | |
126 Args: | |
127 config: An instances of BaseConfig or a subclasses. | |
128 | |
129 Raises: | |
130 UnsupportedConfigFormatError: If supported_config_types was specified | |
131 and config is not in the supported config_types. | |
132 """ | |
133 if not isinstance(config, base_config.BaseConfig): | |
134 raise ValueError('Must use a BaseConfig or subclass instance with the ' | |
135 'DependencyManager.') | |
136 if (self.supported_configs and | |
137 config.GetConfigType() not in self.supported_configs): | |
138 raise exceptions.UnsupportedConfigFormatError(config.GetConfigType(), | |
139 config.config_path) | |
140 for dep_info in config.IterDependencyInfo(): | |
141 dependency = dep_info.dependency | |
142 platform = dep_info.platform | |
143 if dependency not in self._lookup_dict: | |
144 self._lookup_dict[dependency] = {} | |
145 if platform not in self._lookup_dict[dependency]: | |
146 self._lookup_dict[dependency][platform] = dep_info | |
147 else: | |
148 self._lookup_dict[dependency][platform].Update(dep_info) | |
149 | |
150 | |
151 def _GetDependencyInfo(self, dependency, platform): | |
152 """Get information for |dependency| on |platform|, or a default if needed. | |
153 | |
154 Args: | |
155 dependency: Name of the desired dependency, as given in the config(s) | |
156 used in this DependencyManager. | |
157 platform: Name of the platform the dependency will run on. Often of the | |
158 form 'os_architecture'. Must match those specified in the config(s) | |
159 used in this DependencyManager. | |
160 | |
161 Returns: The dependency_info for |dependency| on |platform| if it exists. | |
162 Or the default version of |dependency| if it exists, or None if neither | |
163 exist. | |
164 """ | |
165 if not self._lookup_dict or dependency not in self._lookup_dict: | |
166 return None | |
167 dependency_dict = self._lookup_dict[dependency] | |
168 device_type = platform | |
169 if not device_type in dependency_dict: | |
170 device_type = DEFAULT_TYPE | |
171 return dependency_dict.get(device_type) | |
172 | |
OLD | NEW |