OLD | NEW |
| (Empty) |
1 # Copyright (c) 2012 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 code | |
6 import cpp_util | |
7 from model import Platforms | |
8 from schema_util import CapitalizeFirstLetter | |
9 from schema_util import JsFunctionNameToClassName | |
10 | |
11 import json | |
12 import os | |
13 import re | |
14 | |
15 # TODO(miket/asargent) - parameterize this. | |
16 SOURCE_BASE_PATH = 'chrome/common/extensions/api' | |
17 | |
18 def _RemoveDescriptions(node): | |
19 """Returns a copy of |schema| with "description" fields removed. | |
20 """ | |
21 if isinstance(node, dict): | |
22 result = {} | |
23 for key, value in node.items(): | |
24 # Some schemas actually have properties called "description", so only | |
25 # remove descriptions that have string values. | |
26 if key == 'description' and isinstance(value, basestring): | |
27 continue | |
28 result[key] = _RemoveDescriptions(value) | |
29 return result | |
30 if isinstance(node, list): | |
31 return [_RemoveDescriptions(v) for v in node] | |
32 return node | |
33 | |
34 class SchemaBundleGenerator(object): | |
35 """This class contains methods to generate code based on multiple schemas. | |
36 """ | |
37 | |
38 def __init__(self, root, model, api_defs, cpp_type_generator): | |
39 self._root = root; | |
40 self._model = model | |
41 self._api_defs = api_defs | |
42 self._cpp_type_generator = cpp_type_generator | |
43 | |
44 def GenerateHeader(self, file_base, body_code): | |
45 """Generates a code.Code object for a header file | |
46 | |
47 Parameters: | |
48 - |file_base| - the base of the filename, e.g. 'foo' (for 'foo.h') | |
49 - |body_code| - the code to put in between the multiple inclusion guards""" | |
50 c = code.Code() | |
51 c.Append(cpp_util.CHROMIUM_LICENSE) | |
52 c.Append() | |
53 c.Append(cpp_util.GENERATED_BUNDLE_FILE_MESSAGE % SOURCE_BASE_PATH) | |
54 ifndef_name = cpp_util.GenerateIfndefName(SOURCE_BASE_PATH, file_base) | |
55 c.Append() | |
56 c.Append('#ifndef %s' % ifndef_name) | |
57 c.Append('#define %s' % ifndef_name) | |
58 c.Append() | |
59 c.Concat(body_code) | |
60 c.Append() | |
61 c.Append('#endif // %s' % ifndef_name) | |
62 c.Append() | |
63 return c | |
64 | |
65 def _GetPlatformIfdefs(self, model_object): | |
66 """Generates the "defined" conditional for an #if check if |model_object| | |
67 has platform restrictions. Returns None if there are no restrictions. | |
68 """ | |
69 if model_object.platforms is None: | |
70 return None | |
71 ifdefs = [] | |
72 for platform in model_object.platforms: | |
73 if platform == Platforms.CHROMEOS: | |
74 ifdefs.append('defined(OS_CHROMEOS)') | |
75 else: | |
76 raise ValueError("Unsupported platform ifdef: %s" % platform.name) | |
77 return ' and '.join(ifdefs) | |
78 | |
79 def GenerateAPIHeader(self): | |
80 """Generates the header for API registration / declaration""" | |
81 c = code.Code() | |
82 | |
83 c.Append('#include <string>') | |
84 c.Append() | |
85 c.Append('#include "base/basictypes.h"') | |
86 | |
87 for namespace in self._model.namespaces.values(): | |
88 namespace_name = namespace.unix_name.replace("experimental_", "") | |
89 implementation_header = namespace.compiler_options.get( | |
90 "implemented_in", | |
91 "chrome/browser/extensions/api/%s/%s_api.h" % (namespace_name, | |
92 namespace_name)) | |
93 if not os.path.exists( | |
94 os.path.join(self._root, os.path.normpath(implementation_header))): | |
95 if "implemented_in" in namespace.compiler_options: | |
96 raise ValueError('Header file for namespace "%s" specified in ' | |
97 'compiler_options not found: %s' % | |
98 (namespace.unix_name, implementation_header)) | |
99 continue | |
100 ifdefs = self._GetPlatformIfdefs(namespace) | |
101 if ifdefs is not None: | |
102 c.Append("#if %s" % ifdefs, indent_level=0) | |
103 | |
104 c.Append('#include "%s"' % implementation_header) | |
105 | |
106 if ifdefs is not None: | |
107 c.Append("#endif // %s" % ifdefs, indent_level=0) | |
108 | |
109 c.Append() | |
110 c.Append("class ExtensionFunctionRegistry;") | |
111 c.Append() | |
112 | |
113 c.Concat(self._cpp_type_generator.GetRootNamespaceStart()) | |
114 for namespace in self._model.namespaces.values(): | |
115 c.Append("// TODO(miket): emit code for %s" % (namespace.unix_name)) | |
116 c.Append() | |
117 c.Concat(self.GenerateFunctionRegistry()) | |
118 c.Concat(self._cpp_type_generator.GetRootNamespaceEnd()) | |
119 c.Append() | |
120 return self.GenerateHeader('generated_api', c) | |
121 | |
122 def _GetNamespaceFunctions(self, namespace): | |
123 functions = list(namespace.functions.values()) | |
124 if namespace.compiler_options.get("generate_type_functions", False): | |
125 for type_ in namespace.types.values(): | |
126 functions += list(type_.functions.values()) | |
127 return functions | |
128 | |
129 def GenerateFunctionRegistry(self): | |
130 c = code.Code() | |
131 c.Sblock("class GeneratedFunctionRegistry {") | |
132 c.Append(" public:") | |
133 c.Sblock("static void RegisterAll(ExtensionFunctionRegistry* registry) {") | |
134 for namespace in self._model.namespaces.values(): | |
135 namespace_ifdefs = self._GetPlatformIfdefs(namespace) | |
136 if namespace_ifdefs is not None: | |
137 c.Append("#if %s" % namespace_ifdefs, indent_level=0) | |
138 | |
139 namespace_name = CapitalizeFirstLetter(namespace.name.replace( | |
140 "experimental.", "")) | |
141 for function in self._GetNamespaceFunctions(namespace): | |
142 if function.nocompile: | |
143 continue | |
144 function_ifdefs = self._GetPlatformIfdefs(function) | |
145 if function_ifdefs is not None: | |
146 c.Append("#if %s" % function_ifdefs, indent_level=0) | |
147 | |
148 function_name = JsFunctionNameToClassName(namespace.name, function.name) | |
149 c.Append("registry->RegisterFunction<%sFunction>();" % ( | |
150 function_name)) | |
151 | |
152 if function_ifdefs is not None: | |
153 c.Append("#endif // %s" % function_ifdefs, indent_level=0) | |
154 | |
155 if namespace_ifdefs is not None: | |
156 c.Append("#endif // %s" % namespace_ifdefs, indent_level=0) | |
157 c.Eblock("}") | |
158 c.Eblock("};") | |
159 c.Append() | |
160 return c | |
161 | |
162 def GenerateSchemasHeader(self): | |
163 """Generates a code.Code object for the generated schemas .h file""" | |
164 c = code.Code() | |
165 c.Append('#include <map>') | |
166 c.Append('#include <string>') | |
167 c.Append(); | |
168 c.Append('#include "base/string_piece.h"') | |
169 c.Append() | |
170 c.Concat(self._cpp_type_generator.GetRootNamespaceStart()) | |
171 c.Append() | |
172 c.Sblock('class GeneratedSchemas {') | |
173 c.Append(' public:') | |
174 c.Append('// Puts all API schemas in |schemas|.') | |
175 c.Append('static void Get(' | |
176 'std::map<std::string, base::StringPiece>* schemas);') | |
177 c.Eblock('};'); | |
178 c.Append() | |
179 c.Concat(self._cpp_type_generator.GetRootNamespaceEnd()) | |
180 c.Append() | |
181 return self.GenerateHeader('generated_schemas', c) | |
182 | |
183 def GenerateSchemasCC(self): | |
184 """Generates a code.Code object for the generated schemas .cc file""" | |
185 c = code.Code() | |
186 c.Append(cpp_util.CHROMIUM_LICENSE) | |
187 c.Append() | |
188 c.Append('#include "%s"' % (os.path.join(SOURCE_BASE_PATH, | |
189 'generated_schemas.h'))) | |
190 c.Append() | |
191 c.Concat(self._cpp_type_generator.GetRootNamespaceStart()) | |
192 c.Append() | |
193 c.Append('// static') | |
194 c.Sblock('void GeneratedSchemas::Get(' | |
195 'std::map<std::string, base::StringPiece>* schemas) {') | |
196 for api in self._api_defs: | |
197 namespace = self._model.namespaces[api.get('namespace')] | |
198 # JSON parsing code expects lists of schemas, so dump a singleton list. | |
199 json_content = json.dumps([_RemoveDescriptions(api)], | |
200 separators=(',', ':')) | |
201 # Escape all double-quotes and backslashes. For this to output a valid | |
202 # JSON C string, we need to escape \ and ". | |
203 json_content = json_content.replace('\\', '\\\\').replace('"', '\\"') | |
204 c.Append('(*schemas)["%s"] = "%s";' % (namespace.name, json_content)) | |
205 c.Eblock('}') | |
206 c.Append() | |
207 c.Concat(self._cpp_type_generator.GetRootNamespaceEnd()) | |
208 c.Append() | |
209 return c | |
OLD | NEW |