]>
Commit | Line | Data |
---|---|---|
81345200 A |
1 | #!/usr/bin/env python |
2 | # Copyright (c) 2014 Apple Inc. All rights reserved. | |
3 | # | |
4 | # Redistribution and use in source and binary forms, with or without | |
5 | # modification, are permitted provided that the following conditions are | |
6 | # met: | |
7 | # | |
8 | # * Redistributions of source code must retain the above copyright | |
9 | # notice, this list of conditions and the following disclaimer. | |
10 | # * Redistributions in binary form must reproduce the above | |
11 | # copyright notice, this list of conditions and the following disclaimer | |
12 | # in the documentation and/or other materials provided with the | |
13 | # distribution. | |
14 | # | |
15 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
16 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
17 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
18 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
19 | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
20 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
21 | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
22 | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
23 | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
24 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
25 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
26 | ||
27 | import os.path | |
28 | import re | |
29 | import sys | |
30 | import string | |
31 | from string import Template | |
32 | import optparse | |
33 | import logging | |
34 | from CodeGeneratorReplayInputsTemplates import Templates | |
35 | ||
36 | try: | |
37 | import json | |
38 | except ImportError: | |
39 | import simplejson as json | |
40 | ||
41 | # Configuration values are first looked up in the framework configuration, | |
42 | # and then in the global configuration if there is no framework-specific value. | |
43 | GLOBAL_CONFIG = { | |
44 | "baseFilename": "ReplayInputs", | |
45 | "guardCondition": "ENABLE(WEB_REPLAY)", | |
46 | "traitsFrameworkName": "JavaScriptCore", | |
47 | ||
48 | # These are formatted as ([allowed_frameworks], (framework, header_path)). | |
49 | # The generator can figure out how to format the includes. | |
50 | "headerIncludes": [ | |
51 | (["WebCore"], | |
52 | ("WebCore", "replay/EventLoopInput.h") | |
53 | ), | |
54 | (["JavaScriptCore", "WebCore"], | |
55 | ("JavaScriptCore", "replay/EncodedValue.h") | |
56 | ), | |
57 | (["JavaScriptCore"], | |
58 | ("JavaScriptCore", "replay/NondeterministicInput.h") | |
59 | ), | |
ed1e77d3 A |
60 | (["JavaScriptCore", "WebCore"], |
61 | ("WTF", "wtf/TypeCasts.h") | |
62 | ), | |
81345200 A |
63 | (["WebCore"], |
64 | ("WTF", "wtf/text/WTFString.h") | |
65 | ), | |
66 | ||
67 | # Testing fixtures. | |
68 | (["Test"], | |
69 | ("WebCore", "platform/ExternalNamespaceHeaderIncludeDummy.h") | |
70 | ), | |
71 | (["Test"], | |
72 | ("Test", "platform/InternalNamespaceHeaderIncludeDummy.h") | |
73 | ) | |
74 | ], | |
75 | ||
76 | "implIncludes": [ | |
81345200 A |
77 | (["WebCore"], |
78 | ("WebCore", "replay/SerializationMethods.h") | |
79 | ), | |
80 | (["WebCore", "JavaScriptCore"], | |
81 | ("JavaScriptCore", "inspector/InspectorValues.h") | |
82 | ), | |
ed1e77d3 | 83 | (["WebCore", "JavaScriptCore"], |
81345200 A |
84 | ("WTF", "wtf/NeverDestroyed.h") |
85 | ), | |
81345200 A |
86 | |
87 | # Testing fixtures. | |
88 | (["Test"], | |
89 | ("WebCore", "platform/ExternalNamespaceImplIncludeDummy.h") | |
90 | ), | |
91 | (["Test"], | |
92 | ("Test", "platform/InternalNamespaceImplIncludeDummy.h") | |
93 | ) | |
94 | ], | |
95 | } | |
96 | ||
97 | FRAMEWORK_CONFIG_MAP = { | |
98 | "Global": { | |
99 | "prefix": "", | |
100 | "namespace": "" | |
101 | }, | |
102 | ||
103 | "WTF": { | |
104 | "prefix": "WTF", | |
105 | "namespace": "WTF", | |
106 | }, | |
107 | "JavaScriptCore": { | |
108 | "prefix": "JS", | |
109 | "namespace": "JSC", | |
110 | "exportMacro": "JS_EXPORT_PRIVATE", | |
81345200 A |
111 | }, |
112 | "WebCore": { | |
113 | "prefix": "Web", | |
114 | "namespace": "WebCore", | |
ed1e77d3 | 115 | "exportMacro": "WEBCORE_EXPORT" |
81345200 A |
116 | }, |
117 | # Used for bindings tests. | |
118 | "Test": { | |
119 | "prefix": "Test", | |
120 | "namespace": "Test", | |
ed1e77d3 | 121 | "exportMacro": "TEST_EXPORT_MACRO" |
81345200 A |
122 | } |
123 | } | |
124 | ||
125 | # These settings are specific to an input queue. | |
126 | QUEUE_CONFIG_MAP = { | |
127 | "SCRIPT_MEMOIZED": { | |
128 | "enumValue": "ScriptMemoizedData", | |
129 | "baseClass": "NondeterministicInput<%s>", | |
130 | }, | |
131 | "LOADER_MEMOIZED": { | |
132 | "enumValue": "LoaderMemoizedData", | |
133 | "baseClass": "NondeterministicInput<%s>", | |
134 | }, | |
135 | "EVENT_LOOP": { | |
136 | "enumValue": "EventLoopInput", | |
137 | "baseClass": "EventLoopInput<%s>", | |
138 | }, | |
139 | } | |
140 | ||
141 | # Use a global logger, which normally only logs errors. | |
142 | # It can be configured to log debug messages from the CLI. | |
143 | logging.basicConfig(format='%(levelname)s: %(message)s', level=logging.ERROR) | |
144 | log = logging.getLogger('global') | |
145 | ||
146 | ||
147 | # Model classes, which transliterate JSON input. | |
148 | class ParseException(Exception): | |
149 | pass | |
150 | ||
151 | ||
152 | class TypecheckException(Exception): | |
153 | pass | |
154 | ||
155 | ||
156 | class Framework: | |
157 | def __init__(self, name): | |
158 | self._settings = FRAMEWORK_CONFIG_MAP[name] | |
159 | self.name = name | |
160 | ||
161 | def setting(self, key, default=''): | |
162 | return self._settings.get(key, default) | |
163 | ||
164 | @staticmethod | |
165 | def fromString(frameworkString): | |
166 | if frameworkString == "Global": | |
167 | return Frameworks.Global | |
168 | ||
169 | if frameworkString == "WTF": | |
170 | return Frameworks.WTF | |
171 | ||
172 | if frameworkString == "JavaScriptCore": | |
173 | return Frameworks.JavaScriptCore | |
174 | ||
175 | if frameworkString == "WebCore": | |
176 | return Frameworks.WebCore | |
177 | ||
178 | if frameworkString == "Test": | |
179 | return Frameworks.Test | |
180 | ||
181 | raise ParseException("Unknown framework: " + frameworkString) | |
182 | ||
183 | ||
184 | class Frameworks: | |
185 | Global = Framework("Global") | |
186 | WTF = Framework("WTF") | |
187 | JavaScriptCore = Framework("JavaScriptCore") | |
188 | WebCore = Framework("WebCore") | |
189 | Test = Framework("Test") | |
190 | ||
191 | ||
192 | class InputQueue: | |
193 | def __init__(self, settings): | |
194 | self._settings = settings | |
195 | ||
196 | def setting(self, key, default=''): | |
197 | return self._settings.get(key, default) | |
198 | ||
199 | @staticmethod | |
200 | def fromString(queueString): | |
201 | if queueString == "SCRIPT_MEMOIZED": | |
202 | return InputQueues.SCRIPT_MEMOIZED | |
203 | ||
204 | if queueString == "LOADER_MEMOIZED": | |
205 | return InputQueues.LOADER_MEMOIZED | |
206 | ||
207 | if queueString == "EVENT_LOOP": | |
208 | return InputQueues.EVENT_LOOP | |
209 | ||
210 | raise ParseException("Unknown input queue: " + queueString) | |
211 | ||
212 | ||
213 | class InputQueues: | |
214 | SCRIPT_MEMOIZED = InputQueue(QUEUE_CONFIG_MAP["SCRIPT_MEMOIZED"]) | |
215 | LOADER_MEMOIZED = InputQueue(QUEUE_CONFIG_MAP["LOADER_MEMOIZED"]) | |
216 | EVENT_LOOP = InputQueue(QUEUE_CONFIG_MAP["EVENT_LOOP"]) | |
217 | ||
218 | ||
219 | class Input: | |
ed1e77d3 | 220 | def __init__(self, name, description, framework, queueString, flags, guard=None): |
81345200 A |
221 | self.name = name |
222 | self.description = description | |
ed1e77d3 | 223 | self.framework = framework |
81345200 A |
224 | self.queue = InputQueue.fromString(queueString) |
225 | self._flags = flags | |
226 | self.guard = guard | |
227 | self.members = [] # names should be unique, but ordered. | |
228 | ||
229 | def setting(self, key, default=''): | |
230 | if key in self._flags: | |
231 | return True | |
232 | ||
233 | return self.queue.setting(key, default) | |
234 | ||
235 | ||
236 | class InputMember: | |
237 | def __init__(self, memberName, typeName, flags=[]): | |
238 | self.memberName = memberName | |
239 | self.typeName = typeName | |
240 | self._flags = flags | |
241 | ||
242 | def has_flag(self, key, default=''): | |
243 | return key in self._flags | |
244 | ||
245 | ||
246 | class TypeMode: | |
247 | def __init__(self, name): | |
248 | self._name = name | |
249 | ||
250 | @staticmethod | |
251 | def fromString(modeString): | |
252 | modeString = modeString.upper() | |
253 | if modeString == 'SCALAR': | |
254 | return TypeModes.SCALAR | |
255 | if modeString == 'HEAVY_SCALAR': | |
256 | return TypeModes.HEAVY_SCALAR | |
257 | if modeString == 'OWNED': | |
258 | return TypeModes.OWNED | |
259 | if modeString == 'SHARED': | |
260 | return TypeModes.SHARED | |
261 | if modeString == 'VECTOR': | |
262 | return TypeModes.VECTOR | |
263 | ||
264 | raise ParseException("Unknown type mode: " + modeString) | |
265 | ||
266 | ||
267 | class TypeModes: | |
268 | # Copy for assignment and for getter | |
269 | SCALAR = TypeMode("SCALAR") | |
270 | # Copy for assignment, pass by reference for getter | |
271 | HEAVY_SCALAR = TypeMode("HEAVY_SCALAR") | |
272 | # Move for assignment, pass by reference for getter | |
273 | OWNED = TypeMode("OWNED") | |
274 | # Copy a RefPtr for assignment and getter | |
275 | SHARED = TypeMode("SHARED") | |
276 | # Move operator for assignment, pass by reference for getter | |
277 | VECTOR = TypeMode("VECTOR") | |
278 | ||
279 | ||
280 | class Type: | |
281 | def __init__(self, name, mode, framework, header, enclosing_class, values, guard_values_map, underlying_storage, flags, guard=None): | |
282 | self._name = name | |
283 | self.mode = mode | |
284 | self.framework = framework | |
285 | self.header = header | |
286 | self.enclosing_class = enclosing_class | |
287 | self.values = values | |
288 | self.guard_values_map = guard_values_map | |
289 | self.underlying_storage = underlying_storage | |
290 | self._flags = flags | |
291 | self.guard = guard | |
292 | ||
293 | def __eq__(self, other): | |
294 | return self.type_name() == other.type_name() and self.mode == other.mode | |
295 | ||
296 | def __hash__(self): | |
297 | return self._name.__hash__() | |
298 | ||
299 | def has_flag(self, flagString): | |
300 | return flagString in self._flags | |
301 | ||
302 | def is_struct(self): | |
303 | return self.has_flag("STRUCT") | |
304 | ||
305 | def is_enum(self): | |
306 | return self.has_flag("ENUM") | |
307 | ||
308 | def is_enum_class(self): | |
309 | return self.has_flag("ENUM_CLASS") | |
310 | ||
311 | def declaration_kind(self): | |
312 | if self.is_enum(): | |
313 | return "enum" | |
314 | elif self.is_enum_class(): | |
315 | return "enum class" | |
316 | elif self.is_struct(): | |
317 | return "struct" | |
318 | else: | |
319 | return "class" | |
320 | ||
321 | def qualified_prefix(self): | |
322 | components = [] | |
323 | if self.framework != Frameworks.Global: | |
324 | components.append(self.framework.setting('namespace')) | |
325 | if self.enclosing_class is not None: | |
326 | components.append(self.enclosing_class) | |
327 | components.append("") | |
328 | return "::".join(components) | |
329 | ||
330 | def type_name(self, qualified=False): | |
331 | if qualified: | |
332 | return "%s%s" % (self.qualified_prefix(), self._name) | |
333 | elif self.enclosing_class is not None: | |
334 | return "%s::%s" % (self.enclosing_class, self._name) | |
335 | else: | |
336 | return self._name | |
337 | ||
338 | def storage_type(self, qualified=False): | |
339 | if self.mode == TypeModes.OWNED: | |
340 | return "std::unique_ptr<%s>" % self.type_name(qualified) | |
341 | elif self.mode == TypeModes.SHARED: | |
342 | return "RefPtr<%s>" % self.type_name(qualified) | |
343 | else: | |
344 | return self.type_name(qualified) | |
345 | ||
346 | def borrow_type(self, qualified=False): | |
347 | if self.mode == TypeModes.SCALAR: | |
348 | return self.type_name(qualified) | |
349 | elif self.mode == TypeModes.SHARED: | |
ed1e77d3 | 350 | return "RefPtr<%s>" % self.type_name(qualified) |
81345200 A |
351 | else: |
352 | return "const %s&" % self.type_name(qualified) | |
353 | ||
354 | def argument_type(self, qualified=False): | |
355 | if self.mode == TypeModes.SHARED: | |
ed1e77d3 | 356 | return "RefPtr<%s>&&" % self.type_name(qualified) |
81345200 A |
357 | else: |
358 | return self.storage_type() | |
359 | ||
ed1e77d3 A |
360 | def encoding_type_argument(self, qualified=False): |
361 | return self.type_name(qualified=qualified) | |
362 | ||
81345200 A |
363 | |
364 | def check_for_required_properties(props, obj, what): | |
365 | for prop in props: | |
366 | if prop not in obj: | |
367 | raise ParseException("When parsing %s, required property missing: %s" % (what, prop)) | |
368 | ||
369 | ||
370 | class VectorType(Type): | |
371 | def __init__(self, element_type): | |
372 | self._element_type = element_type | |
373 | self.mode = TypeModes.VECTOR | |
374 | self.framework = element_type.framework | |
375 | self.enclosing_class = None | |
376 | ||
377 | def has_flag(self): | |
378 | return False | |
379 | ||
380 | def is_struct(self): | |
381 | return False | |
382 | ||
383 | def is_enum(self): | |
384 | return False | |
385 | ||
386 | def is_enum_class(self): | |
387 | return False | |
388 | ||
389 | def qualified_prefix(self): | |
390 | return "" | |
391 | ||
392 | def type_name(self, qualified=False): | |
ed1e77d3 A |
393 | return "Vector<%s>" % self._element_type.storage_type(qualified=qualified) |
394 | ||
395 | def encoding_type_argument(self, qualified=False): | |
81345200 A |
396 | return "Vector<%s>" % self._element_type.type_name(qualified=qualified) |
397 | ||
398 | def argument_type(self, qualified=False): | |
399 | return self.type_name(qualified=qualified) + "&" | |
400 | ||
401 | ||
402 | class InputsModel: | |
ed1e77d3 | 403 | def __init__(self): |
81345200 A |
404 | self.inputs = [] |
405 | self.types = [] | |
406 | ||
407 | # Types have associated frameworks and are in their namespace, but within the specification | |
408 | # file types are in a flat namespace. Types with the same name are not allowed. | |
409 | self.types_by_name = {} | |
410 | self.inputs_by_name = {} | |
411 | ||
81345200 A |
412 | def enum_types(self): |
413 | _enums = filter(lambda x: x.is_enum() or x.is_enum_class(), self.types) | |
414 | return sorted(_enums, key=lambda _enum: _enum.type_name()) | |
415 | ||
416 | def get_type_for_member(self, member): | |
417 | if member.has_flag("VECTOR"): | |
418 | return VectorType(self.types_by_name.get(member.typeName)) | |
419 | else: | |
420 | return self.types_by_name.get(member.typeName) | |
421 | ||
ed1e77d3 A |
422 | def parse_specification(self, json): |
423 | if 'types' in json: | |
424 | if not isinstance(json['types'], dict): | |
425 | raise ParseException("Malformed specification: types is not a dict of framework->type list") | |
81345200 | 426 | |
ed1e77d3 A |
427 | for framework_name, type_list in json['types'].iteritems(): |
428 | if not isinstance(type_list, list): | |
429 | raise ParseException("Malformed specification: type list for framework %s is not a list" % framework_name) | |
81345200 | 430 | |
ed1e77d3 A |
431 | framework = Framework.fromString(framework_name) |
432 | for _type in type_list: | |
433 | self.parse_type_with_framework(_type, framework) | |
81345200 | 434 | |
ed1e77d3 A |
435 | if 'inputs' in json: |
436 | if not isinstance(json['inputs'], dict): | |
437 | raise ParseException("Malformed specification: inputs is not a dict of framework->input list") | |
81345200 | 438 | |
ed1e77d3 A |
439 | for framework_name, input_list in json['inputs'].iteritems(): |
440 | if not isinstance(input_list, list): | |
441 | raise ParseException("Malformed specification: input list for framework %s is not a list" % framework_name) | |
81345200 | 442 | |
ed1e77d3 A |
443 | framework = Framework.fromString(framework_name) |
444 | for _input in input_list: | |
445 | self.parse_input_with_framework(_input, framework) | |
446 | ||
447 | def parse_type_with_framework(self, json, framework): | |
81345200 | 448 | check_for_required_properties(['name', 'mode'], json, 'type') |
81345200 A |
449 | if framework is not Frameworks.Global: |
450 | check_for_required_properties(['header'], json, 'non-global type') | |
451 | ||
452 | type_name = json['name'] | |
453 | type_mode = TypeMode.fromString(json['mode']) | |
454 | header = json.get('header') | |
455 | enclosing_class = json.get('enclosing_class') | |
456 | enum_values = json.get('values') | |
457 | guarded_enum_values = json.get('guarded_values', {}) | |
458 | type_storage = json.get('storage') | |
459 | type_flags = json.get('flags', []) | |
460 | guard = json.get('guard', None) | |
461 | _type = Type(type_name, type_mode, framework, header, enclosing_class, enum_values, guarded_enum_values, type_storage, type_flags, guard) | |
462 | if _type.is_enum() or _type.is_enum_class(): | |
463 | check_for_required_properties(['values'], json, 'enum') | |
464 | if not isinstance(json['values'], list) or len(_type.values) == 0: | |
465 | raise ParseException("Malformed specification: enum %s does not supply a list of values" % type_name) | |
466 | ||
ed1e77d3 A |
467 | if _type.is_enum() and enclosing_class is None and type_storage is None: |
468 | raise ParseException("Could not parse enum %s: C-style enums not enclosed by a class must specify their storage type so they can be forward declared." % type_name) | |
81345200 A |
469 | |
470 | self.types.append(_type) | |
471 | ||
ed1e77d3 | 472 | def parse_input_with_framework(self, json, framework): |
81345200 | 473 | check_for_required_properties(['name', 'description', 'queue', 'members'], json, 'input') |
ed1e77d3 | 474 | _input = Input(json['name'], json['description'], framework, json['queue'], json.get('flags', []), json.get('guard')) |
81345200 A |
475 | if isinstance(json['members'], list): |
476 | for member in json['members']: | |
477 | check_for_required_properties(['name', 'type'], member, 'member') | |
478 | _input.members.append(InputMember(member['name'], member['type'], member.get('flags', []))) | |
479 | ||
480 | self.inputs.append(_input) | |
481 | ||
482 | # Types cannot (yet) reference other types, so we can check references in one pass. | |
483 | def resolve_types(self): | |
484 | for _type in self.types: | |
485 | self.typecheck_type(_type) | |
486 | ||
487 | for _input in self.inputs: | |
488 | self.typecheck_input(_input) | |
489 | ||
490 | def typecheck_type(self, _type): | |
491 | log.debug("typecheck type " + _type.type_name()) | |
492 | ||
493 | if _type.type_name() in self.types_by_name: | |
494 | raise TypecheckException("Duplicate type with name: " + _type.type_name()) | |
495 | ||
496 | self.types_by_name[_type.type_name()] = _type | |
497 | ||
498 | def typecheck_input(self, _input): | |
499 | log.debug("typecheck input " + _input.name) | |
500 | ||
501 | if _input.name in self.inputs_by_name: | |
502 | raise TypecheckException("Duplicate input with name: " + _input.name) | |
503 | ||
504 | seen_members = {} | |
505 | ||
506 | for member in _input.members: | |
507 | if member.memberName in seen_members: | |
508 | raise TypecheckException("Duplicate input member with name: " + member.memberName) | |
509 | ||
510 | self.typecheck_input_member(member, _input) | |
511 | seen_members[member.memberName] = member | |
512 | ||
513 | self.inputs_by_name[_input.name] = _input | |
514 | ||
515 | def typecheck_input_member(self, input_member, _input): | |
516 | log.debug("typecheck member '%s' of '%s'" % (input_member.memberName, _input.name)) | |
517 | ||
518 | if not input_member.typeName in self.types_by_name: | |
519 | raise TypecheckException("Unknown type '%s' referenced by member '%s' of input '%s'" % (input_member.typeName, input_member.memberName, _input.name)) | |
520 | ||
521 | ||
522 | # A writer that only updates file if it actually changed. | |
523 | class IncrementalFileWriter: | |
524 | def __init__(self, filepath, force_output): | |
525 | self._filepath = filepath | |
526 | self._output = "" | |
527 | self.force_output = force_output | |
528 | ||
529 | def write(self, text): | |
530 | self._output += text | |
531 | ||
532 | def close(self): | |
533 | text_changed = True | |
534 | self._output = self._output.rstrip() + "\n" | |
535 | ||
536 | try: | |
537 | read_file = open(self._filepath, "r") | |
538 | old_text = read_file.read() | |
539 | read_file.close() | |
540 | text_changed = old_text != self._output | |
541 | except: | |
542 | # Ignore, just overwrite by default | |
543 | pass | |
544 | ||
545 | if text_changed or self.force_output: | |
546 | out_file = open(self._filepath, "w") | |
547 | out_file.write(self._output) | |
548 | out_file.close() | |
549 | ||
550 | ||
551 | def wrap_with_guard(contents, condition=None): | |
552 | if condition is None: | |
553 | return contents | |
554 | ||
555 | return "\n".join([ | |
556 | "#if %s" % condition, | |
557 | contents, | |
558 | "#endif // %s" % condition | |
559 | ]) | |
560 | ||
561 | ||
562 | class Generator: | |
563 | def __init__(self, model, target_framework_name, input_filepath, output_prefix): | |
564 | self._model = model | |
565 | self.target_framework = Framework.fromString(target_framework_name) | |
566 | self.traits_framework = Framework.fromString(self.setting('traitsFrameworkName')) | |
567 | self._input_filepath = input_filepath | |
568 | self._output_prefix = output_prefix | |
569 | ||
570 | def setting(self, key, default=''): | |
571 | return self.target_framework.setting(key, GLOBAL_CONFIG.get(key, default)) | |
572 | ||
ed1e77d3 A |
573 | def should_generate_item(self, item): |
574 | return item.framework is self.target_framework | |
575 | ||
81345200 A |
576 | # This does not account for any filename mangling performed on behalf of the test harness. |
577 | def output_filename(self, extension=None): | |
578 | components = [] | |
579 | if len(self._output_prefix) > 0: | |
580 | components.extend([self._output_prefix, '-']) | |
581 | ||
582 | components.extend([self.setting('prefix'), self.setting('baseFilename')]) | |
583 | ||
584 | if extension is not None: | |
585 | components.extend(['.', extension]) | |
586 | ||
587 | return "".join(components) | |
588 | ||
589 | def write_output_files(self, _dir, force=False): | |
590 | header_file = IncrementalFileWriter(os.path.join(_dir, self.output_filename('h')), force) | |
591 | implementation_file = IncrementalFileWriter(os.path.join(_dir, self.output_filename('cpp')), force) | |
592 | ||
593 | header_file.write(self.generate_header()) | |
594 | implementation_file.write(self.generate_implementation()) | |
595 | ||
596 | header_file.close() | |
597 | implementation_file.close() | |
598 | ||
599 | def generate_header(self): | |
ed1e77d3 A |
600 | enums_to_generate = filter(self.should_generate_item, self._model.enum_types()) |
601 | inputs_to_generate = filter(self.should_generate_item, self._model.inputs) | |
602 | ||
81345200 A |
603 | template_arguments = { |
604 | 'licenseBlock': self.generate_license(), | |
605 | 'headerGuard': re.sub('[-./]', '_', self.output_filename() + ".h"), | |
606 | 'filename': self.output_filename(), | |
607 | 'guardCondition': self.setting('guardCondition'), | |
608 | 'traitsNamespace': self.traits_framework.setting('namespace'), | |
609 | 'inputsNamespace': self.target_framework.setting('namespace'), | |
610 | 'includes': self.generate_includes(defaults=self.setting('headerIncludes')), | |
611 | 'typeForwardDeclarations': self.generate_type_forward_declarations(), | |
ed1e77d3 A |
612 | 'inputForwardDeclarations': "\n".join([wrap_with_guard("class %s;", _input.guard) % _input.name for _input in inputs_to_generate]), |
613 | 'inputClassDeclarations': "\n\n".join([self.generate_class_declaration(_input) for _input in inputs_to_generate]), | |
614 | 'inputTraitDeclarations': "\n\n".join([self.generate_input_trait_declaration(_input) for _input in inputs_to_generate]), | |
615 | 'inputTypeTraitDeclarations': "\n\n".join([self.generate_input_type_trait_declaration(_input) for _input in inputs_to_generate]), | |
616 | 'enumTraitDeclarations': "\n\n".join([wrap_with_guard(self.generate_enum_trait_declaration(_type), _type.guard) for _type in enums_to_generate]), | |
81345200 A |
617 | 'forEachMacro': self.generate_for_each_macro(), |
618 | } | |
619 | ||
620 | return Template(Templates.HeaderSkeleton).substitute(template_arguments) | |
621 | ||
622 | def generate_implementation(self): | |
ed1e77d3 A |
623 | enums_to_generate = filter(self.should_generate_item, self._model.enum_types()) |
624 | inputs_to_generate = filter(self.should_generate_item, self._model.inputs) | |
625 | ||
81345200 A |
626 | template_arguments = { |
627 | 'licenseBlock': self.generate_license(), | |
628 | 'filename': self.output_filename(), | |
629 | 'guardCondition': self.setting('guardCondition'), | |
630 | 'traitsNamespace': self.traits_framework.setting('namespace'), | |
631 | 'inputsNamespace': self.target_framework.setting('namespace'), | |
632 | 'includes': self.generate_includes(defaults=self.setting('implIncludes'), includes_for_types=True), | |
ed1e77d3 A |
633 | 'inputClassImplementations': "\n\n".join([self.generate_class_implementation(_input) for _input in inputs_to_generate]), |
634 | 'inputTraitImplementations': "\n\n".join([self.generate_input_trait_implementation(_input) for _input in inputs_to_generate]), | |
635 | 'enumTraitImplementations': "\n\n".join([wrap_with_guard(self.generate_enum_trait_implementation(_type), _type.guard) for _type in enums_to_generate]), | |
81345200 A |
636 | } |
637 | ||
638 | return Template(Templates.ImplementationSkeleton).substitute(template_arguments) | |
639 | ||
640 | def generate_license(self): | |
641 | return Template(Templates.CopyrightBlock).substitute(None, inputFilename=os.path.basename(self._input_filepath)) | |
642 | ||
643 | def generate_includes(self, defaults=[], includes_for_types=False): | |
644 | lines = set() | |
645 | ||
646 | for _type in self._model.types: | |
647 | # Types in the "global" framework are implicitly declared and available in all namespaces. | |
648 | if _type.framework is Frameworks.Global: | |
649 | continue | |
650 | # For RefCounted types, we reverse when to include the header so that the destructor can be | |
651 | # used in the header file. | |
652 | include_for_destructor = _type.mode is TypeModes.SHARED | |
653 | # Enums within classes cannot be forward declared, so we include | |
654 | # headers with the relevant class declaration. | |
ed1e77d3 | 655 | include_for_enclosing_class = _type.enclosing_class is not None |
81345200 A |
656 | # Include headers for types like URL and String which are copied, not owned or shared. |
657 | include_for_copyable_member = _type.mode is TypeModes.HEAVY_SCALAR | |
658 | if (not includes_for_types) ^ (include_for_destructor or include_for_enclosing_class or include_for_copyable_member): | |
659 | continue | |
660 | ||
661 | if self.target_framework != _type.framework: | |
662 | lines.add("#include <%s>" % _type.header) | |
663 | else: | |
664 | lines.add("#include \"%s\"" % os.path.basename(_type.header)) | |
665 | ||
666 | for entry in defaults: | |
667 | (allowed_framework_names, data) = entry | |
668 | (framework_name, header_path) = data | |
669 | ||
670 | if self.target_framework.name not in allowed_framework_names: | |
671 | continue | |
672 | if self.target_framework.name != framework_name: | |
673 | lines.add("#include <%s>" % header_path) | |
674 | else: | |
675 | lines.add("#include \"%s\"" % os.path.basename(header_path)) | |
676 | ||
677 | return "\n".join(sorted(list(lines))) | |
678 | ||
679 | def generate_type_forward_declarations(self): | |
680 | lines = [] | |
681 | ||
682 | decls_by_framework = {} | |
683 | frameworks = [Framework.fromString(s) for s in FRAMEWORK_CONFIG_MAP.keys() if s != Frameworks.Global.name] | |
684 | for framework in frameworks: | |
685 | decls_by_framework[framework] = [] | |
686 | ||
687 | for _type in self._model.types: | |
688 | if _type.framework not in frameworks: | |
689 | continue | |
690 | if _type.enclosing_class is not None: | |
691 | continue | |
692 | if _type.mode == TypeModes.HEAVY_SCALAR: | |
693 | continue | |
694 | if _type.mode == TypeModes.SCALAR and not (_type.is_enum() or _type.is_enum_class()): | |
695 | continue | |
696 | if _type.is_enum(): | |
697 | declaration = "enum %s : %s;" % (_type.type_name(), _type.underlying_storage) | |
698 | else: | |
699 | declaration = "%s %s;" % (_type.declaration_kind(), _type.type_name()) | |
700 | decls_by_framework[_type.framework].append(declaration) | |
701 | ||
702 | # Declare all namespaces explicitly, even if it's the main namespace. | |
703 | for framework in frameworks: | |
704 | if len(decls_by_framework[framework]) == 0: | |
705 | continue | |
706 | ||
707 | decls_by_framework[framework].sort() | |
708 | lines.append("namespace %s {" % framework.setting('namespace')) | |
709 | lines.extend(decls_by_framework[framework]) | |
710 | lines.append("}") | |
711 | lines.append("") | |
712 | ||
713 | return "\n".join(lines) | |
714 | ||
715 | def generate_class_declaration(self, _input): | |
716 | extra_declarations = [] | |
717 | if _input.queue == InputQueues.EVENT_LOOP: | |
718 | extra_declarations.extend([ | |
719 | "", | |
720 | " // EventLoopInput API", | |
721 | " virtual void dispatch(ReplayController&) override final;", | |
722 | ]) | |
723 | ||
724 | if _input.setting('CREATE_FROM_PAGE'): | |
725 | extra_declarations.extend([ | |
726 | " static std::unique_ptr<%s> createFromPage(const Page&);" % _input.name | |
727 | ]) | |
728 | ||
729 | member_getters = [self.generate_input_member_getter(_member) for _member in _input.members] | |
730 | ||
731 | member_declarations = [self.generate_input_member_declaration(_member) for _member in _input.members] | |
732 | if len(member_declarations) > 0: | |
733 | member_declarations.insert(0, "private:") | |
734 | ||
735 | template_arguments = { | |
736 | 'inputConstructor': self.generate_input_constructor_declaration(_input), | |
737 | 'inputDestructor': self.generate_input_destructor_declaration(_input), | |
738 | 'inputName': _input.name, | |
739 | 'inputQueue': _input.setting('enumValue'), | |
740 | 'baseClass': _input.setting('baseClass') % _input.name, | |
741 | 'extraDeclarations': "\n".join(extra_declarations), | |
742 | 'memberGetters': "\n".join(member_getters), | |
743 | 'memberDeclarations': "\n".join(member_declarations), | |
744 | } | |
745 | ||
746 | return wrap_with_guard(Template(Templates.InputClassDeclaration).substitute(template_arguments), _input.guard) | |
747 | ||
748 | def generate_input_constructor_declaration(self, _input): | |
749 | formals_list = self.generate_constructor_formals_list(_input) | |
750 | terms = [] | |
751 | if self.setting('exportMacro'): | |
752 | terms.append(self.setting('exportMacro')) | |
753 | terms.append("%s(%s)" % (_input.name, formals_list)) | |
754 | return " %s;" % " ".join(terms) | |
755 | ||
756 | def generate_input_destructor_declaration(self, _input): | |
757 | return " virtual ~%s();" % _input.name | |
758 | ||
759 | def generate_input_member_getter(self, _member): | |
760 | member_type = self._model.get_type_for_member(_member) | |
761 | return " %s %s() const { return %s; }" % (member_type.borrow_type(), _member.memberName, self.generate_member_borrow_expression(_member)) | |
762 | ||
763 | def generate_input_member_declaration(self, _member): | |
764 | member_type = self._model.get_type_for_member(_member) | |
765 | return " %s m_%s;" % (member_type.storage_type(), _member.memberName) | |
766 | ||
767 | def generate_input_member_tuples(self, _input): | |
768 | return [(_member, self._model.get_type_for_member(_member)) for _member in _input.members] | |
769 | ||
ed1e77d3 A |
770 | def qualified_input_name(self, _input, forceQualified=False): |
771 | if forceQualified or self.target_framework != self.traits_framework: | |
81345200 | 772 | return "%s::%s" % (self.target_framework.setting('namespace'), _input.name) |
ed1e77d3 A |
773 | else: |
774 | return _input.name | |
81345200 A |
775 | |
776 | def generate_input_trait_declaration(self, _input): | |
777 | decl_type = ['struct'] | |
778 | if len(self.setting('exportMacro')) > 0: | |
779 | decl_type.append(self.setting('exportMacro')) | |
780 | ||
781 | template_arguments = { | |
782 | 'structOrClass': " ".join(decl_type), | |
783 | 'queueType': _input.queue.setting('enumValue'), | |
784 | 'qualifiedInputName': self.qualified_input_name(_input), | |
785 | } | |
786 | ||
787 | return wrap_with_guard(Template(Templates.InputTraitsDeclaration).substitute(template_arguments), _input.guard) | |
788 | ||
ed1e77d3 A |
789 | def generate_input_type_trait_declaration(self, _input): |
790 | template_arguments = { | |
791 | 'qualifiedInputName': self.qualified_input_name(_input, forceQualified=True), | |
792 | } | |
793 | ||
794 | return wrap_with_guard(Template(Templates.InputTypeTraitsDeclaration).substitute(template_arguments), _input.guard) | |
795 | ||
81345200 | 796 | def generate_enum_trait_declaration(self, _type): |
ed1e77d3 A |
797 | decl_type = ['struct'] |
798 | if len(self.setting('exportMacro')) > 0: | |
799 | decl_type.append(self.setting('exportMacro')) | |
800 | ||
81345200 A |
801 | should_qualify_type = _type.framework != self.traits_framework |
802 | template = Templates.EnumTraitDeclaration if _type.is_enum() else Templates.EnumClassTraitDeclaration | |
803 | template_arguments = { | |
ed1e77d3 A |
804 | 'encodingTypeArgument': _type.encoding_type_argument(qualified=should_qualify_type), |
805 | 'enumType': _type.type_name(qualified=should_qualify_type), | |
806 | 'structOrClass': " ".join(decl_type) | |
81345200 A |
807 | } |
808 | return Template(template).substitute(template_arguments) | |
809 | ||
810 | def generate_for_each_macro(self): | |
ed1e77d3 A |
811 | inputs_to_generate = filter(self.should_generate_item, self._model.inputs) |
812 | ||
81345200 A |
813 | macro_name = "%s_REPLAY_INPUT_NAMES_FOR_EACH" % self.setting('prefix').upper() |
814 | lines = [] | |
815 | lines.append("#define %s(macro) \\" % macro_name) | |
ed1e77d3 | 816 | lines.extend([" macro(%s) \\" % _input.name for _input in inputs_to_generate]) |
81345200 A |
817 | lines.append(" \\") |
818 | lines.append("// end of %s" % macro_name) | |
819 | return "\n".join(lines) | |
820 | ||
821 | def generate_class_implementation(self, _input): | |
822 | template_arguments = { | |
823 | 'inputName': _input.name, | |
824 | 'inputsNamespace': self.target_framework.setting('namespace'), | |
825 | 'initializerList': self.generate_constructor_initializer_list(_input), | |
826 | 'constructorFormalsList': self.generate_constructor_formals_list(_input), | |
827 | } | |
828 | ||
829 | return wrap_with_guard(Template(Templates.InputClassImplementation).substitute(template_arguments), _input.guard) | |
830 | ||
831 | def generate_enum_trait_implementation(self, _type): | |
832 | should_qualify_type = _type.framework != self.traits_framework | |
833 | prefix_components = [] | |
834 | if should_qualify_type: | |
835 | prefix_components.append(_type.framework.setting('namespace')) | |
ed1e77d3 | 836 | if _type.is_enum() and _type.enclosing_class is not None: |
81345200 | 837 | prefix_components.append(_type.enclosing_class) |
ed1e77d3 A |
838 | elif _type.is_enum_class(): |
839 | prefix_components.append(_type.type_name(qualified=False)) | |
81345200 A |
840 | prefix_components.append("") |
841 | enum_prefix = "::".join(prefix_components) | |
842 | encodeLines = [] | |
843 | ||
844 | if _type.is_enum(): | |
845 | encode_template = Templates.EnumEncodeCase | |
846 | decode_template = Templates.EnumDecodeCase | |
847 | enum_trait_template = Templates.EnumTraitImplementation | |
848 | else: | |
849 | encode_template = Templates.EnumClassEncodeCase | |
850 | decode_template = Templates.EnumClassDecodeCase | |
851 | enum_trait_template = Templates.EnumClassTraitImplementation | |
852 | ||
853 | # Generate body for encode. | |
854 | for _value in _type.values: | |
855 | template_arguments = { | |
856 | 'enumStringValue': _value, | |
857 | 'qualifiedEnumValue': "%s%s" % (enum_prefix, _value), | |
858 | } | |
859 | encodeLines.append(Template(encode_template).substitute(template_arguments)) | |
860 | ||
861 | for guard, guard_values in _type.guard_values_map.iteritems(): | |
862 | guardedLines = [] | |
863 | for guard_value in guard_values: | |
864 | template_arguments = { | |
865 | 'enumStringValue': guard_value, | |
866 | 'qualifiedEnumValue': "%s%s" % (enum_prefix, guard_value), | |
867 | } | |
868 | guardedLines.append(Template(encode_template).substitute(template_arguments)) | |
869 | encodeLines.append(wrap_with_guard("\n".join(guardedLines), guard)) | |
870 | ||
871 | # Generate body for decode. | |
872 | decodeLines = [] | |
ed1e77d3 A |
873 | for i, _value in enumerate(_type.values): |
874 | ||
81345200 | 875 | template_arguments = { |
ed1e77d3 | 876 | 'branchKeyword': "else if" if i > 0 else "if", |
81345200 A |
877 | 'enumStringValue': _value, |
878 | 'qualifiedEnumValue': "%s%s" % (enum_prefix, _value), | |
879 | 'qualifiedEnumName': _type.type_name(qualified=should_qualify_type) | |
880 | } | |
881 | decodeLines.append(Template(decode_template).substitute(template_arguments)) | |
882 | ||
883 | for guard, guard_values in _type.guard_values_map.iteritems(): | |
884 | guardedLines = [] | |
ed1e77d3 | 885 | for i, guard_value in enumerate(guard_values): |
81345200 | 886 | template_arguments = { |
ed1e77d3 | 887 | 'branchKeyword': "else if" if i > 0 else "if", |
81345200 A |
888 | 'enumStringValue': guard_value, |
889 | 'qualifiedEnumValue': "%s%s" % (enum_prefix, guard_value), | |
890 | 'qualifiedEnumName': _type.type_name(qualified=should_qualify_type) | |
891 | } | |
892 | guardedLines.append(Template(decode_template).substitute(template_arguments)) | |
893 | decodeLines.append(wrap_with_guard("\n".join(guardedLines), guard)) | |
894 | ||
895 | template_arguments = { | |
ed1e77d3 A |
896 | 'encodingTypeArgument': _type.encoding_type_argument(qualified=should_qualify_type), |
897 | 'enumType': _type.type_name(qualified=should_qualify_type), | |
81345200 A |
898 | 'encodeCases': "\n".join(encodeLines), |
899 | 'decodeCases': "\n".join(decodeLines) | |
900 | } | |
901 | ||
902 | return Template(enum_trait_template).substitute(template_arguments) | |
903 | ||
904 | def generate_input_trait_implementation(self, _input): | |
905 | template_arguments = { | |
906 | 'inputsNamespace': self.target_framework.setting('namespace'), | |
ed1e77d3 | 907 | 'inputNameStringLiteral': '"%s"' % _input.name, |
81345200 A |
908 | 'qualifiedInputName': self.qualified_input_name(_input), |
909 | 'constructorArguments': self.generate_constructor_arguments_list(_input), | |
910 | 'constructorFormalsList': self.generate_constructor_formals_list(_input), | |
911 | 'encodeSteps': self.generate_input_encode_implementation(_input), | |
912 | 'decodeSteps': self.generate_input_decode_implementation(_input), | |
913 | } | |
914 | return wrap_with_guard(Template(Templates.InputTraitsImplementation).substitute(template_arguments), _input.guard) | |
915 | ||
916 | def generate_input_encode_implementation(self, _input): | |
917 | steps = [] | |
918 | for (_member, _type) in self.generate_input_member_tuples(_input): | |
919 | should_qualify_type = _type.framework != self.traits_framework | |
ed1e77d3 | 920 | put_method = "put<%s>" % _type.encoding_type_argument(qualified=should_qualify_type) |
81345200 A |
921 | |
922 | steps.extend([ | |
923 | " encodedValue.%s(ASCIILiteral(\"%s\"), input.%s());" % (put_method, _member.memberName, _member.memberName) | |
924 | ]) | |
925 | ||
926 | if len(steps) == 0: | |
927 | steps.extend([ | |
928 | " UNUSED_PARAM(encodedValue);", | |
929 | " UNUSED_PARAM(input);", | |
930 | ]) | |
931 | ||
932 | return "\n".join(steps) | |
933 | ||
934 | def generate_input_decode_implementation(self, _input): | |
935 | steps = [] | |
936 | for (_member, _type) in self.generate_input_member_tuples(_input): | |
937 | should_qualify_type = _type.framework != self.traits_framework | |
ed1e77d3 | 938 | get_method = "get<%s>" % _type.encoding_type_argument(qualified=should_qualify_type) |
81345200 A |
939 | |
940 | lines = [ | |
941 | " %s %s;" % (_type.storage_type(qualified=should_qualify_type), _member.memberName), | |
942 | " if (!encodedValue.%s(ASCIILiteral(\"%s\"), %s))" % (get_method, _member.memberName, _member.memberName), | |
943 | " return false;", | |
944 | "" | |
945 | ] | |
946 | ||
947 | steps.append("\n".join(lines)) | |
948 | ||
949 | if len(steps) == 0: | |
950 | steps.extend([ | |
951 | " UNUSED_PARAM(encodedValue);", | |
952 | ]) | |
953 | ||
954 | return "\n".join(steps) | |
955 | ||
956 | def generate_constructor_initializer_list(self, _input): | |
957 | initializers = [] | |
958 | initializers.append(" : %s()" % (_input.setting('baseClass') % _input.name)) | |
959 | for _member in _input.members: | |
960 | initializers.append(" , m_%s(%s)" % (_member.memberName, self.generate_member_move_expression(_member))) | |
961 | ||
962 | return "\n".join(initializers) | |
963 | ||
964 | def generate_constructor_formals_list(self, _input): | |
965 | member_tuples = self.generate_input_member_tuples(_input) | |
966 | return ", ".join(["%s %s" % (_type.argument_type(), _member.memberName) for (_member, _type) in member_tuples]) | |
967 | ||
968 | def generate_member_borrow_expression(self, _member): | |
969 | _type = self._model.get_type_for_member(_member) | |
970 | expression = "m_%s" % _member.memberName | |
971 | if _type.mode == TypeModes.OWNED: | |
972 | expression = "*" + expression | |
973 | ||
974 | return expression | |
975 | ||
976 | def generate_member_move_expression(self, _member): | |
977 | _type = self._model.get_type_for_member(_member) | |
ed1e77d3 | 978 | if _type.mode in [TypeModes.OWNED, TypeModes.SHARED]: |
81345200 A |
979 | return "WTF::move(%s)" % _member.memberName |
980 | else: | |
981 | return _member.memberName | |
982 | ||
983 | def generate_constructor_arguments_list(self, _input): | |
984 | return ", ".join([self.generate_member_move_expression(_member) for _member in _input.members]) | |
985 | ||
986 | ||
ed1e77d3 | 987 | def generate_from_specifications(input_filepaths=[], output_prefix="", output_dirpath=None, framework_name=None, force_output=False): |
81345200 A |
988 | |
989 | if not framework_name in FRAMEWORK_CONFIG_MAP: | |
990 | raise ParseException("Unknown or unsupported framework name supplied: " + framework_name) | |
991 | ||
ed1e77d3 A |
992 | if len(input_filepaths) == 0: |
993 | raise ParseException("Must provide at least one specification file, none were provided.") | |
994 | ||
995 | def parse_json_from_file(input_filepath): | |
996 | try: | |
997 | with open(input_filepath, "r") as input_file: | |
998 | return json.load(input_file) | |
999 | except ValueError as e: | |
1000 | raise Exception("Error parsing valid JSON in file: " + input_filepath) | |
1001 | ||
1002 | specifications = map(parse_json_from_file, input_filepaths) | |
1003 | ||
1004 | model = InputsModel() | |
1005 | for spec in specifications: | |
1006 | model.parse_specification(spec) | |
1007 | ||
81345200 | 1008 | model.resolve_types() |
ed1e77d3 | 1009 | generator = Generator(model, framework_name, input_filepaths[0], output_prefix) |
81345200 A |
1010 | |
1011 | generator.write_output_files(output_dirpath, force_output) | |
1012 | ||
1013 | ||
1014 | if __name__ == '__main__': | |
1015 | allowed_framework_names = FRAMEWORK_CONFIG_MAP.keys() | |
1016 | ||
ed1e77d3 | 1017 | cli_parser = optparse.OptionParser(usage="usage: %prog [options] <Inputs.json> [, <MoreInputs.json> ]") |
81345200 A |
1018 | cli_parser.add_option("-o", "--outputDir", help="Directory where generated files should be written.") |
1019 | cli_parser.add_option("--framework", type="choice", choices=allowed_framework_names, help="The framework these inputs belong to.") # JavaScriptCore, WebCore | |
1020 | cli_parser.add_option("--force", action="store_true", help="Force output of generated scripts, even if nothing changed.") | |
1021 | cli_parser.add_option("-v", "--debug", action="store_true", help="Log extra output for debugging the generator itself.") | |
1022 | cli_parser.add_option("-t", "--test", action="store_true", help="Enable test mode. Use unique output filenames created by prepending the input filename.") | |
1023 | ||
1024 | options = None | |
1025 | ||
1026 | arg_options, arg_values = cli_parser.parse_args() | |
81345200 A |
1027 | |
1028 | if not arg_options.outputDir: | |
1029 | raise ParseException("Missing output directory") | |
1030 | ||
1031 | if arg_options.debug: | |
1032 | log.setLevel(logging.DEBUG) | |
1033 | ||
1034 | options = { | |
ed1e77d3 | 1035 | 'input_filepaths': arg_values, |
81345200 A |
1036 | 'output_dirpath': arg_options.outputDir, |
1037 | 'output_prefix': os.path.basename(arg_values[0]) if arg_options.test else "", | |
1038 | 'framework_name': arg_options.framework, | |
1039 | 'force_output': arg_options.force | |
1040 | } | |
1041 | ||
1042 | try: | |
ed1e77d3 | 1043 | generate_from_specifications(**options) |
81345200 A |
1044 | except (ParseException, TypecheckException) as e: |
1045 | if arg_options.test: | |
1046 | log.error(e.message) | |
1047 | else: | |
1048 | raise e # Force the build to fail. |