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>
27 from __future__
import print_function
30 # make it possible to import glapi
32 GLAPI
= os
.path
.join(".", os
.path
.dirname(__file__
), "glapi", "gen")
33 sys
.path
.insert(0, GLAPI
)
35 from operator
import attrgetter
37 from optparse
import OptionParser
42 # number of dynamic entries
43 ABI_NUM_DYNAMIC_ENTRIES
= 256
45 class ABIEntry(object):
46 """Represent an ABI entry."""
48 _match_c_param
= re
.compile(
49 '^(?P<type>[\w\s*]+?)(?P<name>\w+)(\[(?P<array>\d+)\])?$')
51 def __init__(self
, cols
, attrs
, xml_data
= None):
54 self
.slot
= attrs
['slot']
55 self
.hidden
= attrs
['hidden']
56 self
.alias
= attrs
['alias']
57 self
.handcode
= attrs
['handcode']
58 self
.xml_data
= xml_data
60 def c_prototype(self
):
61 return '%s %s(%s)' % (self
.c_return(), self
.name
, self
.c_params())
71 """Return the parameter list used in the entry prototype."""
73 for t
, n
, a
in self
.params
:
74 sep
= '' if t
.endswith('*') else ' '
75 arr
= '[%d]' % a
if a
else ''
76 c_params
.append(t
+ sep
+ n
+ arr
)
78 c_params
.append('void')
80 return ", ".join(c_params
)
83 """Return the argument list used in the entry invocation."""
85 for t
, n
, a
in self
.params
:
88 return ", ".join(c_args
)
90 def _parse(self
, cols
):
100 elif len(cols
) == 1 and cols
[0] == 'void':
104 params
.append(self
._parse
_param
(val
))
110 def _parse_param(self
, c_param
):
111 m
= self
._match
_c
_param
.match(c_param
)
113 raise Exception('unrecognized param ' + c_param
)
115 c_type
= m
.group('type').strip()
116 c_name
= m
.group('name')
117 c_array
= m
.group('array')
118 c_array
= int(c_array
) if c_array
else 0
120 return (c_type
, c_name
, c_array
)
123 return self
.c_prototype()
125 def __lt__(self
, other
):
126 # compare slot, alias, and then name
127 if self
.slot
== other
.slot
:
130 elif not other
.alias
:
133 return self
.name
< other
.name
135 return self
.slot
< other
.slot
138 def abi_parse_xml(xml
):
139 """Parse a GLAPI XML file for ABI entries."""
140 api
= gl_XML
.parse_GL_API(xml
, glX_XML
.glx_item_factory())
143 for func
in api
.functionIterateByOffset():
144 # make sure func.name appear first
145 entry_points
= func
.entry_points
[:]
146 entry_points
.remove(func
.name
)
147 entry_points
.insert(0, func
.name
)
149 for name
in entry_points
:
152 'hidden': not func
.is_static_entry_point(name
),
153 'alias': None if name
== func
.name
else func
.name
,
154 'handcode': bool(func
.has_different_protocol(name
)),
160 alias
= entry_dict
[attrs
['alias']]
162 raise Exception('failed to alias %s' % attrs
['alias'])
164 raise Exception('recursive alias %s' % ent
.name
)
165 attrs
['alias'] = alias
166 if attrs
['handcode']:
167 attrs
['handcode'] = func
.static_glx_name(name
)
169 attrs
['handcode'] = None
171 if name
in entry_dict
:
172 raise Exception('%s is duplicated' % (name
))
175 cols
.append(func
.return_type
)
177 params
= func
.get_parameter_string(name
)
178 cols
.extend([p
.strip() for p
in params
.split(',')])
180 ent
= ABIEntry(cols
, attrs
, func
)
181 entry_dict
[ent
.name
] = ent
183 entries
= sorted(entry_dict
.values())
187 def abi_sanity_check(entries
):
192 last_slot
= entries
[-1].slot
194 for slot
in range(last_slot
+ 1):
195 if entries
[i
].slot
!= slot
:
196 raise Exception('entries are not ordered by slots')
198 raise Exception('first entry of slot %d aliases %s'
199 % (slot
, entries
[i
].alias
.name
))
201 while i
< len(entries
) and entries
[i
].slot
== slot
:
203 if not handcode
and ent
.handcode
:
204 handcode
= ent
.handcode
205 elif ent
.handcode
!= handcode
:
206 raise Exception('two aliases with handcode %s != %s',
207 ent
.handcode
, handcode
)
209 if ent
.name
in all_names
:
210 raise Exception('%s is duplicated' % (ent
.name
))
211 if ent
.alias
and ent
.alias
.name
not in all_names
:
212 raise Exception('failed to alias %s' % (ent
.alias
.name
))
213 all_names
.append(ent
.name
)
216 raise Exception('there are %d invalid entries' % (len(entries
) - 1))
218 class ABIPrinter(object):
221 def __init__(self
, entries
):
222 self
.entries
= entries
224 # sort entries by their names
225 self
.entries_sorted_by_names
= sorted(self
.entries
, key
=attrgetter('name'))
227 self
.indent
= ' ' * 3
228 self
.noop_warn
= 'noop_warn'
229 self
.noop_generic
= 'noop_generic'
230 self
.current_get
= 'entry_current_get'
232 self
.api_defines
= []
233 self
.api_headers
= ['"KHR/khrplatform.h"']
234 self
.api_call
= 'KHRONOS_APICALL'
235 self
.api_entry
= 'KHRONOS_APIENTRY'
236 self
.api_attrs
= 'KHRONOS_APIATTRIBUTES'
240 self
.lib_need_table_size
= True
241 self
.lib_need_noop_array
= True
242 self
.lib_need_stubs
= True
243 self
.lib_need_all_entries
= True
244 self
.lib_need_non_hidden_entries
= False
247 return '/* This file is automatically generated by mapi_abi.py. Do not modify. */'
249 def c_public_includes(self
):
250 """Return includes of the client API headers."""
251 defines
= ['#define ' + d
for d
in self
.api_defines
]
252 includes
= ['#include ' + h
for h
in self
.api_headers
]
253 return "\n".join(defines
+ includes
)
255 def need_entry_point(self
, ent
):
256 """Return True if an entry point is needed for the entry."""
257 # non-handcode hidden aliases may share the entry they alias
258 use_alias
= (ent
.hidden
and ent
.alias
and not ent
.handcode
)
261 def c_public_declarations(self
, prefix
):
262 """Return the declarations of public entry points."""
264 for ent
in self
.entries
:
265 if not self
.need_entry_point(ent
):
267 export
= self
.api_call
if not ent
.hidden
else ''
268 if not ent
.hidden
or not self
.lib_need_non_hidden_entries
:
269 decls
.append(self
._c
_decl
(ent
, prefix
, True, export
) + ';')
271 return "\n".join(decls
)
273 def c_mapi_table(self
):
274 """Return defines of the dispatch table size."""
275 num_static_entries
= self
.entries
[-1].slot
+ 1
276 return ('#define MAPI_TABLE_NUM_STATIC %d\n' + \
277 '#define MAPI_TABLE_NUM_DYNAMIC %d') % (
278 num_static_entries
, ABI_NUM_DYNAMIC_ENTRIES
)
280 def _c_function(self
, ent
, prefix
, mangle
=False, stringify
=False):
281 """Return the function name of an entry."""
283 True: { True: '%s_STR(%s)', False: '%s(%s)' },
284 False: { True: '"%s%s"', False: '%s%s' },
286 fmt
= formats
[prefix
.isupper()][stringify
]
288 if mangle
and ent
.hidden
:
289 name
= '_dispatch_stub_' + str(ent
.slot
)
290 return fmt
% (prefix
, name
)
292 def _c_function_call(self
, ent
, prefix
):
293 """Return the function name used for calling."""
295 # _c_function does not handle this case
296 formats
= { True: '%s(%s)', False: '%s%s' }
297 fmt
= formats
[prefix
.isupper()]
298 name
= fmt
% (prefix
, ent
.handcode
)
299 elif self
.need_entry_point(ent
):
300 name
= self
._c
_function
(ent
, prefix
, True)
302 name
= self
._c
_function
(ent
.alias
, prefix
, True)
305 def _c_decl(self
, ent
, prefix
, mangle
=False, export
=''):
306 """Return the C declaration for the entry."""
307 decl
= '%s %s %s(%s)' % (ent
.c_return(), self
.api_entry
,
308 self
._c
_function
(ent
, prefix
, mangle
), ent
.c_params())
310 decl
= export
+ ' ' + decl
312 decl
+= ' ' + self
.api_attrs
316 def _c_cast(self
, ent
):
317 """Return the C cast for the entry."""
318 cast
= '%s (%s *)(%s)' % (
319 ent
.c_return(), self
.api_entry
, ent
.c_params())
323 def c_public_dispatches(self
, prefix
, no_hidden
):
324 """Return the public dispatch functions."""
326 for ent
in self
.entries
:
327 if ent
.hidden
and no_hidden
:
330 if not self
.need_entry_point(ent
):
333 export
= self
.api_call
if not ent
.hidden
else ''
335 proto
= self
._c
_decl
(ent
, prefix
, True, export
)
336 cast
= self
._c
_cast
(ent
)
342 stmt1
+= 'const struct _glapi_table *_tbl = %s();' % (
345 stmt2
+= 'mapi_func _func = ((const mapi_func *) _tbl)[%d];' % (
348 stmt3
+= '%s((%s) _func)(%s);' % (ret
, cast
, ent
.c_args())
350 disp
= '%s\n{\n%s\n%s\n%s\n}' % (proto
, stmt1
, stmt2
, stmt3
)
353 disp
= '#if 0\n' + disp
+ '\n#endif'
355 dispatches
.append(disp
)
357 return '\n\n'.join(dispatches
)
359 def c_public_initializer(self
, prefix
):
360 """Return the initializer for public dispatch functions."""
362 for ent
in self
.entries
:
366 name
= '%s(mapi_func) %s' % (self
.indent
,
367 self
._c
_function
_call
(ent
, prefix
))
370 return ',\n'.join(names
)
372 def c_stub_string_pool(self
):
373 """Return the string pool for use by stubs."""
374 # sort entries by their names
375 sorted_entries
= sorted(self
.entries
, key
=attrgetter('name'))
380 for ent
in sorted_entries
:
382 pool
.append('%s' % (ent
.name
))
383 count
+= len(ent
.name
) + 1
385 pool_str
= self
.indent
+ '"' + \
386 ('\\0"\n' + self
.indent
+ '"').join(pool
) + '";'
387 return (pool_str
, offsets
)
389 def c_stub_initializer(self
, prefix
, pool_offsets
):
390 """Return the initializer for struct mapi_stub array."""
392 for ent
in self
.entries_sorted_by_names
:
393 stubs
.append('%s{ (void *) %d, %d, NULL }' % (
394 self
.indent
, pool_offsets
[ent
], ent
.slot
))
396 return ',\n'.join(stubs
)
398 def c_noop_functions(self
, prefix
, warn_prefix
):
399 """Return the noop functions."""
401 for ent
in self
.entries
:
405 proto
= self
._c
_decl
(ent
, prefix
, False, 'static')
409 for t
, n
, a
in ent
.params
:
410 stmt1
+= "%s(void) %s;" % (space
, n
)
416 stmt1
+= self
.indent
+ '%s(%s);' % (self
.noop_warn
,
417 self
._c
_function
(ent
, warn_prefix
, False, True))
420 stmt2
= self
.indent
+ 'return (%s) 0;' % (ent
.ret
)
421 noop
= '%s\n{\n%s\n%s\n}' % (proto
, stmt1
, stmt2
)
423 noop
= '%s\n{\n%s\n}' % (proto
, stmt1
)
427 return '\n\n'.join(noops
)
429 def c_noop_initializer(self
, prefix
, use_generic
):
430 """Return an initializer for the noop dispatch table."""
431 entries
= [self
._c
_function
(ent
, prefix
)
432 for ent
in self
.entries
if not ent
.alias
]
434 entries
= [self
.noop_generic
] * len(entries
)
436 entries
.extend([self
.noop_generic
] * ABI_NUM_DYNAMIC_ENTRIES
)
438 pre
= self
.indent
+ '(mapi_func) '
439 return pre
+ (',\n' + pre
).join(entries
)
441 def c_asm_gcc(self
, prefix
, no_hidden
):
444 for ent
in self
.entries
:
445 if ent
.hidden
and no_hidden
:
448 if not self
.need_entry_point(ent
):
451 name
= self
._c
_function
(ent
, prefix
, True, True)
457 asm
.append('".hidden "%s"\\n"' % (name
))
459 if ent
.alias
and not (ent
.alias
.hidden
and no_hidden
):
460 asm
.append('".globl "%s"\\n"' % (name
))
461 asm
.append('".set "%s", "%s"\\n"' % (name
,
462 self
._c
_function
(ent
.alias
, prefix
, True, True)))
464 asm
.append('STUB_ASM_ENTRY(%s)"\\n"' % (name
))
465 asm
.append('"\\t"STUB_ASM_CODE("%d")"\\n"' % (ent
.slot
))
471 return "\n".join(asm
)
473 def output_for_lib(self
):
474 print(self
.c_notice())
481 print('#ifdef MAPI_TMP_DEFINES')
482 print(self
.c_public_includes())
484 print(self
.c_public_declarations(self
.prefix_lib
))
485 print('#undef MAPI_TMP_DEFINES')
486 print('#endif /* MAPI_TMP_DEFINES */')
488 if self
.lib_need_table_size
:
490 print('#ifdef MAPI_TMP_TABLE')
491 print(self
.c_mapi_table())
492 print('#undef MAPI_TMP_TABLE')
493 print('#endif /* MAPI_TMP_TABLE */')
495 if self
.lib_need_noop_array
:
497 print('#ifdef MAPI_TMP_NOOP_ARRAY')
498 print('#ifdef DEBUG')
500 print(self
.c_noop_functions(self
.prefix_noop
, self
.prefix_warn
))
502 print('const mapi_func table_%s_array[] = {' % (self
.prefix_noop
))
503 print(self
.c_noop_initializer(self
.prefix_noop
, False))
506 print('#else /* DEBUG */')
508 print('const mapi_func table_%s_array[] = {' % (self
.prefix_noop
))
509 print(self
.c_noop_initializer(self
.prefix_noop
, True))
512 print('#endif /* DEBUG */')
513 print('#undef MAPI_TMP_NOOP_ARRAY')
514 print('#endif /* MAPI_TMP_NOOP_ARRAY */')
516 if self
.lib_need_stubs
:
517 pool
, pool_offsets
= self
.c_stub_string_pool()
519 print('#ifdef MAPI_TMP_PUBLIC_STUBS')
520 print('static const char public_string_pool[] =')
523 print('static const struct mapi_stub public_stubs[] = {')
524 print(self
.c_stub_initializer(self
.prefix_lib
, pool_offsets
))
526 print('#undef MAPI_TMP_PUBLIC_STUBS')
527 print('#endif /* MAPI_TMP_PUBLIC_STUBS */')
529 if self
.lib_need_all_entries
:
531 print('#ifdef MAPI_TMP_PUBLIC_ENTRIES')
532 print(self
.c_public_dispatches(self
.prefix_lib
, False))
534 print('static const mapi_func public_entries[] = {')
535 print(self
.c_public_initializer(self
.prefix_lib
))
537 print('#undef MAPI_TMP_PUBLIC_ENTRIES')
538 print('#endif /* MAPI_TMP_PUBLIC_ENTRIES */')
541 print('#ifdef MAPI_TMP_STUB_ASM_GCC')
543 print(self
.c_asm_gcc(self
.prefix_lib
, False))
545 print('#undef MAPI_TMP_STUB_ASM_GCC')
546 print('#endif /* MAPI_TMP_STUB_ASM_GCC */')
548 if self
.lib_need_non_hidden_entries
:
550 for ent
in self
.entries
:
556 print('#ifdef MAPI_TMP_PUBLIC_ENTRIES_NO_HIDDEN')
557 print(self
.c_public_dispatches(self
.prefix_lib
, True))
559 print('/* does not need public_entries */')
560 print('#undef MAPI_TMP_PUBLIC_ENTRIES_NO_HIDDEN')
561 print('#endif /* MAPI_TMP_PUBLIC_ENTRIES_NO_HIDDEN */')
564 print('#ifdef MAPI_TMP_STUB_ASM_GCC_NO_HIDDEN')
566 print(self
.c_asm_gcc(self
.prefix_lib
, True))
568 print('#undef MAPI_TMP_STUB_ASM_GCC_NO_HIDDEN')
569 print('#endif /* MAPI_TMP_STUB_ASM_GCC_NO_HIDDEN */')
571 class GLAPIPrinter(ABIPrinter
):
572 """OpenGL API Printer"""
574 def __init__(self
, entries
):
576 self
._override
_for
_api
(ent
)
577 super(GLAPIPrinter
, self
).__init
__(entries
)
579 self
.api_defines
= ['GL_GLEXT_PROTOTYPES']
580 self
.api_headers
= ['"GL/gl.h"', '"GL/glext.h"']
581 self
.api_call
= 'GLAPI'
582 self
.api_entry
= 'APIENTRY'
585 self
.lib_need_table_size
= False
586 self
.lib_need_noop_array
= False
587 self
.lib_need_stubs
= False
588 self
.lib_need_all_entries
= False
589 self
.lib_need_non_hidden_entries
= True
591 self
.prefix_lib
= 'GLAPI_PREFIX'
592 self
.prefix_noop
= 'noop'
593 self
.prefix_warn
= self
.prefix_lib
595 self
.c_header
= self
._get
_c
_header
()
597 def _override_for_api(self
, ent
):
598 """Override attributes of an entry if necessary for this
600 # By default, no override is necessary.
603 def _get_c_header(self
):
604 header
= """#ifndef _GLAPI_TMP_H_
605 #define _GLAPI_TMP_H_
606 #ifdef USE_MGL_NAMESPACE
607 #define GLAPI_PREFIX(func) mgl##func
608 #define GLAPI_PREFIX_STR(func) "mgl"#func
610 #define GLAPI_PREFIX(func) gl##func
611 #define GLAPI_PREFIX_STR(func) "gl"#func
612 #endif /* USE_MGL_NAMESPACE */
614 typedef int GLclampx;
615 #endif /* _GLAPI_TMP_H_ */"""
619 class SharedGLAPIPrinter(GLAPIPrinter
):
620 """Shared GLAPI API Printer"""
622 def __init__(self
, entries
):
623 super(SharedGLAPIPrinter
, self
).__init
__(entries
)
625 self
.lib_need_table_size
= True
626 self
.lib_need_noop_array
= True
627 self
.lib_need_stubs
= True
628 self
.lib_need_all_entries
= True
629 self
.lib_need_non_hidden_entries
= False
631 self
.prefix_lib
= 'shared'
632 self
.prefix_warn
= 'gl'
634 def _override_for_api(self
, ent
):
638 def _get_c_header(self
):
639 header
= """#ifndef _GLAPI_TMP_H_
640 #define _GLAPI_TMP_H_
641 typedef int GLclampx;
642 #endif /* _GLAPI_TMP_H_ */"""
647 printers
= ['glapi', 'es1api', 'es2api', 'shared-glapi']
649 parser
= OptionParser(usage
='usage: %prog [options] <xml_file>')
650 parser
.add_option('-p', '--printer', dest
='printer',
651 help='printer to use: %s' % (", ".join(printers
)))
653 options
, args
= parser
.parse_args()
654 if not args
or options
.printer
not in printers
:
658 if not args
[0].endswith('.xml'):
662 return (args
[0], options
)
666 'glapi': GLAPIPrinter
,
667 'shared-glapi': SharedGLAPIPrinter
,
670 filename
, options
= parse_args()
672 entries
= abi_parse_xml(filename
)
673 abi_sanity_check(entries
)
675 printer
= printers
[options
.printer
](entries
)
676 printer
.output_for_lib()
678 if __name__
== '__main__':