2
# Mesa 3-D graphics library
4
# Copyright (C) 2010 LunarG Inc.
6
# Permission is hereby granted, free of charge, to any person obtaining a
7
# copy of this software and associated documentation files (the "Software"),
8
# to deal in the Software without restriction, including without limitation
9
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
10
# and/or sell copies of the Software, and to permit persons to whom the
11
# Software is furnished to do so, subject to the following conditions:
13
# The above copyright notice and this permission notice shall be included
14
# in all copies or substantial portions of the Software.
16
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22
# DEALINGS IN THE SOFTWARE.
25
# Chia-I Wu <olv@lunarg.com>
28
# make it possible to import glapi
30
GLAPI = os.path.join(".", os.path.dirname(__file__), "glapi", "gen")
31
sys.path.insert(0, GLAPI)
33
from operator import attrgetter
35
from optparse import OptionParser
40
# number of dynamic entries
41
ABI_NUM_DYNAMIC_ENTRIES = 256
43
class ABIEntry(object):
44
"""Represent an ABI entry."""
46
_match_c_param = re.compile(
47
'^(?P<type>[\w\s*]+?)(?P<name>\w+)(\[(?P<array>\d+)\])?$')
49
def __init__(self, cols, attrs, xml_data = None):
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
58
def c_prototype(self):
59
return '%s %s(%s)' % (self.c_return(), self.name, self.c_params())
69
"""Return the parameter list used in the entry prototype."""
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)
76
c_params.append('void')
78
return ", ".join(c_params)
81
"""Return the argument list used in the entry invocation."""
83
for t, n, a in self.params:
86
return ", ".join(c_args)
88
def _parse(self, cols):
98
elif len(cols) == 1 and cols[0] == 'void':
102
params.append(self._parse_param(val))
108
def _parse_param(self, c_param):
109
m = self._match_c_param.match(c_param)
111
raise Exception('unrecognized param ' + c_param)
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
118
return (c_type, c_name, c_array)
121
return self.c_prototype()
123
def __lt__(self, other):
124
# compare slot, alias, and then name
125
if self.slot == other.slot:
128
elif not other.alias:
131
return self.name < other.name
133
return self.slot < other.slot
136
def abi_parse_xml(xml):
137
"""Parse a GLAPI XML file for ABI entries."""
138
api = gl_XML.parse_GL_API(xml, glX_XML.glx_item_factory())
141
for func in api.functionIterateByOffset():
142
# make sure func.name appear first
143
entry_points = func.entry_points[:]
144
entry_points.remove(func.name)
145
entry_points.insert(0, func.name)
147
for name in entry_points:
150
'hidden': not func.is_static_entry_point(name),
151
'alias': None if name == func.name else func.name,
152
'handcode': bool(func.has_different_protocol(name)),
158
alias = entry_dict[attrs['alias']]
160
raise Exception('failed to alias %s' % attrs['alias'])
162
raise Exception('recursive alias %s' % ent.name)
163
attrs['alias'] = alias
164
if attrs['handcode']:
165
attrs['handcode'] = func.static_glx_name(name)
167
attrs['handcode'] = None
169
if name in entry_dict:
170
raise Exception('%s is duplicated' % (name))
173
cols.append(func.return_type)
175
params = func.get_parameter_string(name)
176
cols.extend([p.strip() for p in params.split(',')])
178
ent = ABIEntry(cols, attrs, func)
179
entry_dict[ent.name] = ent
181
entries = sorted(entry_dict.values())
185
def abi_sanity_check(entries):
190
last_slot = entries[-1].slot
192
for slot in range(last_slot + 1):
193
if entries[i].slot != slot:
194
raise Exception('entries are not ordered by slots')
196
raise Exception('first entry of slot %d aliases %s'
197
% (slot, entries[i].alias.name))
199
while i < len(entries) and entries[i].slot == slot:
201
if not handcode and ent.handcode:
202
handcode = ent.handcode
203
elif ent.handcode != handcode:
204
raise Exception('two aliases with handcode %s != %s',
205
ent.handcode, handcode)
207
if ent.name in all_names:
208
raise Exception('%s is duplicated' % (ent.name))
209
if ent.alias and ent.alias.name not in all_names:
210
raise Exception('failed to alias %s' % (ent.alias.name))
211
all_names.append(ent.name)
214
raise Exception('there are %d invalid entries' % (len(entries) - 1))
216
class ABIPrinter(object):
219
def __init__(self, entries):
220
self.entries = entries
222
# sort entries by their names
223
self.entries_sorted_by_names = sorted(self.entries, key=attrgetter('name'))
225
self.indent = ' ' * 3
226
self.noop_warn = 'noop_warn'
227
self.noop_generic = 'noop_generic'
228
self.current_get = 'entry_current_get'
230
self.api_defines = []
231
self.api_headers = ['"KHR/khrplatform.h"']
232
self.api_call = 'KHRONOS_APICALL'
233
self.api_entry = 'KHRONOS_APIENTRY'
234
self.api_attrs = 'KHRONOS_APIATTRIBUTES'
238
self.lib_need_table_size = True
239
self.lib_need_noop_array = True
240
self.lib_need_stubs = True
241
self.lib_need_all_entries = True
242
self.lib_need_non_hidden_entries = False
245
return '/* This file is automatically generated by mapi_abi.py. Do not modify. */'
247
def c_public_includes(self):
248
"""Return includes of the client API headers."""
249
defines = ['#define ' + d for d in self.api_defines]
250
includes = ['#include ' + h for h in self.api_headers]
251
return "\n".join(defines + includes)
253
def need_entry_point(self, ent):
254
"""Return True if an entry point is needed for the entry."""
255
# non-handcode hidden aliases may share the entry they alias
256
use_alias = (ent.hidden and ent.alias and not ent.handcode)
259
def c_public_declarations(self, prefix):
260
"""Return the declarations of public entry points."""
262
for ent in self.entries:
263
if not self.need_entry_point(ent):
265
export = self.api_call if not ent.hidden else ''
266
if not ent.hidden or not self.lib_need_non_hidden_entries:
267
decls.append(self._c_decl(ent, prefix, True, export) + ';')
269
return "\n".join(decls)
271
def c_mapi_table(self):
272
"""Return defines of the dispatch table size."""
273
num_static_entries = self.entries[-1].slot + 1
274
return ('#define MAPI_TABLE_NUM_STATIC %d\n' + \
275
'#define MAPI_TABLE_NUM_DYNAMIC %d') % (
276
num_static_entries, ABI_NUM_DYNAMIC_ENTRIES)
278
def _c_function(self, ent, prefix, mangle=False, stringify=False):
279
"""Return the function name of an entry."""
281
True: { True: '%s_STR(%s)', False: '%s(%s)' },
282
False: { True: '"%s%s"', False: '%s%s' },
284
fmt = formats[prefix.isupper()][stringify]
286
if mangle and ent.hidden:
287
name = '_dispatch_stub_' + str(ent.slot)
288
return fmt % (prefix, name)
290
def _c_function_call(self, ent, prefix):
291
"""Return the function name used for calling."""
293
# _c_function does not handle this case
294
formats = { True: '%s(%s)', False: '%s%s' }
295
fmt = formats[prefix.isupper()]
296
name = fmt % (prefix, ent.handcode)
297
elif self.need_entry_point(ent):
298
name = self._c_function(ent, prefix, True)
300
name = self._c_function(ent.alias, prefix, True)
303
def _c_decl(self, ent, prefix, mangle=False, export=''):
304
"""Return the C declaration for the entry."""
305
decl = '%s %s %s(%s)' % (ent.c_return(), self.api_entry,
306
self._c_function(ent, prefix, mangle), ent.c_params())
308
decl = export + ' ' + decl
310
decl += ' ' + self.api_attrs
314
def _c_cast(self, ent):
315
"""Return the C cast for the entry."""
316
cast = '%s (%s *)(%s)' % (
317
ent.c_return(), self.api_entry, ent.c_params())
321
def c_public_dispatches(self, prefix, no_hidden):
322
"""Return the public dispatch functions."""
324
for ent in self.entries:
325
if ent.hidden and no_hidden:
328
if not self.need_entry_point(ent):
331
export = self.api_call if not ent.hidden else ''
333
proto = self._c_decl(ent, prefix, True, export)
334
cast = self._c_cast(ent)
340
stmt1 += 'const struct _glapi_table *_tbl = %s();' % (
343
stmt2 += 'mapi_func _func = ((const mapi_func *) _tbl)[%d];' % (
346
stmt3 += '%s((%s) _func)(%s);' % (ret, cast, ent.c_args())
348
disp = '%s\n{\n%s\n%s\n%s\n}' % (proto, stmt1, stmt2, stmt3)
351
disp = '#if 0\n' + disp + '\n#endif'
353
dispatches.append(disp)
355
return '\n\n'.join(dispatches)
357
def c_public_initializer(self, prefix):
358
"""Return the initializer for public dispatch functions."""
360
for ent in self.entries:
364
name = '%s(mapi_func) %s' % (self.indent,
365
self._c_function_call(ent, prefix))
368
return ',\n'.join(names)
370
def c_stub_string_pool(self):
371
"""Return the string pool for use by stubs."""
372
# sort entries by their names
373
sorted_entries = sorted(self.entries, key=attrgetter('name'))
378
for ent in sorted_entries:
380
pool.append('%s' % (ent.name))
381
count += len(ent.name) + 1
383
pool_str = self.indent + '"' + \
384
('\\0"\n' + self.indent + '"').join(pool) + '";'
385
return (pool_str, offsets)
387
def c_stub_initializer(self, prefix, pool_offsets):
388
"""Return the initializer for struct mapi_stub array."""
390
for ent in self.entries_sorted_by_names:
391
stubs.append('%s{ (void *) %d, %d, NULL }' % (
392
self.indent, pool_offsets[ent], ent.slot))
394
return ',\n'.join(stubs)
396
def c_noop_functions(self, prefix, warn_prefix):
397
"""Return the noop functions."""
399
for ent in self.entries:
403
proto = self._c_decl(ent, prefix, False, 'static')
407
for t, n, a in ent.params:
408
stmt1 += "%s(void) %s;" % (space, n)
414
stmt1 += self.indent + '%s(%s);' % (self.noop_warn,
415
self._c_function(ent, warn_prefix, False, True))
418
stmt2 = self.indent + 'return (%s) 0;' % (ent.ret)
419
noop = '%s\n{\n%s\n%s\n}' % (proto, stmt1, stmt2)
421
noop = '%s\n{\n%s\n}' % (proto, stmt1)
425
return '\n\n'.join(noops)
427
def c_noop_initializer(self, prefix, use_generic):
428
"""Return an initializer for the noop dispatch table."""
429
entries = [self._c_function(ent, prefix)
430
for ent in self.entries if not ent.alias]
432
entries = [self.noop_generic] * len(entries)
434
entries.extend([self.noop_generic] * ABI_NUM_DYNAMIC_ENTRIES)
436
pre = self.indent + '(mapi_func) '
437
return pre + (',\n' + pre).join(entries)
439
def c_asm_gcc(self, prefix, no_hidden):
442
for ent in self.entries:
443
if ent.hidden and no_hidden:
446
if not self.need_entry_point(ent):
449
name = self._c_function(ent, prefix, True, True)
455
asm.append('".hidden "%s"\\n"' % (name))
457
if ent.alias and not (ent.alias.hidden and no_hidden):
458
asm.append('".globl "%s"\\n"' % (name))
459
asm.append('".set "%s", "%s"\\n"' % (name,
460
self._c_function(ent.alias, prefix, True, True)))
462
asm.append('STUB_ASM_ENTRY(%s)"\\n"' % (name))
463
asm.append('"\\t"STUB_ASM_CODE("%d")"\\n"' % (ent.slot))
469
return "\n".join(asm)
471
def output_for_lib(self):
472
print(self.c_notice())
479
print('#ifdef MAPI_TMP_DEFINES')
480
print(self.c_public_includes())
482
print('#ifdef MemoryBarrier')
483
print('#undef MemoryBarrier')
486
print(self.c_public_declarations(self.prefix_lib))
487
print('#undef MAPI_TMP_DEFINES')
488
print('#endif /* MAPI_TMP_DEFINES */')
490
if self.lib_need_table_size:
492
print('#ifdef MAPI_TMP_TABLE')
493
print(self.c_mapi_table())
494
print('#undef MAPI_TMP_TABLE')
495
print('#endif /* MAPI_TMP_TABLE */')
497
if self.lib_need_noop_array:
499
print('#ifdef MAPI_TMP_NOOP_ARRAY')
500
print('#ifdef DEBUG')
502
print(self.c_noop_functions(self.prefix_noop, self.prefix_warn))
504
print('const mapi_func table_%s_array[] = {' % (self.prefix_noop))
505
print(self.c_noop_initializer(self.prefix_noop, False))
508
print('#else /* DEBUG */')
510
print('const mapi_func table_%s_array[] = {' % (self.prefix_noop))
511
print(self.c_noop_initializer(self.prefix_noop, True))
514
print('#endif /* DEBUG */')
515
print('#undef MAPI_TMP_NOOP_ARRAY')
516
print('#endif /* MAPI_TMP_NOOP_ARRAY */')
518
if self.lib_need_stubs:
519
pool, pool_offsets = self.c_stub_string_pool()
521
print('#ifdef MAPI_TMP_PUBLIC_STUBS')
522
print('static const char public_string_pool[] =')
525
print('static const struct mapi_stub public_stubs[] = {')
526
print(self.c_stub_initializer(self.prefix_lib, pool_offsets))
528
print('#undef MAPI_TMP_PUBLIC_STUBS')
529
print('#endif /* MAPI_TMP_PUBLIC_STUBS */')
531
if self.lib_need_all_entries:
533
print('#ifdef MAPI_TMP_PUBLIC_ENTRIES')
534
print(self.c_public_dispatches(self.prefix_lib, False))
536
print('static const mapi_func public_entries[] = {')
537
print(self.c_public_initializer(self.prefix_lib))
539
print('#undef MAPI_TMP_PUBLIC_ENTRIES')
540
print('#endif /* MAPI_TMP_PUBLIC_ENTRIES */')
543
print('#ifdef MAPI_TMP_STUB_ASM_GCC')
545
print(self.c_asm_gcc(self.prefix_lib, False))
547
print('#undef MAPI_TMP_STUB_ASM_GCC')
548
print('#endif /* MAPI_TMP_STUB_ASM_GCC */')
550
if self.lib_need_non_hidden_entries:
552
for ent in self.entries:
558
print('#ifdef MAPI_TMP_PUBLIC_ENTRIES_NO_HIDDEN')
559
print(self.c_public_dispatches(self.prefix_lib, True))
561
print('/* does not need public_entries */')
562
print('#undef MAPI_TMP_PUBLIC_ENTRIES_NO_HIDDEN')
563
print('#endif /* MAPI_TMP_PUBLIC_ENTRIES_NO_HIDDEN */')
566
print('#ifdef MAPI_TMP_STUB_ASM_GCC_NO_HIDDEN')
568
print(self.c_asm_gcc(self.prefix_lib, True))
570
print('#undef MAPI_TMP_STUB_ASM_GCC_NO_HIDDEN')
571
print('#endif /* MAPI_TMP_STUB_ASM_GCC_NO_HIDDEN */')
573
class GLAPIPrinter(ABIPrinter):
574
"""OpenGL API Printer"""
576
def __init__(self, entries):
578
self._override_for_api(ent)
579
super(GLAPIPrinter, self).__init__(entries)
581
self.api_defines = ['GL_GLEXT_PROTOTYPES']
582
self.api_headers = ['"GL/gl.h"', '"GL/glext.h"']
583
self.api_call = 'GLAPI'
584
self.api_entry = 'APIENTRY'
587
self.lib_need_table_size = False
588
self.lib_need_noop_array = False
589
self.lib_need_stubs = False
590
self.lib_need_all_entries = False
591
self.lib_need_non_hidden_entries = True
593
self.prefix_lib = 'GLAPI_PREFIX'
594
self.prefix_noop = 'noop'
595
self.prefix_warn = self.prefix_lib
597
self.c_header = self._get_c_header()
599
def _override_for_api(self, ent):
600
"""Override attributes of an entry if necessary for this
602
# By default, no override is necessary.
605
def _get_c_header(self):
606
header = """#ifndef _GLAPI_TMP_H_
607
#define _GLAPI_TMP_H_
608
#define GLAPI_PREFIX(func) gl##func
609
#define GLAPI_PREFIX_STR(func) "gl"#func
611
typedef int GLclampx;
612
#endif /* _GLAPI_TMP_H_ */"""
616
class SharedGLAPIPrinter(GLAPIPrinter):
617
"""Shared GLAPI API Printer"""
619
def __init__(self, entries):
620
super(SharedGLAPIPrinter, self).__init__(entries)
622
self.lib_need_table_size = True
623
self.lib_need_noop_array = True
624
self.lib_need_stubs = True
625
self.lib_need_all_entries = True
626
self.lib_need_non_hidden_entries = False
628
self.prefix_lib = 'shared'
629
self.prefix_warn = 'gl'
631
def _override_for_api(self, ent):
635
def _get_c_header(self):
636
header = """#ifndef _GLAPI_TMP_H_
637
#define _GLAPI_TMP_H_
638
typedef int GLclampx;
639
#endif /* _GLAPI_TMP_H_ */"""
644
printers = ['glapi', 'es1api', 'es2api', 'shared-glapi']
646
parser = OptionParser(usage='usage: %prog [options] <xml_file>')
647
parser.add_option('-p', '--printer', dest='printer',
648
help='printer to use: %s' % (", ".join(printers)))
650
options, args = parser.parse_args()
651
if not args or options.printer not in printers:
655
if not args[0].endswith('.xml'):
659
return (args[0], options)
663
'glapi': GLAPIPrinter,
664
'shared-glapi': SharedGLAPIPrinter,
667
filename, options = parse_args()
669
entries = abi_parse_xml(filename)
670
abi_sanity_check(entries)
672
printer = printers[options.printer](entries)
673
printer.output_for_lib()
675
if __name__ == '__main__':