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