1#!/usr/bin/env python3
2# Copyright 2017 The Dawn Authors
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8#     http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
16import json, os, sys
17from collections import namedtuple
18
19from generator_lib import Generator, run_generator, FileRender
20
21############################################################
22# OBJECT MODEL
23############################################################
24
25
26class Name:
27    def __init__(self, name, native=False):
28        self.native = native
29        self.name = name
30        if native:
31            self.chunks = [name]
32        else:
33            self.chunks = name.split(' ')
34
35    def get(self):
36        return self.name
37
38    def CamelChunk(self, chunk):
39        return chunk[0].upper() + chunk[1:]
40
41    def canonical_case(self):
42        return (' '.join(self.chunks)).lower()
43
44    def concatcase(self):
45        return ''.join(self.chunks)
46
47    def camelCase(self):
48        return self.chunks[0] + ''.join(
49            [self.CamelChunk(chunk) for chunk in self.chunks[1:]])
50
51    def CamelCase(self):
52        return ''.join([self.CamelChunk(chunk) for chunk in self.chunks])
53
54    def SNAKE_CASE(self):
55        return '_'.join([chunk.upper() for chunk in self.chunks])
56
57    def snake_case(self):
58        return '_'.join(self.chunks)
59
60    def js_enum_case(self):
61        result = self.chunks[0].lower()
62        for chunk in self.chunks[1:]:
63            if not result[-1].isdigit():
64                result += '-'
65            result += chunk.lower()
66        return result
67
68
69def concat_names(*names):
70    return ' '.join([name.canonical_case() for name in names])
71
72
73class Type:
74    def __init__(self, name, json_data, native=False):
75        self.json_data = json_data
76        self.dict_name = name
77        self.name = Name(name, native=native)
78        self.category = json_data['category']
79        self.javascript = self.json_data.get('javascript', True)
80
81
82EnumValue = namedtuple('EnumValue', ['name', 'value', 'valid', 'jsrepr'])
83
84
85class EnumType(Type):
86    def __init__(self, name, json_data):
87        Type.__init__(self, name, json_data)
88
89        self.values = []
90        self.contiguousFromZero = True
91        lastValue = -1
92        for m in self.json_data['values']:
93            value = m['value']
94            if value != lastValue + 1:
95                self.contiguousFromZero = False
96            lastValue = value
97            self.values.append(
98                EnumValue(Name(m['name']), value, m.get('valid', True),
99                          m.get('jsrepr', None)))
100
101        # Assert that all values are unique in enums
102        all_values = set()
103        for value in self.values:
104            if value.value in all_values:
105                raise Exception("Duplicate value {} in enum {}".format(
106                    value.value, name))
107            all_values.add(value.value)
108
109
110BitmaskValue = namedtuple('BitmaskValue', ['name', 'value'])
111
112
113class BitmaskType(Type):
114    def __init__(self, name, json_data):
115        Type.__init__(self, name, json_data)
116        self.values = [
117            BitmaskValue(Name(m['name']), m['value'])
118            for m in self.json_data['values']
119        ]
120        self.full_mask = 0
121        for value in self.values:
122            self.full_mask = self.full_mask | value.value
123
124
125class CallbackType(Type):
126    def __init__(self, name, json_data):
127        Type.__init__(self, name, json_data)
128        self.arguments = []
129
130
131class NativeType(Type):
132    def __init__(self, name, json_data):
133        Type.__init__(self, name, json_data, native=True)
134
135
136# Methods and structures are both "records", so record members correspond to
137# method arguments or structure members.
138class RecordMember:
139    def __init__(self,
140                 name,
141                 typ,
142                 annotation,
143                 optional=False,
144                 is_return_value=False,
145                 default_value=None,
146                 skip_serialize=False):
147        self.name = name
148        self.type = typ
149        self.annotation = annotation
150        self.length = None
151        self.optional = optional
152        self.is_return_value = is_return_value
153        self.handle_type = None
154        self.default_value = default_value
155        self.skip_serialize = skip_serialize
156
157    def set_handle_type(self, handle_type):
158        assert self.type.dict_name == "ObjectHandle"
159        self.handle_type = handle_type
160
161
162Method = namedtuple('Method', ['name', 'return_type', 'arguments'])
163
164
165class ObjectType(Type):
166    def __init__(self, name, json_data):
167        Type.__init__(self, name, json_data)
168        self.methods = []
169        self.built_type = None
170
171
172class Record:
173    def __init__(self, name):
174        self.name = Name(name)
175        self.members = []
176        self.may_have_dawn_object = False
177
178    def update_metadata(self):
179        def may_have_dawn_object(member):
180            if isinstance(member.type, ObjectType):
181                return True
182            elif isinstance(member.type, StructureType):
183                return member.type.may_have_dawn_object
184            else:
185                return False
186
187        self.may_have_dawn_object = any(
188            may_have_dawn_object(member) for member in self.members)
189
190        # Set may_have_dawn_object to true if the type is chained or
191        # extensible. Chained structs may contain a Dawn object.
192        if isinstance(self, StructureType):
193            self.may_have_dawn_object = (self.may_have_dawn_object
194                                         or self.chained or self.extensible)
195
196
197class StructureType(Record, Type):
198    def __init__(self, name, json_data):
199        Record.__init__(self, name)
200        Type.__init__(self, name, json_data)
201        self.chained = json_data.get("chained", False)
202        self.extensible = json_data.get("extensible", False)
203        # Chained structs inherit from wgpu::ChainedStruct, which has
204        # nextInChain, so setting both extensible and chained would result in
205        # two nextInChain members.
206        assert not (self.extensible and self.chained)
207
208
209class Command(Record):
210    def __init__(self, name, members=None):
211        Record.__init__(self, name)
212        self.members = members or []
213        self.derived_object = None
214        self.derived_method = None
215
216
217def linked_record_members(json_data, types):
218    members = []
219    members_by_name = {}
220    for m in json_data:
221        member = RecordMember(Name(m['name']),
222                              types[m['type']],
223                              m.get('annotation', 'value'),
224                              optional=m.get('optional', False),
225                              is_return_value=m.get('is_return_value', False),
226                              default_value=m.get('default', None),
227                              skip_serialize=m.get('skip_serialize', False))
228        handle_type = m.get('handle_type')
229        if handle_type:
230            member.set_handle_type(types[handle_type])
231        members.append(member)
232        members_by_name[member.name.canonical_case()] = member
233
234    for (member, m) in zip(members, json_data):
235        if member.annotation != 'value':
236            if not 'length' in m:
237                if member.type.category != 'object':
238                    member.length = "constant"
239                    member.constant_length = 1
240                else:
241                    assert False
242            elif m['length'] == 'strlen':
243                member.length = 'strlen'
244            else:
245                member.length = members_by_name[m['length']]
246
247    return members
248
249
250############################################################
251# PARSE
252############################################################
253
254
255def link_object(obj, types):
256    def make_method(json_data):
257        arguments = linked_record_members(json_data.get('args', []), types)
258        return Method(Name(json_data['name']),
259                      types[json_data.get('returns', 'void')], arguments)
260
261    obj.methods = [make_method(m) for m in obj.json_data.get('methods', [])]
262    obj.methods.sort(key=lambda method: method.name.canonical_case())
263
264
265def link_structure(struct, types):
266    struct.members = linked_record_members(struct.json_data['members'], types)
267
268
269def link_callback(callback, types):
270    callback.arguments = linked_record_members(callback.json_data['args'],
271                                               types)
272
273
274# Sort structures so that if struct A has struct B as a member, then B is
275# listed before A.
276#
277# This is a form of topological sort where we try to keep the order reasonably
278# similar to the original order (though the sort isn't technically stable).
279#
280# It works by computing for each struct type what is the depth of its DAG of
281# dependents, then resorting based on that depth using Python's stable sort.
282# This makes a toposort because if A depends on B then its depth will be bigger
283# than B's. It is also nice because all nodes with the same depth are kept in
284# the input order.
285def topo_sort_structure(structs):
286    for struct in structs:
287        struct.visited = False
288        struct.subdag_depth = 0
289
290    def compute_depth(struct):
291        if struct.visited:
292            return struct.subdag_depth
293
294        max_dependent_depth = 0
295        for member in struct.members:
296            if member.type.category == 'structure':
297                max_dependent_depth = max(max_dependent_depth,
298                                          compute_depth(member.type) + 1)
299
300        struct.subdag_depth = max_dependent_depth
301        struct.visited = True
302        return struct.subdag_depth
303
304    for struct in structs:
305        compute_depth(struct)
306
307    result = sorted(structs, key=lambda struct: struct.subdag_depth)
308
309    for struct in structs:
310        del struct.visited
311        del struct.subdag_depth
312
313    return result
314
315
316def parse_json(json):
317    category_to_parser = {
318        'bitmask': BitmaskType,
319        'enum': EnumType,
320        'native': NativeType,
321        'callback': CallbackType,
322        'object': ObjectType,
323        'structure': StructureType,
324    }
325
326    types = {}
327
328    by_category = {}
329    for name in category_to_parser.keys():
330        by_category[name] = []
331
332    for (name, json_data) in json.items():
333        if name[0] == '_':
334            continue
335        category = json_data['category']
336        parsed = category_to_parser[category](name, json_data)
337        by_category[category].append(parsed)
338        types[name] = parsed
339
340    for obj in by_category['object']:
341        link_object(obj, types)
342
343    for struct in by_category['structure']:
344        link_structure(struct, types)
345
346    for callback in by_category['callback']:
347        link_callback(callback, types)
348
349    for category in by_category.keys():
350        by_category[category] = sorted(
351            by_category[category], key=lambda typ: typ.name.canonical_case())
352
353    by_category['structure'] = topo_sort_structure(by_category['structure'])
354
355    for struct in by_category['structure']:
356        struct.update_metadata()
357
358    return {'types': types, 'by_category': by_category}
359
360
361############################################################
362# WIRE STUFF
363############################################################
364
365
366# Create wire commands from api methods
367def compute_wire_params(api_params, wire_json):
368    wire_params = api_params.copy()
369    types = wire_params['types']
370
371    commands = []
372    return_commands = []
373
374    wire_json['special items']['client_handwritten_commands'] += wire_json[
375        'special items']['client_side_commands']
376
377    # Generate commands from object methods
378    for api_object in wire_params['by_category']['object']:
379        for method in api_object.methods:
380            command_name = concat_names(api_object.name, method.name)
381            command_suffix = Name(command_name).CamelCase()
382
383            # Only object return values or void are supported.
384            # Other methods must be handwritten.
385            is_object = method.return_type.category == 'object'
386            is_void = method.return_type.name.canonical_case() == 'void'
387            if not (is_object or is_void):
388                assert command_suffix in (
389                    wire_json['special items']['client_handwritten_commands'])
390                continue
391
392            if command_suffix in (
393                    wire_json['special items']['client_side_commands']):
394                continue
395
396            # Create object method commands by prepending "self"
397            members = [
398                RecordMember(Name('self'), types[api_object.dict_name],
399                             'value')
400            ]
401            members += method.arguments
402
403            # Client->Server commands that return an object return the
404            # result object handle
405            if method.return_type.category == 'object':
406                result = RecordMember(Name('result'),
407                                      types['ObjectHandle'],
408                                      'value',
409                                      is_return_value=True)
410                result.set_handle_type(method.return_type)
411                members.append(result)
412
413            command = Command(command_name, members)
414            command.derived_object = api_object
415            command.derived_method = method
416            commands.append(command)
417
418    for (name, json_data) in wire_json['commands'].items():
419        commands.append(Command(name, linked_record_members(json_data, types)))
420
421    for (name, json_data) in wire_json['return commands'].items():
422        return_commands.append(
423            Command(name, linked_record_members(json_data, types)))
424
425    wire_params['cmd_records'] = {
426        'command': commands,
427        'return command': return_commands
428    }
429
430    for commands in wire_params['cmd_records'].values():
431        for command in commands:
432            command.update_metadata()
433        commands.sort(key=lambda c: c.name.canonical_case())
434
435    wire_params.update(wire_json.get('special items', {}))
436
437    return wire_params
438
439
440#############################################################
441# Generator
442#############################################################
443
444
445def as_varName(*names):
446    return names[0].camelCase() + ''.join(
447        [name.CamelCase() for name in names[1:]])
448
449
450def as_cType(name):
451    if name.native:
452        return name.concatcase()
453    else:
454        return 'WGPU' + name.CamelCase()
455
456
457def as_cTypeDawn(name):
458    if name.native:
459        return name.concatcase()
460    else:
461        return 'Dawn' + name.CamelCase()
462
463
464def as_cTypeEnumSpecialCase(typ):
465    if typ.category == 'bitmask':
466        return as_cType(typ.name) + 'Flags'
467    return as_cType(typ.name)
468
469
470def as_cppType(name):
471    if name.native:
472        return name.concatcase()
473    else:
474        return name.CamelCase()
475
476
477def as_jsEnumValue(value):
478    if value.jsrepr: return value.jsrepr
479    return "'" + value.name.js_enum_case() + "'"
480
481
482def convert_cType_to_cppType(typ, annotation, arg, indent=0):
483    if typ.category == 'native':
484        return arg
485    if annotation == 'value':
486        if typ.category == 'object':
487            return '{}::Acquire({})'.format(as_cppType(typ.name), arg)
488        elif typ.category == 'structure':
489            converted_members = [
490                convert_cType_to_cppType(
491                    member.type, member.annotation,
492                    '{}.{}'.format(arg, as_varName(member.name)), indent + 1)
493                for member in typ.members
494            ]
495
496            converted_members = [(' ' * 4) + m for m in converted_members]
497            converted_members = ',\n'.join(converted_members)
498
499            return as_cppType(typ.name) + ' {\n' + converted_members + '\n}'
500        else:
501            return 'static_cast<{}>({})'.format(as_cppType(typ.name), arg)
502    else:
503        return 'reinterpret_cast<{} {}>({})'.format(as_cppType(typ.name),
504                                                    annotation, arg)
505
506
507def decorate(name, typ, arg):
508    if arg.annotation == 'value':
509        return typ + ' ' + name
510    elif arg.annotation == '*':
511        return typ + ' * ' + name
512    elif arg.annotation == 'const*':
513        return typ + ' const * ' + name
514    elif arg.annotation == 'const*const*':
515        return 'const ' + typ + '* const * ' + name
516    else:
517        assert False
518
519
520def annotated(typ, arg):
521    name = as_varName(arg.name)
522    return decorate(name, typ, arg)
523
524
525def as_cEnum(type_name, value_name):
526    assert not type_name.native and not value_name.native
527    return 'WGPU' + type_name.CamelCase() + '_' + value_name.CamelCase()
528
529
530def as_cEnumDawn(type_name, value_name):
531    assert not type_name.native and not value_name.native
532    return ('DAWN' + '_' + type_name.SNAKE_CASE() + '_' +
533            value_name.SNAKE_CASE())
534
535
536def as_cppEnum(value_name):
537    assert not value_name.native
538    if value_name.concatcase()[0].isdigit():
539        return "e" + value_name.CamelCase()
540    return value_name.CamelCase()
541
542
543def as_cMethod(type_name, method_name):
544    assert not type_name.native and not method_name.native
545    return 'wgpu' + type_name.CamelCase() + method_name.CamelCase()
546
547
548def as_cMethodDawn(type_name, method_name):
549    assert not type_name.native and not method_name.native
550    return 'dawn' + type_name.CamelCase() + method_name.CamelCase()
551
552
553def as_MethodSuffix(type_name, method_name):
554    assert not type_name.native and not method_name.native
555    return type_name.CamelCase() + method_name.CamelCase()
556
557
558def as_cProc(type_name, method_name):
559    assert not type_name.native and not method_name.native
560    return 'WGPU' + 'Proc' + type_name.CamelCase() + method_name.CamelCase()
561
562
563def as_cProcDawn(type_name, method_name):
564    assert not type_name.native and not method_name.native
565    return 'Dawn' + 'Proc' + type_name.CamelCase() + method_name.CamelCase()
566
567
568def as_frontendType(typ):
569    if typ.category == 'object':
570        return typ.name.CamelCase() + 'Base*'
571    elif typ.category in ['bitmask', 'enum']:
572        return 'wgpu::' + typ.name.CamelCase()
573    elif typ.category == 'structure':
574        return as_cppType(typ.name)
575    else:
576        return as_cType(typ.name)
577
578
579def as_wireType(typ):
580    if typ.category == 'object':
581        return typ.name.CamelCase() + '*'
582    elif typ.category in ['bitmask', 'enum']:
583        return 'WGPU' + typ.name.CamelCase()
584    else:
585        return as_cppType(typ.name)
586
587
588def c_methods(types, typ):
589    return typ.methods + [
590        Method(Name('reference'), types['void'], []),
591        Method(Name('release'), types['void'], []),
592    ]
593
594
595def get_c_methods_sorted_by_name(api_params):
596    unsorted = [(as_MethodSuffix(typ.name, method.name), typ, method) \
597            for typ in api_params['by_category']['object'] \
598            for method in c_methods(api_params['types'], typ) ]
599    return [(typ, method) for (_, typ, method) in sorted(unsorted)]
600
601
602def has_callback_arguments(method):
603    return any(arg.type.category == 'callback' for arg in method.arguments)
604
605
606class MultiGeneratorFromDawnJSON(Generator):
607    def get_description(self):
608        return 'Generates code for various target from Dawn.json.'
609
610    def add_commandline_arguments(self, parser):
611        allowed_targets = [
612            'dawn_headers', 'dawncpp_headers', 'dawncpp', 'dawn_proc',
613            'mock_webgpu', 'dawn_wire', "dawn_native_utils"
614        ]
615
616        parser.add_argument('--dawn-json',
617                            required=True,
618                            type=str,
619                            help='The DAWN JSON definition to use.')
620        parser.add_argument('--wire-json',
621                            default=None,
622                            type=str,
623                            help='The DAWN WIRE JSON definition to use.')
624        parser.add_argument(
625            '--targets',
626            required=True,
627            type=str,
628            help=
629            'Comma-separated subset of targets to output. Available targets: '
630            + ', '.join(allowed_targets))
631
632    def get_file_renders(self, args):
633        with open(args.dawn_json) as f:
634            loaded_json = json.loads(f.read())
635        api_params = parse_json(loaded_json)
636
637        targets = args.targets.split(',')
638
639        wire_json = None
640        if args.wire_json:
641            with open(args.wire_json) as f:
642                wire_json = json.loads(f.read())
643
644        base_params = {
645            'Name': lambda name: Name(name),
646            'as_annotated_cType': \
647                lambda arg: annotated(as_cTypeEnumSpecialCase(arg.type), arg),
648            'as_annotated_cppType': \
649                lambda arg: annotated(as_cppType(arg.type.name), arg),
650            'as_cEnum': as_cEnum,
651            'as_cEnumDawn': as_cEnumDawn,
652            'as_cppEnum': as_cppEnum,
653            'as_cMethod': as_cMethod,
654            'as_cMethodDawn': as_cMethodDawn,
655            'as_MethodSuffix': as_MethodSuffix,
656            'as_cProc': as_cProc,
657            'as_cProcDawn': as_cProcDawn,
658            'as_cType': as_cType,
659            'as_cTypeDawn': as_cTypeDawn,
660            'as_cppType': as_cppType,
661            'as_jsEnumValue': as_jsEnumValue,
662            'convert_cType_to_cppType': convert_cType_to_cppType,
663            'as_varName': as_varName,
664            'decorate': decorate,
665            'c_methods': lambda typ: c_methods(api_params['types'], typ),
666            'c_methods_sorted_by_name': \
667                get_c_methods_sorted_by_name(api_params),
668        }
669
670        renders = []
671
672        if 'dawn_headers' in targets:
673            renders.append(
674                FileRender('webgpu.h', 'src/include/dawn/webgpu.h',
675                           [base_params, api_params]))
676            renders.append(
677                FileRender('dawn_proc_table.h',
678                           'src/include/dawn/dawn_proc_table.h',
679                           [base_params, api_params]))
680
681        if 'dawncpp_headers' in targets:
682            renders.append(
683                FileRender('webgpu_cpp.h', 'src/include/dawn/webgpu_cpp.h',
684                           [base_params, api_params]))
685
686        if 'dawn_proc' in targets:
687            renders.append(
688                FileRender('dawn_proc.c', 'src/dawn/dawn_proc.c',
689                           [base_params, api_params]))
690            renders.append(
691                FileRender('dawn_thread_dispatch_proc.cpp',
692                           'src/dawn/dawn_thread_dispatch_proc.cpp',
693                           [base_params, api_params]))
694
695        if 'dawncpp' in targets:
696            renders.append(
697                FileRender('webgpu_cpp.cpp', 'src/dawn/webgpu_cpp.cpp',
698                           [base_params, api_params]))
699
700        if 'emscripten_bits' in targets:
701            renders.append(
702                FileRender('webgpu_struct_info.json',
703                           'src/dawn/webgpu_struct_info.json',
704                           [base_params, api_params]))
705            renders.append(
706                FileRender('library_webgpu_enum_tables.js',
707                           'src/dawn/library_webgpu_enum_tables.js',
708                           [base_params, api_params]))
709
710        if 'mock_webgpu' in targets:
711            mock_params = [
712                base_params, api_params, {
713                    'has_callback_arguments': has_callback_arguments
714                }
715            ]
716            renders.append(
717                FileRender('mock_webgpu.h', 'src/dawn/mock_webgpu.h',
718                           mock_params))
719            renders.append(
720                FileRender('mock_webgpu.cpp', 'src/dawn/mock_webgpu.cpp',
721                           mock_params))
722
723        if 'dawn_native_utils' in targets:
724            frontend_params = [
725                base_params,
726                api_params,
727                {
728                    # TODO: as_frontendType and co. take a Type, not a Name :(
729                    'as_frontendType': lambda typ: as_frontendType(typ),
730                    'as_annotated_frontendType': \
731                        lambda arg: annotated(as_frontendType(arg.type), arg),
732                }
733            ]
734
735            renders.append(
736                FileRender('dawn_native/ValidationUtils.h',
737                           'src/dawn_native/ValidationUtils_autogen.h',
738                           frontend_params))
739            renders.append(
740                FileRender('dawn_native/ValidationUtils.cpp',
741                           'src/dawn_native/ValidationUtils_autogen.cpp',
742                           frontend_params))
743            renders.append(
744                FileRender('dawn_native/wgpu_structs.h',
745                           'src/dawn_native/wgpu_structs_autogen.h',
746                           frontend_params))
747            renders.append(
748                FileRender('dawn_native/wgpu_structs.cpp',
749                           'src/dawn_native/wgpu_structs_autogen.cpp',
750                           frontend_params))
751            renders.append(
752                FileRender('dawn_native/ProcTable.cpp',
753                           'src/dawn_native/ProcTable.cpp', frontend_params))
754
755        if 'dawn_wire' in targets:
756            additional_params = compute_wire_params(api_params, wire_json)
757
758            wire_params = [
759                base_params, api_params, {
760                    'as_wireType': as_wireType,
761                    'as_annotated_wireType': \
762                        lambda arg: annotated(as_wireType(arg.type), arg),
763                }, additional_params
764            ]
765            renders.append(
766                FileRender('dawn_wire/ObjectType.h',
767                           'src/dawn_wire/ObjectType_autogen.h', wire_params))
768            renders.append(
769                FileRender('dawn_wire/WireCmd.h',
770                           'src/dawn_wire/WireCmd_autogen.h', wire_params))
771            renders.append(
772                FileRender('dawn_wire/WireCmd.cpp',
773                           'src/dawn_wire/WireCmd_autogen.cpp', wire_params))
774            renders.append(
775                FileRender('dawn_wire/client/ApiObjects.h',
776                           'src/dawn_wire/client/ApiObjects_autogen.h',
777                           wire_params))
778            renders.append(
779                FileRender('dawn_wire/client/ApiProcs.cpp',
780                           'src/dawn_wire/client/ApiProcs_autogen.cpp',
781                           wire_params))
782            renders.append(
783                FileRender('dawn_wire/client/ClientBase.h',
784                           'src/dawn_wire/client/ClientBase_autogen.h',
785                           wire_params))
786            renders.append(
787                FileRender('dawn_wire/client/ClientHandlers.cpp',
788                           'src/dawn_wire/client/ClientHandlers_autogen.cpp',
789                           wire_params))
790            renders.append(
791                FileRender(
792                    'dawn_wire/client/ClientPrototypes.inc',
793                    'src/dawn_wire/client/ClientPrototypes_autogen.inc',
794                    wire_params))
795            renders.append(
796                FileRender('dawn_wire/server/ServerBase.h',
797                           'src/dawn_wire/server/ServerBase_autogen.h',
798                           wire_params))
799            renders.append(
800                FileRender('dawn_wire/server/ServerDoers.cpp',
801                           'src/dawn_wire/server/ServerDoers_autogen.cpp',
802                           wire_params))
803            renders.append(
804                FileRender('dawn_wire/server/ServerHandlers.cpp',
805                           'src/dawn_wire/server/ServerHandlers_autogen.cpp',
806                           wire_params))
807            renders.append(
808                FileRender(
809                    'dawn_wire/server/ServerPrototypes.inc',
810                    'src/dawn_wire/server/ServerPrototypes_autogen.inc',
811                    wire_params))
812
813        return renders
814
815    def get_dependencies(self, args):
816        deps = [os.path.abspath(args.dawn_json)]
817        if args.wire_json != None:
818            deps += [os.path.abspath(args.wire_json)]
819        return deps
820
821
822if __name__ == '__main__':
823    sys.exit(run_generator(MultiGeneratorFromDawnJSON()))
824