1# Copyright 2018 The ANGLE Project Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4#
5# gen_packed_gl_enums.py:
6#   Code generation for the packed enums.
7#   NOTE: don't run this script directly. Run scripts/run_code_generation.py.
8
9import datetime, json, os, sys
10from collections import namedtuple
11from collections import OrderedDict
12
13Enum = namedtuple('Enum', ['name', 'values', 'max_value'])
14EnumValue = namedtuple('EnumValue', ['name', 'gl_name', 'value'])
15
16Generators = [
17    {
18        'json': 'packed_gl_enums.json',
19        'output': 'PackedGLEnums',
20        'namespace': 'gl',
21        'enum_type': 'GLenum',
22    },
23    {
24        'json': 'packed_egl_enums.json',
25        'output': 'PackedEGLEnums',
26        'namespace': 'egl',
27        'enum_type': 'EGLenum',
28    },
29]
30
31
32def load_enums(path):
33    with open(path) as map_file:
34        enums_dict = json.loads(map_file.read(), object_pairs_hook=OrderedDict)
35
36    enums = []
37    for (enum_name, value_list) in enums_dict.iteritems():
38
39        values = []
40        i = 0
41
42        for (value_name, value_gl_name) in value_list.iteritems():
43            values.append(EnumValue(value_name, value_gl_name, i))
44            i += 1
45
46        assert (i < 255)  # This makes sure enums fit in the uint8_t
47        enums.append(Enum(enum_name, values, i))
48
49    enums.sort(key=lambda enum: enum.name)
50    return enums
51
52
53def generate_include_guard(path):
54    return path.replace(".", "_").upper()
55
56
57def header_name_from_cpp_name(path):
58    return path.replace(".cpp", ".h")
59
60
61header_template = """// GENERATED FILE - DO NOT EDIT.
62// Generated by {script_name} using data from {data_source_name}.
63//
64// Copyright {copyright_year} The ANGLE Project Authors. All rights reserved.
65// Use of this source code is governed by a BSD-style license that can be
66// found in the LICENSE file.
67//
68// {file_name}:
69//   Declares ANGLE-specific enums classes for {api_enum_name}s and functions operating
70//   on them.
71
72#ifndef COMMON_{include_guard}_
73#define COMMON_{include_guard}_
74
75#include <angle_gl.h>
76#include <EGL/egl.h>
77#include <EGL/eglext.h>
78
79#include <cstdint>
80#include <ostream>
81
82namespace {namespace}
83{{
84
85template<typename Enum>
86Enum From{api_enum_name}({api_enum_name} from);
87{content}
88}}  // namespace {namespace}
89
90#endif // COMMON_{include_guard}_
91"""
92
93enum_declaration_template = """
94enum class {enum_name} : uint8_t
95{{
96{value_declarations}
97
98    InvalidEnum = {max_value},
99    EnumCount = {max_value},
100}};
101
102template <>
103{enum_name} From{api_enum_name}<{enum_name}>({api_enum_name} from);
104{api_enum_name} To{api_enum_name}({enum_name} from);
105std::ostream &operator<<(std::ostream &os, {enum_name} value);
106"""
107
108
109def write_header(enums, path_prefix, file_name, data_source_name, namespace, api_enum_name):
110    content = ['']
111
112    for enum in enums:
113        value_declarations = []
114        for value in enum.values:
115            value_declarations.append('    ' + value.name + ' = ' + str(value.value) + ',')
116
117        content.append(
118            enum_declaration_template.format(
119                enum_name=enum.name,
120                max_value=str(enum.max_value),
121                value_declarations='\n'.join(value_declarations),
122                api_enum_name=api_enum_name))
123
124    header = header_template.format(
125        content=''.join(content),
126        copyright_year=datetime.date.today().year,
127        data_source_name=data_source_name,
128        script_name=sys.argv[0],
129        file_name=file_name,
130        include_guard=generate_include_guard(file_name),
131        namespace=namespace,
132        api_enum_name=api_enum_name)
133
134    with (open(path_prefix + file_name, 'wt')) as f:
135        f.write(header)
136
137
138cpp_template = """// GENERATED FILE - DO NOT EDIT.
139// Generated by {script_name} using data from {data_source_name}.
140//
141// Copyright {copyright_year} The ANGLE Project Authors. All rights reserved.
142// Use of this source code is governed by a BSD-style license that can be
143// found in the LICENSE file.
144//
145// {file_name}:
146//   Implements ANGLE-specific enums classes for {api_enum_name}s and functions operating
147//   on them.
148
149#include "common/debug.h"
150#include "common/{header_name}"
151
152namespace {namespace}
153{{
154{content}
155}}  // namespace {namespace}
156"""
157
158enum_implementation_template = """
159template <>
160{enum_name} From{api_enum_name}<{enum_name}>({api_enum_name} from)
161{{
162    switch (from)
163    {{
164{from_glenum_cases}
165        default:
166            return {enum_name}::InvalidEnum;
167    }}
168}}
169
170{api_enum_name} To{api_enum_name}({enum_name} from)
171{{
172    switch (from)
173    {{
174{to_glenum_cases}
175        default:
176            UNREACHABLE();
177            return 0;
178    }}
179}}
180
181std::ostream &operator<<(std::ostream &os, {enum_name} value)
182{{
183    switch (value)
184    {{
185{ostream_cases}
186        default:
187            os << "GL_INVALID_ENUM";
188            break;
189    }}
190    return os;
191}}
192"""
193
194
195def write_cpp(enums, path_prefix, file_name, data_source_name, namespace, api_enum_name):
196    content = ['']
197
198    for enum in enums:
199        from_glenum_cases = []
200        to_glenum_cases = []
201        ostream_cases = []
202        for value in enum.values:
203            qualified_name = enum.name + '::' + value.name
204            from_glenum_cases.append('        case ' + value.gl_name + ':\n            return ' +
205                                     qualified_name + ';')
206            to_glenum_cases.append('        case ' + qualified_name + ':\n            return ' +
207                                   value.gl_name + ';')
208            ostream_cases.append('        case ' + qualified_name + ':\n            os << "' +
209                                 value.gl_name + '";\n            break;')
210
211        content.append(
212            enum_implementation_template.format(
213                enum_name=enum.name,
214                from_glenum_cases='\n'.join(from_glenum_cases),
215                max_value=str(enum.max_value),
216                to_glenum_cases='\n'.join(to_glenum_cases),
217                api_enum_name=api_enum_name,
218                ostream_cases='\n'.join(ostream_cases)))
219
220    cpp = cpp_template.format(
221        content=''.join(content),
222        copyright_year=datetime.date.today().year,
223        data_source_name=data_source_name,
224        script_name=sys.argv[0],
225        file_name=file_name,
226        header_name=header_name_from_cpp_name(file_name),
227        namespace=namespace,
228        api_enum_name=api_enum_name)
229
230    with (open(path_prefix + file_name, 'wt')) as f:
231        f.write(cpp)
232
233
234def main():
235
236    # auto_script parameters.
237    if len(sys.argv) > 1:
238        inputs = []
239        outputs = []
240        for generator in Generators:
241            inputs += [generator['json']]
242            outputs += [
243                generator['output'] + '_autogen.cpp',
244                generator['output'] + '_autogen.h',
245            ]
246
247        if sys.argv[1] == 'inputs':
248            print ','.join(inputs)
249        elif sys.argv[1] == 'outputs':
250            print ','.join(outputs)
251        else:
252            print('Invalid script parameters')
253            return 1
254        return 0
255
256    path_prefix = os.path.dirname(os.path.realpath(__file__)) + os.path.sep
257
258    for generator in Generators:
259        json_file = generator['json']
260        output_file = generator['output']
261        namespace = generator['namespace']
262        enum_type = generator['enum_type']
263        enums = load_enums(path_prefix + json_file)
264        write_header(enums, path_prefix, output_file + '_autogen.h', json_file, namespace,
265                     enum_type)
266        write_cpp(enums, path_prefix, output_file + '_autogen.cpp', json_file, namespace,
267                  enum_type)
268    return 0
269
270
271if __name__ == '__main__':
272    sys.exit(main())
273