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 | |
Jeffrey Yasskin
2013/10/02 00:19:17
Do we still need this code?
not at google - send to devlin
2013/10/02 00:40:02
Yes, this is the class responsible for stepping ba
| |
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 | |
58 def _GetApiSchema(api_name, file_system): | 89 def _GetApiSchema(api_name, file_system): |
59 '''Searches |file_system| for |api_name|'s API schema data, and parses and | 90 '''Searches |file_system| for |api_name|'s API schema data, and parses and |
60 returns it if found. | 91 returns it if found. |
61 ''' | 92 ''' |
62 file_names = file_system.ReadSingle('%s/' % API_PATH) | 93 api_file_name = _GetApiSchemaFilename(api_name, file_system) |
63 # API names can be represented in unix_style and camelCase formats. | 94 if api_file_name is None: |
64 possibilities = (api_name, UnixName(api_name)) | 95 return None |
65 | 96 |
66 def get_file_data(file_name): | 97 api_file_text = file_system.ReadSingle(api_file_name) |
67 return file_system.ReadSingle('%s/%s' % (API_PATH, file_name)) | 98 if api_file_name == _EXTENSION_API: |
68 | 99 return [api for api in json_parse.Parse(api_file_text) |
69 if _EXTENSION_API in file_names: | 100 if api['namespace'] == api_name][0] |
Jeffrey Yasskin
2013/10/02 00:19:17
It looks like the old code would return None if th
not at google - send to devlin
2013/10/02 00:40:02
It still does return None; _GetApiSchemaFilename w
| |
70 # Prior to Chrome version 18, extension_api.json contained all API schema | 101 if api_file_name.endswith('.idl'): |
71 # data, which replaced the current implementation of individual API files. | 102 return idl_parser.IDLParser().ParseData(api_file_text) |
72 # | 103 return json_parse.Parse(api_file_text) # [0]? |
Jeffrey Yasskin
2013/10/02 00:19:17
Figure out the "[0]?" before checking this in.
not at google - send to devlin
2013/10/02 00:40:02
Done.
| |
73 # TODO(epeterson) This file will be parsed a lot, but the data remains the | |
74 # same for each API. Avoid doing unnecessary work by re-parsing. | |
75 # (see http://crbug.com/295812) | |
76 extension_api_json = json_parse.Parse(get_file_data(_EXTENSION_API)) | |
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 | 104 |
91 | 105 |
92 class AvailabilityFinder(object): | 106 class AvailabilityFinder(object): |
93 '''Generates availability information for APIs by looking at API schemas and | 107 '''Generates availability information for APIs by looking at API schemas and |
94 _features files over multiple release versions of Chrome. | 108 _features files over multiple release versions of Chrome. |
95 ''' | 109 ''' |
96 | 110 |
97 def __init__(self, | 111 def __init__(self, |
98 file_system_iterator, | 112 file_system_iterator, |
99 object_store_creator, | 113 object_store_creator, |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
132 # found using _api_features.json. | 146 # found using _api_features.json. |
133 available_channel = available_channel or ( | 147 available_channel = available_channel or ( |
134 _GetChannelFromPermissionFeatures(api_name, features_fs) | 148 _GetChannelFromPermissionFeatures(api_name, features_fs) |
135 or _GetChannelFromManifestFeatures(api_name, features_fs)) | 149 or _GetChannelFromManifestFeatures(api_name, features_fs)) |
136 if available_channel is not None: | 150 if available_channel is not None: |
137 return available_channel == 'stable' | 151 return available_channel == 'stable' |
138 if version >= 5: | 152 if version >= 5: |
139 # Fall back to a check for file system existence if the API is not | 153 # 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 | 154 # stable in any of the _features.json files, or if the _features files |
141 # do not exist (version 19 and earlier). | 155 # do not exist (version 19 and earlier). |
142 return _GetApiSchema(api_name, file_system) is not None | 156 return _GetApiSchemaFilename(api_name, file_system) is not None |
Jeffrey Yasskin
2013/10/02 00:19:17
Replace these with "ApiExists(api_name, file_syste
not at google - send to devlin
2013/10/02 00:40:02
Done.
| |
143 | 157 |
144 def _CheckChannelAvailability(self, api_name, file_system, channel_name): | 158 def _CheckChannelAvailability(self, api_name, file_system, channel_name): |
145 '''Searches through the _features files in a given |file_system| and | 159 '''Searches through the _features files in a given |file_system| and |
146 determines whether or not an API is available on the given channel, | 160 determines whether or not an API is available on the given channel, |
147 |channel_name|. | 161 |channel_name|. |
148 ''' | 162 ''' |
149 fs_factory = CompiledFileSystem.Factory(file_system, | 163 fs_factory = CompiledFileSystem.Factory(file_system, |
150 self._object_store_creator) | 164 self._object_store_creator) |
151 features_fs = fs_factory.Create(lambda _, json: json_parse.Parse(json), | 165 features_fs = fs_factory.Create(lambda _, json: json_parse.Parse(json), |
152 AvailabilityFinder, | 166 AvailabilityFinder, |
153 category='features') | 167 category='features') |
154 available_channel = (_GetChannelFromApiFeatures(api_name, features_fs) | 168 available_channel = (_GetChannelFromApiFeatures(api_name, features_fs) |
155 or _GetChannelFromPermissionFeatures(api_name, features_fs) | 169 or _GetChannelFromPermissionFeatures(api_name, features_fs) |
156 or _GetChannelFromManifestFeatures(api_name, features_fs)) | 170 or _GetChannelFromManifestFeatures(api_name, features_fs)) |
157 if (available_channel is None and | 171 if (available_channel is None and |
158 _GetApiSchema(api_name, file_system) is not None): | 172 _GetApiSchemaFilename(api_name, file_system) is not None): |
159 # If an API is not represented in any of the _features files, but exists | 173 # 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. | 174 # in the filesystem, then assume it is available in this version. |
161 # The windows API is an example of this. | 175 # The windows API is an example of this. |
162 available_channel = channel_name | 176 available_channel = channel_name |
163 # If the channel we're checking is the same as or newer than the | 177 # 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. | 178 # |available_channel| then the API is available at this channel. |
165 return (available_channel is not None and | 179 return (available_channel is not None and |
166 BranchUtility.NewestChannel((available_channel, channel_name)) | 180 BranchUtility.NewestChannel((available_channel, channel_name)) |
167 == channel_name) | 181 == channel_name) |
168 | 182 |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
201 return availability | 215 return availability |
202 | 216 |
203 def GetApiNodeAvailability(self, api_name): | 217 def GetApiNodeAvailability(self, api_name): |
204 '''Returns an APISchemaGraph annotated with each node's availability (the | 218 '''Returns an APISchemaGraph annotated with each node's availability (the |
205 ChannelInfo at the oldest channel it's available in). | 219 ChannelInfo at the oldest channel it's available in). |
206 ''' | 220 ''' |
207 availability_graph = self._node_level_object_store.Get(api_name).Get() | 221 availability_graph = self._node_level_object_store.Get(api_name).Get() |
208 if availability_graph is not None: | 222 if availability_graph is not None: |
209 return availability_graph | 223 return availability_graph |
210 | 224 |
225 | |
211 availability_graph = APISchemaGraph() | 226 availability_graph = APISchemaGraph() |
212 trunk_graph = APISchemaGraph(_GetApiSchema(api_name, | 227 trunk_graph = APISchemaGraph(_GetApiSchema(api_name, |
213 self._host_file_system)) | 228 self._host_file_system)) |
214 def update_availability_graph(file_system, channel_info): | 229 def update_availability_graph(file_system, channel_info): |
215 version_graph = APISchemaGraph(_GetApiSchema(api_name, file_system)) | 230 version_graph = APISchemaGraph(_GetApiSchema(api_name, file_system)) |
216 # Keep track of any new schema elements from this version by adding | 231 # Keep track of any new schema elements from this version by adding |
217 # them to |availability_graph|. | 232 # them to |availability_graph|. |
218 # | 233 # |
219 # Calling |availability_graph|.Lookup() on the nodes being updated | 234 # Calling |availability_graph|.Lookup() on the nodes being updated |
220 # will return the |annotation| object. | 235 # will return the |annotation| object. |
221 availability_graph.Update(version_graph.Subtract(availability_graph), | 236 availability_graph.Update(version_graph.Subtract(availability_graph), |
222 annotation=channel_info) | 237 annotation=channel_info) |
223 | 238 |
224 # Continue looping until there are no longer differences between this | 239 # Continue looping until there are no longer differences between this |
225 # version and trunk. | 240 # version and trunk. |
226 return trunk_graph != version_graph | 241 return trunk_graph != version_graph |
227 | 242 |
228 self._file_system_iterator.Ascending( | 243 self._file_system_iterator.Ascending(self.GetApiAvailability(api_name), |
229 self.GetApiAvailability(api_name), | 244 update_availability_graph) |
230 update_availability_graph) | |
231 | 245 |
232 self._node_level_object_store.Set(api_name, availability_graph) | 246 self._node_level_object_store.Set(api_name, availability_graph) |
233 return availability_graph | 247 return availability_graph |
OLD | NEW |