OLD | NEW |
1 # Copyright 2013 The Chromium Authors. All rights reserved. | 1 # Copyright 2013 The Chromium Authors. All rights reserved. |
2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
4 | 4 |
5 import collections | 5 from collections import Mapping |
6 import os | 6 import os |
7 | 7 |
8 from api_schema_graph import APISchemaGraph | 8 from api_schema_graph import APISchemaGraph |
9 from branch_utility import BranchUtility | 9 from branch_utility import BranchUtility |
10 from compiled_file_system import CompiledFileSystem | 10 from compiled_file_system import CompiledFileSystem |
11 from svn_constants import API_PATH | 11 from svn_constants import API_PATH |
12 from third_party.json_schema_compiler import idl_schema, idl_parser, json_parse | 12 from third_party.json_schema_compiler import idl_schema, idl_parser, json_parse |
13 from third_party.json_schema_compiler.json_parse import OrderedDict | 13 from third_party.json_schema_compiler.json_parse import OrderedDict |
14 from third_party.json_schema_compiler.model import UnixName | 14 from third_party.json_schema_compiler.model import UnixName |
15 | 15 |
16 | 16 |
17 _EXTENSION_API = 'extension_api.json' | 17 _EXTENSION_API = 'extension_api.json' |
18 | 18 |
19 | 19 |
20 def _GetChannelFromFeatures(api_name, file_system, path): | 20 def _GetChannelFromFeatures(api_name, file_system, path): |
21 '''Finds API channel information within _features.json files at the given | 21 '''Finds API channel information within _features.json files at the given |
22 |path| for the given |file_system|. Returns None if channel information for | 22 |path| for the given |file_system|. Returns None if channel information for |
23 the API cannot be located. | 23 the API cannot be located. |
24 ''' | 24 ''' |
25 feature = file_system.GetFromFile(path).get(api_name) | 25 feature = file_system.GetFromFile(path).get(api_name) |
26 | 26 |
27 if feature is None: | 27 if feature is None: |
28 return None | 28 return None |
29 if isinstance(feature, collections.Mapping): | 29 if isinstance(feature, Mapping): |
30 # The channel information exists as a solitary dict. | 30 # The channel information exists as a solitary dict. |
31 return feature.get('channel') | 31 return feature.get('channel') |
32 # The channel information dict is nested within a list for whitelisting | 32 # The channel information dict is nested within a list for whitelisting |
33 # purposes. Take the newest channel out of all of the entries. | 33 # purposes. Take the newest channel out of all of the entries. |
34 return BranchUtility.NewestChannel(entry.get('channel') for entry in feature) | 34 return BranchUtility.NewestChannel(entry.get('channel') for entry in feature) |
35 | 35 |
36 | 36 |
37 def _GetChannelFromApiFeatures(api_name, file_system): | 37 def _GetChannelFromApiFeatures(api_name, file_system): |
38 return _GetChannelFromFeatures( | 38 return _GetChannelFromFeatures( |
39 api_name, | 39 api_name, |
40 file_system, | 40 file_system, |
41 '%s/_api_features.json' % API_PATH) | 41 '%s/_api_features.json' % API_PATH) |
42 | 42 |
43 | 43 |
44 def _GetChannelFromManifestFeatures(api_name, file_system): | 44 def _GetChannelFromManifestFeatures(api_name, file_system): |
45 return _GetChannelFromFeatures( | 45 return _GetChannelFromFeatures( |
46 UnixName(api_name), #_manifest_features uses unix_style API names | 46 UnixName(api_name), #_manifest_features uses unix_style API names |
47 file_system, | 47 file_system, |
48 '%s/_manifest_features.json' % API_PATH) | 48 '%s/_manifest_features.json' % API_PATH) |
49 | 49 |
50 | 50 |
51 def _GetChannelFromPermissionFeatures(api_name, file_system): | 51 def _GetChannelFromPermissionFeatures(api_name, file_system): |
52 return _GetChannelFromFeatures( | 52 return _GetChannelFromFeatures( |
53 api_name, | 53 api_name, |
54 file_system, | 54 file_system, |
55 '%s/_permission_features.json' % API_PATH) | 55 '%s/_permission_features.json' % API_PATH) |
56 | 56 |
57 | 57 |
| 58 def _GetApiSchemaFilename(api_name, file_system): |
| 59 '''Gets the name of the file which contains the schema for |api_name| in |
| 60 |file_system|, or None if the API is not found. Note that this may be the |
| 61 single _EXTENSION_API file which all APIs share in older versions of Chrome. |
| 62 ''' |
| 63 def under_api_path(path): |
| 64 return '%s/%s' % (API_PATH, path) |
| 65 |
| 66 file_names = file_system.ReadSingle(under_api_path('')) |
| 67 |
| 68 if _EXTENSION_API in file_names: |
| 69 # Prior to Chrome version 18, extension_api.json contained all API schema |
| 70 # data, which replaced the current implementation of individual API files. |
| 71 # We're forced to parse this (very large) file to determine if the API |
| 72 # exists in it. |
| 73 # |
| 74 # TODO(epeterson) Avoid doing unnecessary work by re-parsing. |
| 75 # See http://crbug.com/295812. |
| 76 extension_api_json = json_parse.Parse( |
| 77 file_system.ReadSingle('%s/%s'% (API_PATH, _EXTENSION_API))) |
| 78 if any(api['namespace'] == api_name for api in extension_api_json): |
| 79 return under_api_path(_EXTENSION_API) |
| 80 return None |
| 81 |
| 82 api_file_names = [ |
| 83 file_name for file_name in file_names |
| 84 if os.path.splitext(file_name)[0] in (api_name, UnixName(api_name))] |
| 85 assert len(api_file_names) < 2 |
| 86 return under_api_path(api_file_names[0]) if api_file_names else None |
| 87 |
| 88 |
| 89 def _HasApiSchema(api_name, file_system): |
| 90 return _GetApiSchemaFilename(api_name, file_system) is not None |
| 91 |
| 92 |
58 def _GetApiSchema(api_name, file_system): | 93 def _GetApiSchema(api_name, file_system): |
59 '''Searches |file_system| for |api_name|'s API schema data, and parses and | 94 '''Searches |file_system| for |api_name|'s API schema data, and parses and |
60 returns it if found. | 95 returns it if found. |
61 ''' | 96 ''' |
62 file_names = file_system.ReadSingle('%s/' % API_PATH) | 97 api_file_name = _GetApiSchemaFilename(api_name, file_system) |
63 # API names can be represented in unix_style and camelCase formats. | 98 if api_file_name is None: |
64 possibilities = (api_name, UnixName(api_name)) | 99 return None |
65 | 100 |
66 def get_file_data(file_name): | 101 api_file_text = file_system.ReadSingle(api_file_name) |
67 return file_system.ReadSingle('%s/%s' % (API_PATH, file_name)) | 102 if api_file_name == _EXTENSION_API: |
68 | 103 matching_schemas = [api for api in json_parse.Parse(api_file_text) |
69 if _EXTENSION_API in file_names: | 104 if api['namespace'] == api_name] |
70 # Prior to Chrome version 18, extension_api.json contained all API schema | 105 elif api_file_name.endswith('.idl'): |
71 # data, which replaced the current implementation of individual API files. | 106 matching_schemas = idl_parser.IDLParser().ParseData(api_file_text) |
72 # | 107 else: |
73 # TODO(epeterson) This file will be parsed a lot, but the data remains the | 108 matching_schemas = json_parse.Parse(api_file_text) |
74 # same for each API. Avoid doing unnecessary work by re-parsing. | 109 # There should only be a single matching schema per file. |
75 # (see http://crbug.com/295812) | 110 assert len(matching_schemas) == 1 |
76 extension_api_json = json_parse.Parse(get_file_data(_EXTENSION_API)) | 111 return matching_schemas |
77 api = [api for api in extension_api_json if api['namespace'] == api_name] | |
78 return api if api else None | |
79 | |
80 def check_file(file_name): | |
81 return os.path.splitext(file_name)[0] in (api_name, UnixName(api_name)) | |
82 | |
83 for file_name in file_names: | |
84 if check_file(file_name): | |
85 if file_name.endswith('idl'): | |
86 idl_data = idl_parser.IDLParser().ParseData(get_file_data(file_name)) | |
87 return idl_schema.IDLSchema(idl_data).process() | |
88 return json_parse.Parse(get_file_data(file_name)) | |
89 return None | |
90 | 112 |
91 | 113 |
92 class AvailabilityFinder(object): | 114 class AvailabilityFinder(object): |
93 '''Generates availability information for APIs by looking at API schemas and | 115 '''Generates availability information for APIs by looking at API schemas and |
94 _features files over multiple release versions of Chrome. | 116 _features files over multiple release versions of Chrome. |
95 ''' | 117 ''' |
96 | 118 |
97 def __init__(self, | 119 def __init__(self, |
98 file_system_iterator, | 120 file_system_iterator, |
99 object_store_creator, | 121 object_store_creator, |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
132 # found using _api_features.json. | 154 # found using _api_features.json. |
133 available_channel = available_channel or ( | 155 available_channel = available_channel or ( |
134 _GetChannelFromPermissionFeatures(api_name, features_fs) | 156 _GetChannelFromPermissionFeatures(api_name, features_fs) |
135 or _GetChannelFromManifestFeatures(api_name, features_fs)) | 157 or _GetChannelFromManifestFeatures(api_name, features_fs)) |
136 if available_channel is not None: | 158 if available_channel is not None: |
137 return available_channel == 'stable' | 159 return available_channel == 'stable' |
138 if version >= 5: | 160 if version >= 5: |
139 # Fall back to a check for file system existence if the API is not | 161 # Fall back to a check for file system existence if the API is not |
140 # stable in any of the _features.json files, or if the _features files | 162 # stable in any of the _features.json files, or if the _features files |
141 # do not exist (version 19 and earlier). | 163 # do not exist (version 19 and earlier). |
142 return _GetApiSchema(api_name, file_system) is not None | 164 return _HasApiSchema(api_name, file_system) |
143 | 165 |
144 def _CheckChannelAvailability(self, api_name, file_system, channel_name): | 166 def _CheckChannelAvailability(self, api_name, file_system, channel_name): |
145 '''Searches through the _features files in a given |file_system| and | 167 '''Searches through the _features files in a given |file_system| and |
146 determines whether or not an API is available on the given channel, | 168 determines whether or not an API is available on the given channel, |
147 |channel_name|. | 169 |channel_name|. |
148 ''' | 170 ''' |
149 fs_factory = CompiledFileSystem.Factory(file_system, | 171 fs_factory = CompiledFileSystem.Factory(file_system, |
150 self._object_store_creator) | 172 self._object_store_creator) |
151 features_fs = fs_factory.Create(lambda _, json: json_parse.Parse(json), | 173 features_fs = fs_factory.Create(lambda _, json: json_parse.Parse(json), |
152 AvailabilityFinder, | 174 AvailabilityFinder, |
153 category='features') | 175 category='features') |
154 available_channel = (_GetChannelFromApiFeatures(api_name, features_fs) | 176 available_channel = (_GetChannelFromApiFeatures(api_name, features_fs) |
155 or _GetChannelFromPermissionFeatures(api_name, features_fs) | 177 or _GetChannelFromPermissionFeatures(api_name, features_fs) |
156 or _GetChannelFromManifestFeatures(api_name, features_fs)) | 178 or _GetChannelFromManifestFeatures(api_name, features_fs)) |
157 if (available_channel is None and | 179 if available_channel is None and _HasApiSchema(api_name, file_system): |
158 _GetApiSchema(api_name, file_system) is not None): | |
159 # If an API is not represented in any of the _features files, but exists | 180 # If an API is not represented in any of the _features files, but exists |
160 # in the filesystem, then assume it is available in this version. | 181 # in the filesystem, then assume it is available in this version. |
161 # The windows API is an example of this. | 182 # The windows API is an example of this. |
162 available_channel = channel_name | 183 available_channel = channel_name |
163 # If the channel we're checking is the same as or newer than the | 184 # If the channel we're checking is the same as or newer than the |
164 # |available_channel| then the API is available at this channel. | 185 # |available_channel| then the API is available at this channel. |
165 return (available_channel is not None and | 186 return (available_channel is not None and |
166 BranchUtility.NewestChannel((available_channel, channel_name)) | 187 BranchUtility.NewestChannel((available_channel, channel_name)) |
167 == channel_name) | 188 == channel_name) |
168 | 189 |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
201 return availability | 222 return availability |
202 | 223 |
203 def GetApiNodeAvailability(self, api_name): | 224 def GetApiNodeAvailability(self, api_name): |
204 '''Returns an APISchemaGraph annotated with each node's availability (the | 225 '''Returns an APISchemaGraph annotated with each node's availability (the |
205 ChannelInfo at the oldest channel it's available in). | 226 ChannelInfo at the oldest channel it's available in). |
206 ''' | 227 ''' |
207 availability_graph = self._node_level_object_store.Get(api_name).Get() | 228 availability_graph = self._node_level_object_store.Get(api_name).Get() |
208 if availability_graph is not None: | 229 if availability_graph is not None: |
209 return availability_graph | 230 return availability_graph |
210 | 231 |
| 232 |
211 availability_graph = APISchemaGraph() | 233 availability_graph = APISchemaGraph() |
212 trunk_graph = APISchemaGraph(_GetApiSchema(api_name, | 234 trunk_graph = APISchemaGraph(_GetApiSchema(api_name, |
213 self._host_file_system)) | 235 self._host_file_system)) |
214 def update_availability_graph(file_system, channel_info): | 236 def update_availability_graph(file_system, channel_info): |
215 version_graph = APISchemaGraph(_GetApiSchema(api_name, file_system)) | 237 version_graph = APISchemaGraph(_GetApiSchema(api_name, file_system)) |
216 # Keep track of any new schema elements from this version by adding | 238 # Keep track of any new schema elements from this version by adding |
217 # them to |availability_graph|. | 239 # them to |availability_graph|. |
218 # | 240 # |
219 # Calling |availability_graph|.Lookup() on the nodes being updated | 241 # Calling |availability_graph|.Lookup() on the nodes being updated |
220 # will return the |annotation| object. | 242 # will return the |annotation| object. |
221 availability_graph.Update(version_graph.Subtract(availability_graph), | 243 availability_graph.Update(version_graph.Subtract(availability_graph), |
222 annotation=channel_info) | 244 annotation=channel_info) |
223 | 245 |
224 # Continue looping until there are no longer differences between this | 246 # Continue looping until there are no longer differences between this |
225 # version and trunk. | 247 # version and trunk. |
226 return trunk_graph != version_graph | 248 return trunk_graph != version_graph |
227 | 249 |
228 self._file_system_iterator.Ascending( | 250 self._file_system_iterator.Ascending(self.GetApiAvailability(api_name), |
229 self.GetApiAvailability(api_name), | 251 update_availability_graph) |
230 update_availability_graph) | |
231 | 252 |
232 self._node_level_object_store.Set(api_name, availability_graph) | 253 self._node_level_object_store.Set(api_name, availability_graph) |
233 return availability_graph | 254 return availability_graph |
OLD | NEW |