Chromium Code Reviews| 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 |