Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(468)

Side by Side Diff: headless/lib/browser/devtools_api/client_api_generator.py

Issue 2473073003: [headless] Refactor headless devtools client API. (Closed)
Patch Set: Address nits Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 # Copyright 2016 The Chromium Authors. All rights reserved. 1 # Copyright 2016 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 argparse 5 import argparse
6 import collections
6 import os.path 7 import os.path
8 import re
7 import sys 9 import sys
8 import re
9 try: 10 try:
10 import json 11 import json
11 except ImportError: 12 except ImportError:
12 import simplejson as json 13 import simplejson as json
13 14
14 # Path handling for libraries and templates 15 # Path handling for libraries and templates
15 # Paths have to be normalized because Jinja uses the exact template path to 16 # Paths have to be normalized because Jinja uses the exact template path to
16 # determine the hash used in the cache filename, and we need a pre-caching step 17 # determine the hash used in the cache filename, and we need a pre-caching step
17 # to be concurrency-safe. Use absolute path because __file__ is absolute if 18 # to be concurrency-safe. Use absolute path because __file__ is absolute if
18 # module is imported, and relative if executed directly. 19 # module is imported, and relative if executed directly.
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
54 def CamelCaseToHackerStyle(name): 55 def CamelCaseToHackerStyle(name):
55 # Do two passes to insert '_' chars to deal with overlapping matches (e.g., 56 # Do two passes to insert '_' chars to deal with overlapping matches (e.g.,
56 # 'LoLoLoL'). 57 # 'LoLoLoL').
57 name = re.sub(r'([^_])([A-Z][a-z]+?)', r'\1_\2', name) 58 name = re.sub(r'([^_])([A-Z][a-z]+?)', r'\1_\2', name)
58 name = re.sub(r'([^_])([A-Z][a-z]+?)', r'\1_\2', name) 59 name = re.sub(r'([^_])([A-Z][a-z]+?)', r'\1_\2', name)
59 return name.lower() 60 return name.lower()
60 61
61 62
62 def SanitizeLiteral(literal): 63 def SanitizeLiteral(literal):
63 return { 64 return {
64 # Rename null enumeration values to avoid a clash with the NULL macro. 65 # Rename null enumeration values to avoid a clash with the NULL macro.
65 'null': 'none', 66 'null': 'none',
66 # Rename mathematical constants to avoid colliding with C macros. 67 # Rename mathematical constants to avoid colliding with C macros.
67 'Infinity': 'InfinityValue', 68 'Infinity': 'InfinityValue',
68 '-Infinity': 'NegativeInfinityValue', 69 '-Infinity': 'NegativeInfinityValue',
69 'NaN': 'NaNValue', 70 'NaN': 'NaNValue',
70 # Turn negative zero into a safe identifier. 71 # Turn negative zero into a safe identifier.
71 '-0': 'NegativeZeroValue', 72 '-0': 'NegativeZeroValue',
72 }.get(literal, literal) 73 }.get(literal, literal)
73 74
74 75
75 def InitializeJinjaEnv(cache_dir): 76 def InitializeJinjaEnv(cache_dir):
76 jinja_env = jinja2.Environment( 77 jinja_env = jinja2.Environment(
77 loader=jinja2.FileSystemLoader(templates_dir), 78 loader=jinja2.FileSystemLoader(templates_dir),
78 # Bytecode cache is not concurrency-safe unless pre-cached: 79 # Bytecode cache is not concurrency-safe unless pre-cached:
79 # if pre-cached this is read-only, but writing creates a race condition. 80 # if pre-cached this is read-only, but writing creates a race condition.
80 bytecode_cache=jinja2.FileSystemBytecodeCache(cache_dir), 81 bytecode_cache=jinja2.FileSystemBytecodeCache(cache_dir),
81 keep_trailing_newline=True, # Newline-terminate generated files. 82 keep_trailing_newline=True, # Newline-terminate generated files.
82 lstrip_blocks=True, # So we can indent control flow tags. 83 lstrip_blocks=True, # So we can indent control flow tags.
83 trim_blocks=True) 84 trim_blocks=True)
84 jinja_env.filters.update({ 85 jinja_env.filters.update({
85 'to_title_case': ToTitleCase, 86 'to_title_case': ToTitleCase,
86 'dash_to_camelcase': DashToCamelCase, 87 'dash_to_camelcase': DashToCamelCase,
87 'camelcase_to_hacker_style': CamelCaseToHackerStyle, 88 'camelcase_to_hacker_style': CamelCaseToHackerStyle,
88 'sanitize_literal': SanitizeLiteral, 89 'sanitize_literal': SanitizeLiteral,
89 }) 90 })
90 jinja_env.add_extension('jinja2.ext.loopcontrols') 91 jinja_env.add_extension('jinja2.ext.loopcontrols')
91 return jinja_env 92 return jinja_env
92 93
93 94
94 def PatchFullQualifiedRefs(json_api): 95 def PatchFullQualifiedRefs(json_api):
95 def PatchFullQualifiedRefsInDomain(json, domain_name): 96 def PatchFullQualifiedRefsInDomain(json, domain_name):
96 if isinstance(json, list): 97 if isinstance(json, list):
97 for item in json: 98 for item in json:
98 PatchFullQualifiedRefsInDomain(item, domain_name) 99 PatchFullQualifiedRefsInDomain(item, domain_name)
99 100
100 if not isinstance(json, dict): 101 if not isinstance(json, dict):
101 return 102 return
102 for key in json: 103 for key in json:
103 if key != '$ref': 104 if key != '$ref':
104 PatchFullQualifiedRefsInDomain(json[key], domain_name) 105 PatchFullQualifiedRefsInDomain(json[key], domain_name)
105 continue 106 continue
106 if not '.' in json['$ref']: 107 if not '.' in json['$ref']:
107 json['$ref'] = domain_name + '.' + json['$ref'] 108 json['$ref'] = domain_name + '.' + json['$ref']
108 109
109 for domain in json_api['domains']: 110 for domain in json_api['domains']:
110 PatchFullQualifiedRefsInDomain(domain, domain['domain']) 111 PatchFullQualifiedRefsInDomain(domain, domain['domain'])
111 112
112 113
113 def CreateUserTypeDefinition(domain, type): 114 def CreateUserTypeDefinition(domain, type):
114 namespace = CamelCaseToHackerStyle(domain['domain']) 115 namespace = CamelCaseToHackerStyle(domain['domain'])
115 return { 116 return {
116 'return_type': 'std::unique_ptr<headless::%s::%s>' % ( 117 'return_type': 'std::unique_ptr<headless::%s::%s>' % (
117 namespace, type['id']), 118 namespace, type['id']),
118 'pass_type': 'std::unique_ptr<headless::%s::%s>' % (namespace, type['id']), 119 'pass_type': 'std::unique_ptr<headless::%s::%s>' % (
119 'to_raw_type': '*%s', 120 namespace, type['id']),
120 'to_raw_return_type': '%s.get()', 121 'to_raw_type': '*%s',
121 'to_pass_type': 'std::move(%s)', 122 'to_raw_return_type': '%s.get()',
122 'type': 'std::unique_ptr<headless::%s::%s>' % (namespace, type['id']), 123 'to_pass_type': 'std::move(%s)',
123 'raw_type': 'headless::%s::%s' % (namespace, type['id']), 124 'type': 'std::unique_ptr<headless::%s::%s>' % (namespace, type['id']),
124 'raw_pass_type': 'headless::%s::%s*' % (namespace, type['id']), 125 'raw_type': 'headless::%s::%s' % (namespace, type['id']),
125 'raw_return_type': 'const headless::%s::%s*' % (namespace, type['id']), 126 'raw_pass_type': 'headless::%s::%s*' % (namespace, type['id']),
127 'raw_return_type': 'const headless::%s::%s*' % (namespace, type['id']),
126 } 128 }
127 129
128 130
129 def CreateEnumTypeDefinition(domain_name, type): 131 def CreateEnumTypeDefinition(domain_name, type):
130 namespace = CamelCaseToHackerStyle(domain_name) 132 namespace = CamelCaseToHackerStyle(domain_name)
131 return { 133 return {
132 'return_type': 'headless::%s::%s' % (namespace, type['id']), 134 'return_type': 'headless::%s::%s' % (namespace, type['id']),
133 'pass_type': 'headless::%s::%s' % (namespace, type['id']), 135 'pass_type': 'headless::%s::%s' % (namespace, type['id']),
134 'to_raw_type': '%s', 136 'to_raw_type': '%s',
135 'to_raw_return_type': '%s', 137 'to_raw_return_type': '%s',
136 'to_pass_type': '%s', 138 'to_pass_type': '%s',
137 'type': 'headless::%s::%s' % (namespace, type['id']), 139 'type': 'headless::%s::%s' % (namespace, type['id']),
138 'raw_type': 'headless::%s::%s' % (namespace, type['id']), 140 'raw_type': 'headless::%s::%s' % (namespace, type['id']),
139 'raw_pass_type': 'headless::%s::%s' % (namespace, type['id']), 141 'raw_pass_type': 'headless::%s::%s' % (namespace, type['id']),
140 'raw_return_type': 'headless::%s::%s' % (namespace, type['id']), 142 'raw_return_type': 'headless::%s::%s' % (namespace, type['id']),
141 } 143 }
142 144
143 145
144 def CreateObjectTypeDefinition(): 146 def CreateObjectTypeDefinition():
145 return { 147 return {
146 'return_type': 'std::unique_ptr<base::DictionaryValue>', 148 'return_type': 'std::unique_ptr<base::DictionaryValue>',
147 'pass_type': 'std::unique_ptr<base::DictionaryValue>', 149 'pass_type': 'std::unique_ptr<base::DictionaryValue>',
148 'to_raw_type': '*%s', 150 'to_raw_type': '*%s',
149 'to_raw_return_type': '%s.get()', 151 'to_raw_return_type': '%s.get()',
150 'to_pass_type': 'std::move(%s)', 152 'to_pass_type': 'std::move(%s)',
151 'type': 'std::unique_ptr<base::DictionaryValue>', 153 'type': 'std::unique_ptr<base::DictionaryValue>',
152 'raw_type': 'base::DictionaryValue', 154 'raw_type': 'base::DictionaryValue',
153 'raw_pass_type': 'base::DictionaryValue*', 155 'raw_pass_type': 'base::DictionaryValue*',
154 'raw_return_type': 'const base::DictionaryValue*', 156 'raw_return_type': 'const base::DictionaryValue*',
155 } 157 }
156 158
157 159
158 def WrapObjectTypeDefinition(type): 160 def WrapObjectTypeDefinition(type):
159 id = type.get('id', 'base::Value') 161 id = type.get('id', 'base::Value')
160 return { 162 return {
161 'return_type': 'std::unique_ptr<%s>' % id, 163 'return_type': 'std::unique_ptr<%s>' % id,
162 'pass_type': 'std::unique_ptr<%s>' % id, 164 'pass_type': 'std::unique_ptr<%s>' % id,
163 'to_raw_type': '*%s', 165 'to_raw_type': '*%s',
164 'to_raw_return_type': '%s.get()', 166 'to_raw_return_type': '%s.get()',
165 'to_pass_type': 'std::move(%s)', 167 'to_pass_type': 'std::move(%s)',
166 'type': 'std::unique_ptr<%s>' % id, 168 'type': 'std::unique_ptr<%s>' % id,
167 'raw_type': id, 169 'raw_type': id,
168 'raw_pass_type': '%s*' % id, 170 'raw_pass_type': '%s*' % id,
169 'raw_return_type': 'const %s*' % id, 171 'raw_return_type': 'const %s*' % id,
170 } 172 }
171 173
172 174
173 def CreateAnyTypeDefinition(): 175 def CreateAnyTypeDefinition():
174 return { 176 return {
175 'return_type': 'std::unique_ptr<base::Value>', 177 'return_type': 'std::unique_ptr<base::Value>',
176 'pass_type': 'std::unique_ptr<base::Value>', 178 'pass_type': 'std::unique_ptr<base::Value>',
177 'to_raw_type': '*%s', 179 'to_raw_type': '*%s',
178 'to_raw_return_type': '%s.get()', 180 'to_raw_return_type': '%s.get()',
179 'to_pass_type': 'std::move(%s)', 181 'to_pass_type': 'std::move(%s)',
180 'type': 'std::unique_ptr<base::Value>', 182 'type': 'std::unique_ptr<base::Value>',
181 'raw_type': 'base::Value', 183 'raw_type': 'base::Value',
182 'raw_pass_type': 'base::Value*', 184 'raw_pass_type': 'base::Value*',
183 'raw_return_type': 'const base::Value*', 185 'raw_return_type': 'const base::Value*',
184 } 186 }
185 187
186 188
187 def CreateStringTypeDefinition(domain): 189 def CreateStringTypeDefinition(domain):
188 return { 190 return {
189 'return_type': 'std::string', 191 'return_type': 'std::string',
190 'pass_type': 'const std::string&', 192 'pass_type': 'const std::string&',
191 'to_pass_type': '%s', 193 'to_pass_type': '%s',
192 'to_raw_type': '%s', 194 'to_raw_type': '%s',
193 'to_raw_return_type': '%s', 195 'to_raw_return_type': '%s',
194 'type': 'std::string', 196 'type': 'std::string',
195 'raw_type': 'std::string', 197 'raw_type': 'std::string',
196 'raw_pass_type': 'const std::string&', 198 'raw_pass_type': 'const std::string&',
197 'raw_return_type': 'std::string', 199 'raw_return_type': 'std::string',
198 } 200 }
199 201
200 202
201 def CreatePrimitiveTypeDefinition(type): 203 def CreatePrimitiveTypeDefinition(type):
202 typedefs = { 204 typedefs = {
203 'number': 'double', 205 'number': 'double',
204 'integer': 'int', 206 'integer': 'int',
205 'boolean': 'bool', 207 'boolean': 'bool',
206 'string': 'std::string', 208 'string': 'std::string',
207 } 209 }
208 return { 210 return {
209 'return_type': typedefs[type], 211 'return_type': typedefs[type],
210 'pass_type': typedefs[type], 212 'pass_type': typedefs[type],
211 'to_pass_type': '%s', 213 'to_pass_type': '%s',
212 'to_raw_type': '%s', 214 'to_raw_type': '%s',
213 'to_raw_return_type': '%s', 215 'to_raw_return_type': '%s',
214 'type': typedefs[type], 216 'type': typedefs[type],
215 'raw_type': typedefs[type], 217 'raw_type': typedefs[type],
216 'raw_pass_type': typedefs[type], 218 'raw_pass_type': typedefs[type],
217 'raw_return_type': typedefs[type], 219 'raw_return_type': typedefs[type],
218 } 220 }
219 221
220 222
221 type_definitions = {} 223 type_definitions = {}
222 type_definitions['number'] = CreatePrimitiveTypeDefinition('number') 224 type_definitions['number'] = CreatePrimitiveTypeDefinition('number')
223 type_definitions['integer'] = CreatePrimitiveTypeDefinition('integer') 225 type_definitions['integer'] = CreatePrimitiveTypeDefinition('integer')
224 type_definitions['boolean'] = CreatePrimitiveTypeDefinition('boolean') 226 type_definitions['boolean'] = CreatePrimitiveTypeDefinition('boolean')
225 type_definitions['string'] = CreatePrimitiveTypeDefinition('string') 227 type_definitions['string'] = CreatePrimitiveTypeDefinition('string')
226 type_definitions['object'] = CreateObjectTypeDefinition() 228 type_definitions['object'] = CreateObjectTypeDefinition()
227 type_definitions['any'] = CreateAnyTypeDefinition() 229 type_definitions['any'] = CreateAnyTypeDefinition()
228 230
229 231
230 def WrapArrayDefinition(type): 232 def WrapArrayDefinition(type):
231 return { 233 return {
232 'return_type': 'std::vector<%s>' % type['type'], 234 'return_type': 'std::vector<%s>' % type['type'],
233 'pass_type': 'std::vector<%s>' % type['type'], 235 'pass_type': 'std::vector<%s>' % type['type'],
234 'to_raw_type': '%s', 236 'to_raw_type': '%s',
235 'to_raw_return_type': '&%s', 237 'to_raw_return_type': '&%s',
236 'to_pass_type': 'std::move(%s)', 238 'to_pass_type': 'std::move(%s)',
237 'type': 'std::vector<%s>' % type['type'], 239 'type': 'std::vector<%s>' % type['type'],
238 'raw_type': 'std::vector<%s>' % type['type'], 240 'raw_type': 'std::vector<%s>' % type['type'],
239 'raw_pass_type': 'std::vector<%s>*' % type['type'], 241 'raw_pass_type': 'std::vector<%s>*' % type['type'],
240 'raw_return_type': 'const std::vector<%s>*' % type['type'], 242 'raw_return_type': 'const std::vector<%s>*' % type['type'],
241 } 243 }
242 244
243 245
244 def CreateTypeDefinitions(json_api): 246 def CreateTypeDefinitions(json_api):
245 for domain in json_api['domains']: 247 for domain in json_api['domains']:
246 if not ('types' in domain): 248 if not ('types' in domain):
247 continue 249 continue
248 for type in domain['types']: 250 for type in domain['types']:
249 if type['type'] == 'object': 251 if type['type'] == 'object':
250 if 'properties' in type: 252 if 'properties' in type:
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
294 def SynthesizeEnumType(domain, owner, type): 296 def SynthesizeEnumType(domain, owner, type):
295 type['id'] = ToTitleCase(owner) + ToTitleCase(type['name']) 297 type['id'] = ToTitleCase(owner) + ToTitleCase(type['name'])
296 type_definitions[domain['domain'] + '.' + type['id']] = ( 298 type_definitions[domain['domain'] + '.' + type['id']] = (
297 CreateEnumTypeDefinition(domain['domain'], type)) 299 CreateEnumTypeDefinition(domain['domain'], type))
298 type['$ref'] = domain['domain'] + '.' + type['id'] 300 type['$ref'] = domain['domain'] + '.' + type['id']
299 domain['types'].append(type) 301 domain['types'].append(type)
300 302
301 303
302 def SynthesizeCommandTypes(json_api): 304 def SynthesizeCommandTypes(json_api):
303 """Generate types for command parameters, return values and enum 305 """Generate types for command parameters, return values and enum
304 properties.""" 306 properties.
307 """
305 for domain in json_api['domains']: 308 for domain in json_api['domains']:
306 if not 'types' in domain: 309 if not 'types' in domain:
307 domain['types'] = [] 310 domain['types'] = []
308 for type in domain['types']: 311 for type in domain['types']:
309 if type['type'] == 'object': 312 if type['type'] == 'object':
310 for property in type.get('properties', []): 313 for property in type.get('properties', []):
311 if 'enum' in property and not '$ref' in property: 314 if 'enum' in property and not '$ref' in property:
312 SynthesizeEnumType(domain, type['id'], property) 315 SynthesizeEnumType(domain, type['id'], property)
313 316
314 for command in domain.get('commands', []): 317 for command in domain.get('commands', []):
315 if 'parameters' in command: 318 if 'parameters' in command:
316 for parameter in command['parameters']: 319 for parameter in command['parameters']:
317 if 'enum' in parameter and not '$ref' in parameter: 320 if 'enum' in parameter and not '$ref' in parameter:
318 SynthesizeEnumType(domain, command['name'], parameter) 321 SynthesizeEnumType(domain, command['name'], parameter)
319 parameters_type = { 322 parameters_type = {
320 'id': ToTitleCase(command['name']) + 'Params', 323 'id': ToTitleCase(command['name']) + 'Params',
321 'type': 'object', 324 'type': 'object',
322 'description': 'Parameters for the %s command.' % ToTitleCase( 325 'description': 'Parameters for the %s command.' % ToTitleCase(
323 command['name']), 326 command['name']),
324 'properties': command['parameters'] 327 'properties': command['parameters']
325 } 328 }
326 domain['types'].append(parameters_type) 329 domain['types'].append(parameters_type)
327 if 'returns' in command: 330 if 'returns' in command:
328 for parameter in command['returns']: 331 for parameter in command['returns']:
329 if 'enum' in parameter and not '$ref' in parameter: 332 if 'enum' in parameter and not '$ref' in parameter:
330 SynthesizeEnumType(domain, command['name'], parameter) 333 SynthesizeEnumType(domain, command['name'], parameter)
331 result_type = { 334 result_type = {
332 'id': ToTitleCase(command['name']) + 'Result', 335 'id': ToTitleCase(command['name']) + 'Result',
333 'type': 'object', 336 'type': 'object',
334 'description': 'Result for the %s command.' % ToTitleCase( 337 'description': 'Result for the %s command.' % ToTitleCase(
335 command['name']), 338 command['name']),
336 'properties': command['returns'] 339 'properties': command['returns']
337 } 340 }
338 domain['types'].append(result_type) 341 domain['types'].append(result_type)
339 342
340 343
341 def SynthesizeEventTypes(json_api): 344 def SynthesizeEventTypes(json_api):
342 """Generate types for events and their properties. 345 """Generate types for events and their properties.
343 346
344 Note that parameter objects are also created for events without parameters to 347 Note that parameter objects are also created for events without parameters to
345 make it easier to introduce parameters later. 348 make it easier to introduce parameters later.
346 """ 349 """
347 for domain in json_api['domains']: 350 for domain in json_api['domains']:
348 if not 'types' in domain: 351 if not 'types' in domain:
349 domain['types'] = [] 352 domain['types'] = []
350 for event in domain.get('events', []): 353 for event in domain.get('events', []):
351 for parameter in event.get('parameters', []): 354 for parameter in event.get('parameters', []):
352 if 'enum' in parameter and not '$ref' in parameter: 355 if 'enum' in parameter and not '$ref' in parameter:
353 SynthesizeEnumType(domain, event['name'], parameter) 356 SynthesizeEnumType(domain, event['name'], parameter)
354 event_type = { 357 event_type = {
355 'id': ToTitleCase(event['name']) + 'Params', 358 'id': ToTitleCase(event['name']) + 'Params',
356 'type': 'object', 359 'type': 'object',
357 'description': 'Parameters for the %s event.' % ToTitleCase( 360 'description': 'Parameters for the %s event.' % ToTitleCase(
358 event['name']), 361 event['name']),
359 'properties': event.get('parameters', []) 362 'properties': event.get('parameters', [])
360 } 363 }
361 domain['types'].append(event_type) 364 domain['types'].append(event_type)
362 365
363 366
367 def InitializeDomainDependencies(json_api):
368 """For each domain create list of domains given domain depends on,
369 including itself."""
370
371 direct_deps = collections.defaultdict(set)
372
373 def GetDomainDepsFromRefs(domain_name, json):
374 if isinstance(json, list):
375 for value in json:
376 GetDomainDepsFromRefs(domain_name, value)
377 return
378
379 if not isinstance(json, dict):
380 return
381 for value in json.itervalues():
382 GetDomainDepsFromRefs(domain_name, value)
383
384 if '$ref' in json:
385 if '.' in json['$ref']:
386 dep = json['$ref'].split('.')[0]
387 direct_deps[domain_name].add(dep)
388
389 for domain in json_api['domains']:
390 direct_deps[domain['domain']] = set(domain.get('dependencies', []))
391 GetDomainDepsFromRefs(domain['domain'], domain)
392
393 def TraverseDependencies(domain, deps):
394 if domain in deps:
395 return
396 deps.add(domain)
397
398 for dep in direct_deps[domain]:
399 TraverseDependencies(dep, deps)
400
401 for domain in json_api['domains']:
402 domain_deps = set()
403 TraverseDependencies(domain['domain'], domain_deps)
404 domain['dependencies'] = sorted(domain_deps)
405
406
364 def PatchExperimentalCommandsAndEvents(json_api): 407 def PatchExperimentalCommandsAndEvents(json_api):
365 """ 408 """Mark all commands and events in experimental domains as experimental
366 Mark all commands and events in experimental domains as experimental
367 and make sure experimental commands have at least empty parameters 409 and make sure experimental commands have at least empty parameters
368 and return values. 410 and return values.
369 """ 411 """
370 for domain in json_api['domains']: 412 for domain in json_api['domains']:
371 if domain.get('experimental', False): 413 if domain.get('experimental', False):
372 for command in domain.get('commands', []): 414 for command in domain.get('commands', []):
373 command['experimental'] = True 415 command['experimental'] = True
374 for event in domain.get('events', []): 416 for event in domain.get('events', []):
375 event['experimental'] = True 417 event['experimental'] = True
376 418
377 419
420 def EnsureDirectoryExists(path):
421 if not os.path.exists(path):
422 os.makedirs(path)
423
424
378 def EnsureCommandsHaveParametersAndReturnTypes(json_api): 425 def EnsureCommandsHaveParametersAndReturnTypes(json_api):
379 """ 426 """Make sure all commands have at least empty parameters and return values.
380 Make sure all commands have at least empty parameters and return values. This 427 This guarantees API compatibility if a previously experimental command is made
381 guarantees API compatibility if a previously experimental command is made
382 stable. 428 stable.
383 """ 429 """
384 for domain in json_api['domains']: 430 for domain in json_api['domains']:
385 for command in domain.get('commands', []): 431 for command in domain.get('commands', []):
386 if not 'parameters' in command: 432 if not 'parameters' in command:
387 command['parameters'] = [] 433 command['parameters'] = []
388 if not 'returns' in command: 434 if not 'returns' in command:
389 command['returns'] = [] 435 command['returns'] = []
390 for event in domain.get('events', []): 436 for event in domain.get('events', []):
391 if not 'parameters' in event: 437 if not 'parameters' in event:
392 event['parameters'] = [] 438 event['parameters'] = []
393 439
394 440
395 def Generate(jinja_env, output_dirname, json_api, class_name, file_types): 441 def Generate(jinja_env, output_dirname, json_api,
442 class_name, file_types, file_name=None):
443 if file_name is None:
444 file_name = class_name
445 EnsureDirectoryExists(output_dirname)
396 template_context = { 446 template_context = {
397 'api': json_api, 447 'api': json_api,
398 'join_arrays': JoinArrays, 448 'join_arrays': JoinArrays,
399 'resolve_type': ResolveType, 449 'resolve_type': ResolveType,
400 'type_definition': TypeDefinition, 450 'type_definition': TypeDefinition,
401 } 451 }
402 for file_type in file_types: 452 for file_type in file_types:
403 template = jinja_env.get_template('/%s_%s.template' % ( 453 template = jinja_env.get_template('/%s_%s.template' % (
404 class_name, file_type)) 454 class_name, file_type))
405 output_file = '%s/%s.%s' % (output_dirname, class_name, file_type) 455 output_file = '%s/%s.%s' % (output_dirname, file_name, file_type)
406 with open(output_file, 'w') as f: 456 with open(output_file, 'w') as f:
407 f.write(template.render(template_context)) 457 f.write(template.render(template_context))
408 458
409 459
410 def GenerateDomains(jinja_env, output_dirname, json_api, class_name, 460 def GeneratePerDomain(jinja_env, output_dirname, json_api, class_name,
411 file_types): 461 file_types, domain_name_to_file_name_func):
462 EnsureDirectoryExists(output_dirname)
412 for file_type in file_types: 463 for file_type in file_types:
413 template = jinja_env.get_template('/%s_%s.template' % ( 464 template = jinja_env.get_template('/%s_%s.template' % (
414 class_name, file_type)) 465 class_name, file_type))
415 for domain in json_api['domains']: 466 for domain in json_api['domains']:
416 template_context = { 467 template_context = {
417 'domain': domain, 468 'domain': domain,
418 'resolve_type': ResolveType, 469 'resolve_type': ResolveType,
419 } 470 }
420 domain_name = CamelCaseToHackerStyle(domain['domain']) 471 domain_name = CamelCaseToHackerStyle(domain['domain'])
421 output_file = '%s/%s.%s' % (output_dirname, domain_name, file_type) 472 output_file = '%s/%s.%s' % (output_dirname,
473 domain_name_to_file_name_func(domain_name),
474 file_type)
422 with open(output_file, 'w') as f: 475 with open(output_file, 'w') as f:
423 f.write(template.render(template_context)) 476 f.write(template.render(template_context))
424 477
425 478
479 def GenerateDomains(jinja_env, output_dirname, json_api):
480 GeneratePerDomain(
481 jinja_env, os.path.join(output_dirname, 'devtools', 'domains'), json_api,
482 'domain', ['cc', 'h'],
483 lambda domain_name: domain_name)
484
485 # TODO(altimin): Remove this in 2017.
486 # Generate DOMAIN.h in the old directory for backwards compatibility.
487 GeneratePerDomain(
488 jinja_env, os.path.join(output_dirname, 'domains'), json_api,
489 'deprecated_domain', ['h'], lambda domain_name: domain_name)
490
491
492 def GenerateTypes(jinja_env, output_dirname, json_api):
493 # Generate forward declarations for types.
494 GeneratePerDomain(
495 jinja_env, os.path.join(output_dirname, 'devtools', 'internal'),
496 json_api, 'domain_types_forward_declaration', ['h'],
497 lambda domain_name: 'types_forward_declaration_%s' % (domain_name, ))
498 # Generate types on per-domain basis.
499 GeneratePerDomain(
500 jinja_env, os.path.join(output_dirname, 'devtools', 'domains'),
501 json_api, 'domain_types', ['h', 'cc'],
502 lambda domain_name: 'types_%s' % (domain_name, ))
503
504 # TODO(altimin): Remove this in 2017.
505 # Generate types.h for backwards compatibility.
506 Generate(jinja_env, os.path.join(output_dirname, 'domains'), json_api,
507 'deprecated_types', ['h'], 'types')
508
509
510 def GenerateTypeConversions(jinja_env, output_dirname, json_api):
511 # Generate type conversions on per-domain basis.
512 GeneratePerDomain(
513 jinja_env, os.path.join(output_dirname, 'devtools', 'internal'),
514 json_api, 'domain_type_conversions', ['h'],
515 lambda domain_name: 'type_conversions_%s' % (domain_name, ))
516
517 # TODO(altimin): Remove this in 2017.
518 # Generate type_conversions.h for backwards compatibility.
519 Generate(jinja_env, os.path.join(output_dirname, 'domains'), json_api,
520 'deprecated_type_conversions', ['h'], 'type_conversions')
521
522
426 if __name__ == '__main__': 523 if __name__ == '__main__':
427 json_api, output_dirname = ParseArguments(sys.argv[1:]) 524 json_api, output_dirname = ParseArguments(sys.argv[1:])
428 jinja_env = InitializeJinjaEnv(output_dirname) 525 jinja_env = InitializeJinjaEnv(output_dirname)
526 InitializeDomainDependencies(json_api)
429 PatchExperimentalCommandsAndEvents(json_api) 527 PatchExperimentalCommandsAndEvents(json_api)
430 EnsureCommandsHaveParametersAndReturnTypes(json_api) 528 EnsureCommandsHaveParametersAndReturnTypes(json_api)
431 SynthesizeCommandTypes(json_api) 529 SynthesizeCommandTypes(json_api)
432 SynthesizeEventTypes(json_api) 530 SynthesizeEventTypes(json_api)
433 PatchFullQualifiedRefs(json_api) 531 PatchFullQualifiedRefs(json_api)
434 CreateTypeDefinitions(json_api) 532 CreateTypeDefinitions(json_api)
435 Generate(jinja_env, output_dirname, json_api, 'types', ['cc', 'h']) 533 GenerateDomains(jinja_env, output_dirname, json_api)
436 Generate(jinja_env, output_dirname, json_api, 'type_conversions', ['h']) 534 GenerateTypes(jinja_env, output_dirname, json_api)
437 GenerateDomains(jinja_env, output_dirname, json_api, 'domain', ['cc', 'h']) 535 GenerateTypeConversions(jinja_env, output_dirname, json_api)
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698