1#!/usr/bin/env python
2
3# Mesa 3-D graphics library
4#
5# Copyright (C) 2010 LunarG Inc.
6#
7# Permission is hereby granted, free of charge, to any person obtaining a
8# copy of this software and associated documentation files (the "Software"),
9# to deal in the Software without restriction, including without limitation
10# the rights to use, copy, modify, merge, publish, distribute, sublicense,
11# and/or sell copies of the Software, and to permit persons to whom the
12# Software is furnished to do so, subject to the following conditions:
13#
14# The above copyright notice and this permission notice shall be included
15# in all copies or substantial portions of the Software.
16#
17# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
20# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23# DEALINGS IN THE SOFTWARE.
24#
25# Authors:
26#    Chia-I Wu <olv@lunarg.com>
27
28import sys
29# make it possible to import glapi
30import os
31GLAPI = os.path.join(".", os.path.dirname(sys.argv[0]), "glapi/gen")
32sys.path.append(GLAPI)
33
34import re
35from optparse import OptionParser
36import gl_XML
37import glX_XML
38
39
40# number of dynamic entries
41ABI_NUM_DYNAMIC_ENTRIES = 256
42
43class ABIEntry(object):
44    """Represent an ABI entry."""
45
46    _match_c_param = re.compile(
47            '^(?P<type>[\w\s*]+?)(?P<name>\w+)(\[(?P<array>\d+)\])?$')
48
49    def __init__(self, cols, attrs, xml_data = None):
50        self._parse(cols)
51
52        self.slot = attrs['slot']
53        self.hidden = attrs['hidden']
54        self.alias = attrs['alias']
55        self.handcode = attrs['handcode']
56        self.xml_data = xml_data
57
58    def c_prototype(self):
59        return '%s %s(%s)' % (self.c_return(), self.name, self.c_params())
60
61    def c_return(self):
62        ret = self.ret
63        if not ret:
64            ret = 'void'
65
66        return ret
67
68    def c_params(self):
69        """Return the parameter list used in the entry prototype."""
70        c_params = []
71        for t, n, a in self.params:
72            sep = '' if t.endswith('*') else ' '
73            arr = '[%d]' % a if a else ''
74            c_params.append(t + sep + n + arr)
75        if not c_params:
76            c_params.append('void')
77
78        return ", ".join(c_params)
79
80    def c_args(self):
81        """Return the argument list used in the entry invocation."""
82        c_args = []
83        for t, n, a in self.params:
84            c_args.append(n)
85
86        return ", ".join(c_args)
87
88    def _parse(self, cols):
89        ret = cols.pop(0)
90        if ret == 'void':
91            ret = None
92
93        name = cols.pop(0)
94
95        params = []
96        if not cols:
97            raise Exception(cols)
98        elif len(cols) == 1 and cols[0] == 'void':
99            pass
100        else:
101            for val in cols:
102                params.append(self._parse_param(val))
103
104        self.ret = ret
105        self.name = name
106        self.params = params
107
108    def _parse_param(self, c_param):
109        m = self._match_c_param.match(c_param)
110        if not m:
111            raise Exception('unrecognized param ' + c_param)
112
113        c_type = m.group('type').strip()
114        c_name = m.group('name')
115        c_array = m.group('array')
116        c_array = int(c_array) if c_array else 0
117
118        return (c_type, c_name, c_array)
119
120    def __str__(self):
121        return self.c_prototype()
122
123    def __cmp__(self, other):
124        # compare slot, alias, and then name
125        res = cmp(self.slot, other.slot)
126        if not res:
127            if not self.alias:
128                res = -1
129            elif not other.alias:
130                res = 1
131
132            if not res:
133                res = cmp(self.name, other.name)
134
135        return res
136
137def abi_parse_xml(xml):
138    """Parse a GLAPI XML file for ABI entries."""
139    api = gl_XML.parse_GL_API(xml, glX_XML.glx_item_factory())
140
141    entry_dict = {}
142    for func in api.functionIterateByOffset():
143        # make sure func.name appear first
144        entry_points = func.entry_points[:]
145        entry_points.remove(func.name)
146        entry_points.insert(0, func.name)
147
148        for name in entry_points:
149            attrs = {
150                    'slot': func.offset,
151                    'hidden': not func.is_static_entry_point(name),
152                    'alias': None if name == func.name else func.name,
153                    'handcode': bool(func.has_different_protocol(name)),
154            }
155
156            # post-process attrs
157            if attrs['alias']:
158                try:
159                    alias = entry_dict[attrs['alias']]
160                except KeyError:
161                    raise Exception('failed to alias %s' % attrs['alias'])
162                if alias.alias:
163                    raise Exception('recursive alias %s' % ent.name)
164                attrs['alias'] = alias
165            if attrs['handcode']:
166                attrs['handcode'] = func.static_glx_name(name)
167            else:
168                attrs['handcode'] = None
169
170            if entry_dict.has_key(name):
171                raise Exception('%s is duplicated' % (name))
172
173            cols = []
174            cols.append(func.return_type)
175            cols.append(name)
176            params = func.get_parameter_string(name)
177            cols.extend([p.strip() for p in params.split(',')])
178
179            ent = ABIEntry(cols, attrs, func)
180            entry_dict[ent.name] = ent
181
182    entries = entry_dict.values()
183    entries.sort()
184
185    return entries
186
187def abi_parse_line(line):
188    cols = [col.strip() for col in line.split(',')]
189
190    attrs = {
191            'slot': -1,
192            'hidden': False,
193            'alias': None,
194            'handcode': None,
195    }
196
197    # extract attributes from the first column
198    vals = cols[0].split(':')
199    while len(vals) > 1:
200        val = vals.pop(0)
201        if val.startswith('slot='):
202            attrs['slot'] = int(val[5:])
203        elif val == 'hidden':
204            attrs['hidden'] = True
205        elif val.startswith('alias='):
206            attrs['alias'] = val[6:]
207        elif val.startswith('handcode='):
208            attrs['handcode'] = val[9:]
209        elif not val:
210            pass
211        else:
212            raise Exception('unknown attribute %s' % val)
213    cols[0] = vals[0]
214
215    return (attrs, cols)
216
217def abi_parse(filename):
218    """Parse a CSV file for ABI entries."""
219    fp = open(filename) if filename != '-' else sys.stdin
220    lines = [line.strip() for line in fp.readlines()
221            if not line.startswith('#') and line.strip()]
222
223    entry_dict = {}
224    next_slot = 0
225    for line in lines:
226        attrs, cols = abi_parse_line(line)
227
228        # post-process attributes
229        if attrs['alias']:
230            try:
231                alias = entry_dict[attrs['alias']]
232            except KeyError:
233                raise Exception('failed to alias %s' % attrs['alias'])
234            if alias.alias:
235                raise Exception('recursive alias %s' % ent.name)
236            slot = alias.slot
237            attrs['alias'] = alias
238        else:
239            slot = next_slot
240            next_slot += 1
241
242        if attrs['slot'] < 0:
243            attrs['slot'] = slot
244        elif attrs['slot'] != slot:
245            raise Exception('invalid slot in %s' % (line))
246
247        ent = ABIEntry(cols, attrs)
248        if entry_dict.has_key(ent.name):
249            raise Exception('%s is duplicated' % (ent.name))
250        entry_dict[ent.name] = ent
251
252    entries = entry_dict.values()
253    entries.sort()
254
255    return entries
256
257def abi_sanity_check(entries):
258    if not entries:
259        return
260
261    all_names = []
262    last_slot = entries[-1].slot
263    i = 0
264    for slot in xrange(last_slot + 1):
265        if entries[i].slot != slot:
266            raise Exception('entries are not ordered by slots')
267        if entries[i].alias:
268            raise Exception('first entry of slot %d aliases %s'
269                    % (slot, entries[i].alias.name))
270        handcode = None
271        while i < len(entries) and entries[i].slot == slot:
272            ent = entries[i]
273            if not handcode and ent.handcode:
274                handcode = ent.handcode
275            elif ent.handcode != handcode:
276                raise Exception('two aliases with handcode %s != %s',
277                        ent.handcode, handcode)
278
279            if ent.name in all_names:
280                raise Exception('%s is duplicated' % (ent.name))
281            if ent.alias and ent.alias.name not in all_names:
282                raise Exception('failed to alias %s' % (ent.alias.name))
283            all_names.append(ent.name)
284            i += 1
285    if i < len(entries):
286        raise Exception('there are %d invalid entries' % (len(entries) - 1))
287
288class ABIPrinter(object):
289    """MAPI Printer"""
290
291    def __init__(self, entries):
292        self.entries = entries
293
294        # sort entries by their names
295        self.entries_sorted_by_names = self.entries[:]
296        self.entries_sorted_by_names.sort(lambda x, y: cmp(x.name, y.name))
297
298        self.indent = ' ' * 3
299        self.noop_warn = 'noop_warn'
300        self.noop_generic = 'noop_generic'
301        self.current_get = 'entry_current_get'
302
303        self.api_defines = []
304        self.api_headers = ['"KHR/khrplatform.h"']
305        self.api_call = 'KHRONOS_APICALL'
306        self.api_entry = 'KHRONOS_APIENTRY'
307        self.api_attrs = 'KHRONOS_APIATTRIBUTES'
308
309        self.c_header = ''
310
311        self.lib_need_table_size = True
312        self.lib_need_noop_array = True
313        self.lib_need_stubs = True
314        self.lib_need_all_entries = True
315        self.lib_need_non_hidden_entries = False
316
317    def c_notice(self):
318        return '/* This file is automatically generated by mapi_abi.py.  Do not modify. */'
319
320    def c_public_includes(self):
321        """Return includes of the client API headers."""
322        defines = ['#define ' + d for d in self.api_defines]
323        includes = ['#include ' + h for h in self.api_headers]
324        return "\n".join(defines + includes)
325
326    def need_entry_point(self, ent):
327        """Return True if an entry point is needed for the entry."""
328        # non-handcode hidden aliases may share the entry they alias
329        use_alias = (ent.hidden and ent.alias and not ent.handcode)
330        return not use_alias
331
332    def c_public_declarations(self, prefix):
333        """Return the declarations of public entry points."""
334        decls = []
335        for ent in self.entries:
336            if not self.need_entry_point(ent):
337                continue
338            export = self.api_call if not ent.hidden else ''
339            decls.append(self._c_decl(ent, prefix, True, export) + ';')
340
341        return "\n".join(decls)
342
343    def c_mapi_table(self):
344        """Return defines of the dispatch table size."""
345        num_static_entries = self.entries[-1].slot + 1
346        return ('#define MAPI_TABLE_NUM_STATIC %d\n' + \
347                '#define MAPI_TABLE_NUM_DYNAMIC %d') % (
348                        num_static_entries, ABI_NUM_DYNAMIC_ENTRIES)
349
350    def c_mapi_table_initializer(self, prefix):
351        """Return the array initializer for mapi_table_fill."""
352        entries = [self._c_function(ent, prefix)
353                for ent in self.entries if not ent.alias]
354        pre = self.indent + '(mapi_proc) '
355        return pre + (',\n' + pre).join(entries)
356
357    def c_mapi_table_spec(self):
358        """Return the spec for mapi_init."""
359        specv1 = []
360        line = '"1'
361        for ent in self.entries:
362            if not ent.alias:
363                line += '\\0"\n'
364                specv1.append(line)
365                line = '"'
366            line += '%s\\0' % ent.name
367        line += '";'
368        specv1.append(line)
369
370        return self.indent + self.indent.join(specv1)
371
372    def _c_function(self, ent, prefix, mangle=False, stringify=False):
373        """Return the function name of an entry."""
374        formats = {
375                True: { True: '%s_STR(%s)', False: '%s(%s)' },
376                False: { True: '"%s%s"', False: '%s%s' },
377        }
378        fmt = formats[prefix.isupper()][stringify]
379        name = ent.name
380        if mangle and ent.hidden:
381            name = '_dispatch_stub_' + str(ent.slot)
382        return fmt % (prefix, name)
383
384    def _c_function_call(self, ent, prefix):
385        """Return the function name used for calling."""
386        if ent.handcode:
387            # _c_function does not handle this case
388            formats = { True: '%s(%s)', False: '%s%s' }
389            fmt = formats[prefix.isupper()]
390            name = fmt % (prefix, ent.handcode)
391        elif self.need_entry_point(ent):
392            name = self._c_function(ent, prefix, True)
393        else:
394            name = self._c_function(ent.alias, prefix, True)
395        return name
396
397    def _c_decl(self, ent, prefix, mangle=False, export=''):
398        """Return the C declaration for the entry."""
399        decl = '%s %s %s(%s)' % (ent.c_return(), self.api_entry,
400                self._c_function(ent, prefix, mangle), ent.c_params())
401        if export:
402            decl = export + ' ' + decl
403        if self.api_attrs:
404            decl += ' ' + self.api_attrs
405
406        return decl
407
408    def _c_cast(self, ent):
409        """Return the C cast for the entry."""
410        cast = '%s (%s *)(%s)' % (
411                ent.c_return(), self.api_entry, ent.c_params())
412
413        return cast
414
415    def c_private_declarations(self, prefix):
416        """Return the declarations of private functions."""
417        decls = [self._c_decl(ent, prefix) + ';'
418                for ent in self.entries if not ent.alias]
419
420        return "\n".join(decls)
421
422    def c_public_dispatches(self, prefix, no_hidden):
423        """Return the public dispatch functions."""
424        dispatches = []
425        for ent in self.entries:
426            if ent.hidden and no_hidden:
427                continue
428
429            if not self.need_entry_point(ent):
430                continue
431
432            export = self.api_call if not ent.hidden else ''
433
434            proto = self._c_decl(ent, prefix, True, export)
435            cast = self._c_cast(ent)
436
437            ret = ''
438            if ent.ret:
439                ret = 'return '
440            stmt1 = self.indent
441            stmt1 += 'const struct mapi_table *_tbl = %s();' % (
442                    self.current_get)
443            stmt2 = self.indent
444            stmt2 += 'mapi_func _func = ((const mapi_func *) _tbl)[%d];' % (
445                    ent.slot)
446            stmt3 = self.indent
447            stmt3 += '%s((%s) _func)(%s);' % (ret, cast, ent.c_args())
448
449            disp = '%s\n{\n%s\n%s\n%s\n}' % (proto, stmt1, stmt2, stmt3)
450
451            if ent.handcode:
452                disp = '#if 0\n' + disp + '\n#endif'
453
454            dispatches.append(disp)
455
456        return '\n\n'.join(dispatches)
457
458    def c_public_initializer(self, prefix):
459        """Return the initializer for public dispatch functions."""
460        names = []
461        for ent in self.entries:
462            if ent.alias:
463                continue
464
465            name = '%s(mapi_func) %s' % (self.indent,
466                    self._c_function_call(ent, prefix))
467            names.append(name)
468
469        return ',\n'.join(names)
470
471    def c_stub_string_pool(self):
472        """Return the string pool for use by stubs."""
473        # sort entries by their names
474        sorted_entries = self.entries[:]
475        sorted_entries.sort(lambda x, y: cmp(x.name, y.name))
476
477        pool = []
478        offsets = {}
479        count = 0
480        for ent in sorted_entries:
481            offsets[ent] = count
482            pool.append('%s' % (ent.name))
483            count += len(ent.name) + 1
484
485        pool_str =  self.indent + '"' + \
486                ('\\0"\n' + self.indent + '"').join(pool) + '";'
487        return (pool_str, offsets)
488
489    def c_stub_initializer(self, prefix, pool_offsets):
490        """Return the initializer for struct mapi_stub array."""
491        stubs = []
492        for ent in self.entries_sorted_by_names:
493            stubs.append('%s{ (void *) %d, %d, NULL }' % (
494                self.indent, pool_offsets[ent], ent.slot))
495
496        return ',\n'.join(stubs)
497
498    def c_noop_functions(self, prefix, warn_prefix):
499        """Return the noop functions."""
500        noops = []
501        for ent in self.entries:
502            if ent.alias:
503                continue
504
505            proto = self._c_decl(ent, prefix, False, 'static')
506
507            stmt1 = self.indent;
508            space = ''
509            for t, n, a in ent.params:
510                stmt1 += "%s(void) %s;" % (space, n)
511                space = ' '
512
513            if ent.params:
514                stmt1 += '\n';
515
516            stmt1 += self.indent + '%s(%s);' % (self.noop_warn,
517                    self._c_function(ent, warn_prefix, False, True))
518
519            if ent.ret:
520                stmt2 = self.indent + 'return (%s) 0;' % (ent.ret)
521                noop = '%s\n{\n%s\n%s\n}' % (proto, stmt1, stmt2)
522            else:
523                noop = '%s\n{\n%s\n}' % (proto, stmt1)
524
525            noops.append(noop)
526
527        return '\n\n'.join(noops)
528
529    def c_noop_initializer(self, prefix, use_generic):
530        """Return an initializer for the noop dispatch table."""
531        entries = [self._c_function(ent, prefix)
532                for ent in self.entries if not ent.alias]
533        if use_generic:
534            entries = [self.noop_generic] * len(entries)
535
536        entries.extend([self.noop_generic] * ABI_NUM_DYNAMIC_ENTRIES)
537
538        pre = self.indent + '(mapi_func) '
539        return pre + (',\n' + pre).join(entries)
540
541    def c_asm_gcc(self, prefix, no_hidden):
542        asm = []
543
544        for ent in self.entries:
545            if ent.hidden and no_hidden:
546                continue
547
548            if not self.need_entry_point(ent):
549                continue
550
551            name = self._c_function(ent, prefix, True, True)
552
553            if ent.handcode:
554                asm.append('#if 0')
555
556            if ent.hidden:
557                asm.append('".hidden "%s"\\n"' % (name))
558
559            if ent.alias and not (ent.alias.hidden and no_hidden):
560                asm.append('".globl "%s"\\n"' % (name))
561                asm.append('".set "%s", "%s"\\n"' % (name,
562                    self._c_function(ent.alias, prefix, True, True)))
563            else:
564                asm.append('STUB_ASM_ENTRY(%s)"\\n"' % (name))
565                asm.append('"\\t"STUB_ASM_CODE("%d")"\\n"' % (ent.slot))
566
567            if ent.handcode:
568                asm.append('#endif')
569            asm.append('')
570
571        return "\n".join(asm)
572
573    def output_for_lib(self):
574        print self.c_notice()
575
576        if self.c_header:
577            print
578            print self.c_header
579
580        print
581        print '#ifdef MAPI_TMP_DEFINES'
582        print self.c_public_includes()
583        print
584        print self.c_public_declarations(self.prefix_lib)
585        print '#undef MAPI_TMP_DEFINES'
586        print '#endif /* MAPI_TMP_DEFINES */'
587
588        if self.lib_need_table_size:
589            print
590            print '#ifdef MAPI_TMP_TABLE'
591            print self.c_mapi_table()
592            print '#undef MAPI_TMP_TABLE'
593            print '#endif /* MAPI_TMP_TABLE */'
594
595        if self.lib_need_noop_array:
596            print
597            print '#ifdef MAPI_TMP_NOOP_ARRAY'
598            print '#ifdef DEBUG'
599            print
600            print self.c_noop_functions(self.prefix_noop, self.prefix_warn)
601            print
602            print 'const mapi_func table_%s_array[] = {' % (self.prefix_noop)
603            print self.c_noop_initializer(self.prefix_noop, False)
604            print '};'
605            print
606            print '#else /* DEBUG */'
607            print
608            print 'const mapi_func table_%s_array[] = {' % (self.prefix_noop)
609            print self.c_noop_initializer(self.prefix_noop, True)
610            print '};'
611            print
612            print '#endif /* DEBUG */'
613            print '#undef MAPI_TMP_NOOP_ARRAY'
614            print '#endif /* MAPI_TMP_NOOP_ARRAY */'
615
616        if self.lib_need_stubs:
617            pool, pool_offsets = self.c_stub_string_pool()
618            print
619            print '#ifdef MAPI_TMP_PUBLIC_STUBS'
620            print 'static const char public_string_pool[] ='
621            print pool
622            print
623            print 'static const struct mapi_stub public_stubs[] = {'
624            print self.c_stub_initializer(self.prefix_lib, pool_offsets)
625            print '};'
626            print '#undef MAPI_TMP_PUBLIC_STUBS'
627            print '#endif /* MAPI_TMP_PUBLIC_STUBS */'
628
629        if self.lib_need_all_entries:
630            print
631            print '#ifdef MAPI_TMP_PUBLIC_ENTRIES'
632            print self.c_public_dispatches(self.prefix_lib, False)
633            print
634            print 'static const mapi_func public_entries[] = {'
635            print self.c_public_initializer(self.prefix_lib)
636            print '};'
637            print '#undef MAPI_TMP_PUBLIC_ENTRIES'
638            print '#endif /* MAPI_TMP_PUBLIC_ENTRIES */'
639
640            print
641            print '#ifdef MAPI_TMP_STUB_ASM_GCC'
642            print '__asm__('
643            print self.c_asm_gcc(self.prefix_lib, False)
644            print ');'
645            print '#undef MAPI_TMP_STUB_ASM_GCC'
646            print '#endif /* MAPI_TMP_STUB_ASM_GCC */'
647
648        if self.lib_need_non_hidden_entries:
649            all_hidden = True
650            for ent in self.entries:
651                if not ent.hidden:
652                    all_hidden = False
653                    break
654            if not all_hidden:
655                print
656                print '#ifdef MAPI_TMP_PUBLIC_ENTRIES_NO_HIDDEN'
657                print self.c_public_dispatches(self.prefix_lib, True)
658                print
659                print '/* does not need public_entries */'
660                print '#undef MAPI_TMP_PUBLIC_ENTRIES_NO_HIDDEN'
661                print '#endif /* MAPI_TMP_PUBLIC_ENTRIES_NO_HIDDEN */'
662
663                print
664                print '#ifdef MAPI_TMP_STUB_ASM_GCC_NO_HIDDEN'
665                print '__asm__('
666                print self.c_asm_gcc(self.prefix_lib, True)
667                print ');'
668                print '#undef MAPI_TMP_STUB_ASM_GCC_NO_HIDDEN'
669                print '#endif /* MAPI_TMP_STUB_ASM_GCC_NO_HIDDEN */'
670
671    def output_for_app(self):
672        print self.c_notice()
673        print
674        print self.c_private_declarations(self.prefix_app)
675        print
676        print '#ifdef API_TMP_DEFINE_SPEC'
677        print
678        print 'static const char %s_spec[] =' % (self.prefix_app)
679        print self.c_mapi_table_spec()
680        print
681        print 'static const mapi_proc %s_procs[] = {' % (self.prefix_app)
682        print self.c_mapi_table_initializer(self.prefix_app)
683        print '};'
684        print
685        print '#endif /* API_TMP_DEFINE_SPEC */'
686
687class GLAPIPrinter(ABIPrinter):
688    """OpenGL API Printer"""
689
690    def __init__(self, entries):
691        for ent in entries:
692            self._override_for_api(ent)
693        super(GLAPIPrinter, self).__init__(entries)
694
695        self.api_defines = ['GL_GLEXT_PROTOTYPES']
696        self.api_headers = ['"GL/gl.h"', '"GL/glext.h"']
697        self.api_call = 'GLAPI'
698        self.api_entry = 'APIENTRY'
699        self.api_attrs = ''
700
701        self.lib_need_table_size = False
702        self.lib_need_noop_array = False
703        self.lib_need_stubs = False
704        self.lib_need_all_entries = False
705        self.lib_need_non_hidden_entries = True
706
707        self.prefix_lib = 'GLAPI_PREFIX'
708        self.prefix_app = '_mesa_'
709        self.prefix_noop = 'noop'
710        self.prefix_warn = self.prefix_lib
711
712        self.c_header = self._get_c_header()
713
714    def _override_for_api(self, ent):
715        """Override attributes of an entry if necessary for this
716        printer."""
717        # By default, no override is necessary.
718        pass
719
720    def _get_c_header(self):
721        header = """#ifndef _GLAPI_TMP_H_
722#define _GLAPI_TMP_H_
723#ifdef USE_MGL_NAMESPACE
724#define GLAPI_PREFIX(func)  mgl##func
725#define GLAPI_PREFIX_STR(func)  "mgl"#func
726#else
727#define GLAPI_PREFIX(func)  gl##func
728#define GLAPI_PREFIX_STR(func)  "gl"#func
729#endif /* USE_MGL_NAMESPACE */
730
731typedef int GLclampx;
732#endif /* _GLAPI_TMP_H_ */"""
733
734        return header
735
736class ES1APIPrinter(GLAPIPrinter):
737    """OpenGL ES 1.x API Printer"""
738
739    def __init__(self, entries):
740        super(ES1APIPrinter, self).__init__(entries)
741        self.prefix_lib = 'gl'
742        self.prefix_warn = 'gl'
743
744    def _override_for_api(self, ent):
745        if ent.xml_data is None:
746            raise Exception('ES2 API printer requires XML input')
747        ent.hidden = (ent.name not in \
748            ent.xml_data.entry_points_for_api_version('es1')) \
749            or ent.hidden
750        ent.handcode = False
751
752    def _get_c_header(self):
753        header = """#ifndef _GLAPI_TMP_H_
754#define _GLAPI_TMP_H_
755typedef int GLclampx;
756#endif /* _GLAPI_TMP_H_ */"""
757
758        return header
759
760class ES2APIPrinter(GLAPIPrinter):
761    """OpenGL ES 2.x API Printer"""
762
763    def __init__(self, entries):
764        super(ES2APIPrinter, self).__init__(entries)
765        self.prefix_lib = 'gl'
766        self.prefix_warn = 'gl'
767
768    def _override_for_api(self, ent):
769        if ent.xml_data is None:
770            raise Exception('ES2 API printer requires XML input')
771        ent.hidden = (ent.name not in \
772            ent.xml_data.entry_points_for_api_version('es2')) \
773            or ent.hidden
774
775        # This is hella ugly.  The same-named function in desktop OpenGL is
776        # hidden, but it needs to be exposed by libGLESv2 for OpenGL ES 3.0.
777        # There's no way to express in the XML that a function should be be
778        # hidden in one API but exposed in another.
779        if ent.name == 'GetInternalformativ':
780            ent.hidden = False
781
782        ent.handcode = False
783
784    def _get_c_header(self):
785        header = """#ifndef _GLAPI_TMP_H_
786#define _GLAPI_TMP_H_
787typedef int GLclampx;
788#endif /* _GLAPI_TMP_H_ */"""
789
790        return header
791
792class SharedGLAPIPrinter(GLAPIPrinter):
793    """Shared GLAPI API Printer"""
794
795    def __init__(self, entries):
796        super(SharedGLAPIPrinter, self).__init__(entries)
797
798        self.lib_need_table_size = True
799        self.lib_need_noop_array = True
800        self.lib_need_stubs = True
801        self.lib_need_all_entries = True
802        self.lib_need_non_hidden_entries = False
803
804        self.prefix_lib = 'shared'
805        self.prefix_warn = 'gl'
806
807    def _override_for_api(self, ent):
808        ent.hidden = True
809        ent.handcode = False
810
811    def _get_c_header(self):
812        header = """#ifndef _GLAPI_TMP_H_
813#define _GLAPI_TMP_H_
814typedef int GLclampx;
815#endif /* _GLAPI_TMP_H_ */"""
816
817        return header
818
819def parse_args():
820    printers = ['glapi', 'es1api', 'es2api', 'shared-glapi']
821    modes = ['lib', 'app']
822
823    parser = OptionParser(usage='usage: %prog [options] <filename>')
824    parser.add_option('-p', '--printer', dest='printer',
825            help='printer to use: %s' % (", ".join(printers)))
826    parser.add_option('-m', '--mode', dest='mode',
827            help='target user: %s' % (", ".join(modes)))
828
829    options, args = parser.parse_args()
830    if not args or options.printer not in printers or \
831            options.mode not in modes:
832        parser.print_help()
833        sys.exit(1)
834
835    return (args[0], options)
836
837def main():
838    printers = {
839        'glapi': GLAPIPrinter,
840        'es1api': ES1APIPrinter,
841        'es2api': ES2APIPrinter,
842        'shared-glapi': SharedGLAPIPrinter,
843    }
844
845    filename, options = parse_args()
846
847    if filename.endswith('.xml'):
848        entries = abi_parse_xml(filename)
849    else:
850        entries = abi_parse(filename)
851    abi_sanity_check(entries)
852
853    printer = printers[options.printer](entries)
854    if options.mode == 'lib':
855        printer.output_for_lib()
856    else:
857        printer.output_for_app()
858
859if __name__ == '__main__':
860    main()
861