3 # Mesa 3-D graphics library
6 # Copyright (C) 2010 LunarG Inc.
8 # Permission is hereby granted, free of charge, to any person obtaining a
9 # copy of this software and associated documentation files (the "Software"),
10 # to deal in the Software without restriction, including without limitation
11 # the rights to use, copy, modify, merge, publish, distribute, sublicense,
12 # and/or sell copies of the Software, and to permit persons to whom the
13 # Software is furnished to do so, subject to the following conditions:
15 # The above copyright notice and this permission notice shall be included
16 # in all copies or substantial portions of the Software.
18 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21 # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24 # DEALINGS IN THE SOFTWARE.
27 # Chia-I Wu <olv@lunarg.com>
31 from optparse
import OptionParser
33 # number of dynamic entries
34 ABI_NUM_DYNAMIC_ENTRIES
= 256
36 class ABIEntry(object):
37 """Represent an ABI entry."""
39 _match_c_param
= re
.compile(
40 '^(?P<type>[\w\s*]+?)(?P<name>\w+)(\[(?P<array>\d+)\])?$')
42 def __init__(self
, cols
, attrs
):
45 self
.slot
= attrs
['slot']
46 self
.hidden
= attrs
['hidden']
47 self
.alias
= attrs
['alias']
48 self
.handcode
= attrs
['handcode']
50 def c_prototype(self
):
51 return '%s %s(%s)' % (self
.c_return(), self
.name
, self
.c_params())
61 """Return the parameter list used in the entry prototype."""
63 for t
, n
, a
in self
.params
:
64 sep
= '' if t
.endswith('*') else ' '
65 arr
= '[%d]' % a
if a
else ''
66 c_params
.append(t
+ sep
+ n
+ arr
)
68 c_params
.append('void')
70 return ", ".join(c_params
)
73 """Return the argument list used in the entry invocation."""
75 for t
, n
, a
in self
.params
:
78 return ", ".join(c_args
)
80 def _parse(self
, cols
):
90 elif len(cols
) == 1 and cols
[0] == 'void':
94 params
.append(self
._parse
_param
(val
))
100 def _parse_param(self
, c_param
):
101 m
= self
._match
_c
_param
.match(c_param
)
103 raise Exception('unrecognized param ' + c_param
)
105 c_type
= m
.group('type').strip()
106 c_name
= m
.group('name')
107 c_array
= m
.group('array')
108 c_array
= int(c_array
) if c_array
else 0
110 return (c_type
, c_name
, c_array
)
113 return self
.c_prototype()
115 def __cmp__(self
, other
):
116 # compare slot, alias, and then name
117 res
= cmp(self
.slot
, other
.slot
)
121 elif not other
.alias
:
125 res
= cmp(self
.name
, other
.name
)
129 def abi_parse_xml(xml
):
130 """Parse a GLAPI XML file for ABI entries."""
132 GLAPI
= "./%s/../glapi/gen" % (os
.path
.dirname(sys
.argv
[0]))
133 sys
.path
.append(GLAPI
)
134 import gl_XML
, glX_XML
136 api
= gl_XML
.parse_GL_API(xml
, glX_XML
.glx_item_factory())
139 for func
in api
.functionIterateByOffset():
140 # make sure func.name appear first
141 entry_points
= func
.entry_points
[:]
142 entry_points
.remove(func
.name
)
143 entry_points
.insert(0, func
.name
)
145 for name
in entry_points
:
148 'hidden': not func
.is_static_entry_point(name
),
149 'alias': None if name
== func
.name
else func
.name
,
150 'handcode': bool(func
.has_different_protocol(name
)),
156 alias
= entry_dict
[attrs
['alias']]
158 raise Exception('failed to alias %s' % attrs
['alias'])
160 raise Exception('recursive alias %s' % ent
.name
)
161 attrs
['alias'] = alias
162 if attrs
['handcode']:
163 attrs
['handcode'] = func
.static_glx_name(name
)
165 attrs
['handcode'] = None
167 if entry_dict
.has_key(name
):
168 raise Exception('%s is duplicated' % (name
))
171 cols
.append(func
.return_type
)
173 params
= func
.get_parameter_string(name
)
174 cols
.extend([p
.strip() for p
in params
.split(',')])
176 ent
= ABIEntry(cols
, attrs
)
177 entry_dict
[ent
.name
] = ent
179 entries
= entry_dict
.values()
184 def abi_parse_line(line
):
185 cols
= [col
.strip() for col
in line
.split(',')]
194 # extract attributes from the first column
195 vals
= cols
[0].split(':')
198 if val
.startswith('slot='):
199 attrs
['slot'] = int(val
[5:])
200 elif val
== 'hidden':
201 attrs
['hidden'] = True
202 elif val
.startswith('alias='):
203 attrs
['alias'] = val
[6:]
204 elif val
.startswith('handcode='):
205 attrs
['handcode'] = val
[9:]
209 raise Exception('unknown attribute %s' % val
)
214 def abi_parse(filename
):
215 """Parse a CSV file for ABI entries."""
216 fp
= open(filename
) if filename
!= '-' else sys
.stdin
217 lines
= [line
.strip() for line
in fp
.readlines()
218 if not line
.startswith('#') and line
.strip()]
223 attrs
, cols
= abi_parse_line(line
)
225 # post-process attributes
228 alias
= entry_dict
[attrs
['alias']]
230 raise Exception('failed to alias %s' % attrs
['alias'])
232 raise Exception('recursive alias %s' % ent
.name
)
234 attrs
['alias'] = alias
239 if attrs
['slot'] < 0:
241 elif attrs
['slot'] != slot
:
242 raise Exception('invalid slot in %s' % (line
))
244 ent
= ABIEntry(cols
, attrs
)
245 if entry_dict
.has_key(ent
.name
):
246 raise Exception('%s is duplicated' % (ent
.name
))
247 entry_dict
[ent
.name
] = ent
249 entries
= entry_dict
.values()
254 def abi_sanity_check(entries
):
259 last_slot
= entries
[-1].slot
261 for slot
in xrange(last_slot
+ 1):
262 if entries
[i
].slot
!= slot
:
263 raise Exception('entries are not ordered by slots')
265 raise Exception('first entry of slot %d aliases %s'
266 % (slot
, entries
[i
].alias
.name
))
268 while i
< len(entries
) and entries
[i
].slot
== slot
:
270 if not handcode
and ent
.handcode
:
271 handcode
= ent
.handcode
272 elif ent
.handcode
!= handcode
:
273 raise Exception('two aliases with handcode %s != %s',
274 ent
.handcode
, handcode
)
276 if ent
.name
in all_names
:
277 raise Exception('%s is duplicated' % (ent
.name
))
278 if ent
.alias
and ent
.alias
.name
not in all_names
:
279 raise Exception('failed to alias %s' % (ent
.alias
.name
))
280 all_names
.append(ent
.name
)
283 raise Exception('there are %d invalid entries' % (len(entries
) - 1))
285 class ABIPrinter(object):
288 def __init__(self
, entries
):
289 self
.entries
= entries
291 # sort entries by their names
292 self
.entries_sorted_by_names
= self
.entries
[:]
293 self
.entries_sorted_by_names
.sort(lambda x
, y
: cmp(x
.name
, y
.name
))
295 self
.indent
= ' ' * 3
296 self
.noop_warn
= 'noop_warn'
297 self
.noop_generic
= 'noop_generic'
299 self
.api_defines
= []
300 self
.api_headers
= ['"KHR/khrplatform.h"']
301 self
.api_call
= 'KHRONOS_APICALL'
302 self
.api_entry
= 'KHRONOS_APIENTRY'
303 self
.api_attrs
= 'KHRONOS_APIATTRIBUTES'
305 self
.lib_need_table_size
= True
306 self
.lib_need_noop_array
= True
307 self
.lib_need_stubs
= True
308 self
.lib_need_entries
= True
311 return '/* This file is automatically generated by mapi_abi.py. Do not modify. */'
313 def c_public_includes(self
):
314 """Return includes of the client API headers."""
315 defines
= ['#define ' + d
for d
in self
.api_defines
]
316 includes
= ['#include ' + h
for h
in self
.api_headers
]
317 return "\n".join(defines
+ includes
)
319 def need_entry_point(self
, ent
):
320 """Return True if an entry point is needed for the entry."""
321 # non-handcode hidden aliases may share the entry they alias
322 use_alias
= (ent
.hidden
and ent
.alias
and not ent
.handcode
)
325 def c_public_declarations(self
, prefix
):
326 """Return the declarations of public entry points."""
328 for ent
in self
.entries
:
329 if not self
.need_entry_point(ent
):
331 export
= self
.api_call
if not ent
.hidden
else ''
332 decls
.append(self
._c
_decl
(ent
, prefix
, True, export
) + ';')
334 return "\n".join(decls
)
336 def c_mapi_table(self
):
337 """Return defines of the dispatch table size."""
338 num_static_entries
= 0
339 for ent
in self
.entries
:
341 num_static_entries
+= 1
343 return ('#define MAPI_TABLE_NUM_STATIC %d\n' + \
344 '#define MAPI_TABLE_NUM_DYNAMIC %d') % (
345 num_static_entries
, ABI_NUM_DYNAMIC_ENTRIES
)
347 def c_mapi_table_initializer(self
, prefix
):
348 """Return the array initializer for mapi_table_fill."""
349 entries
= [self
._c
_function
(ent
, prefix
)
350 for ent
in self
.entries
if not ent
.alias
]
351 pre
= self
.indent
+ '(mapi_proc) '
352 return pre
+ (',\n' + pre
).join(entries
)
354 def c_mapi_table_spec(self
):
355 """Return the spec for mapi_init."""
358 for ent
in self
.entries
:
363 line
+= '%s\\0' % ent
.name
367 return self
.indent
+ self
.indent
.join(specv1
)
369 def _c_function(self
, ent
, prefix
, mangle
=False, stringify
=False):
370 """Return the function name of an entry."""
371 formats
= { True: '"%s%s"', False: '%s%s' }
372 fmt
= formats
[stringify
]
374 if mangle
and ent
.hidden
:
375 name
= '_dispatch_stub_' + str(ent
.slot
)
376 return fmt
% (prefix
, name
)
378 def _c_function_call(self
, ent
, prefix
):
379 """Return the function name used for calling."""
381 # _c_function does not handle this case
383 name
= fmt
% (prefix
, ent
.handcode
)
384 elif self
.need_entry_point(ent
):
385 name
= self
._c
_function
(ent
, prefix
, True)
387 name
= self
._c
_function
(ent
.alias
, prefix
, True)
390 def _c_decl(self
, ent
, prefix
, mangle
=False, export
=''):
391 """Return the C declaration for the entry."""
392 decl
= '%s %s %s(%s)' % (ent
.c_return(), self
.api_entry
,
393 self
._c
_function
(ent
, prefix
, mangle
), ent
.c_params())
395 decl
= export
+ ' ' + decl
397 decl
+= ' ' + self
.api_attrs
401 def _c_cast(self
, ent
):
402 """Return the C cast for the entry."""
403 cast
= '%s (%s *)(%s)' % (
404 ent
.c_return(), self
.api_entry
, ent
.c_params())
408 def c_private_declarations(self
, prefix
):
409 """Return the declarations of private functions."""
410 decls
= [self
._c
_decl
(ent
, prefix
) + ';'
411 for ent
in self
.entries
if not ent
.alias
]
413 return "\n".join(decls
)
415 def c_public_dispatches(self
, prefix
):
416 """Return the public dispatch functions."""
418 for ent
in self
.entries
:
419 if not self
.need_entry_point(ent
):
422 export
= self
.api_call
if not ent
.hidden
else ''
424 proto
= self
._c
_decl
(ent
, prefix
, True, export
)
425 cast
= self
._c
_cast
(ent
)
431 stmt1
+= 'const struct mapi_table *tbl = u_current_get();'
433 stmt2
+= 'mapi_func func = ((const mapi_func *) tbl)[%d];' % (
436 stmt3
+= '%s((%s) func)(%s);' % (ret
, cast
, ent
.c_args())
438 disp
= '%s\n{\n%s\n%s\n%s\n}' % (proto
, stmt1
, stmt2
, stmt3
)
441 disp
= '#if 0\n' + disp
+ '\n#endif'
443 dispatches
.append(disp
)
445 return '\n\n'.join(dispatches
)
447 def c_public_initializer(self
, prefix
):
448 """Return the initializer for public dispatch functions."""
450 for ent
in self
.entries
:
454 name
= '%s(mapi_func) %s' % (self
.indent
,
455 self
._c
_function
_call
(ent
, prefix
))
458 return ',\n'.join(names
)
460 def c_stub_string_pool(self
):
461 """Return the string pool for use by stubs."""
462 # sort entries by their names
463 sorted_entries
= self
.entries
[:]
464 sorted_entries
.sort(lambda x
, y
: cmp(x
.name
, y
.name
))
469 for ent
in sorted_entries
:
471 pool
.append('%s' % (ent
.name
))
472 count
+= len(ent
.name
) + 1
474 pool_str
= self
.indent
+ '"' + \
475 ('\\0"\n' + self
.indent
+ '"').join(pool
) + '";'
476 return (pool_str
, offsets
)
478 def c_stub_initializer(self
, prefix
, pool_offsets
):
479 """Return the initializer for struct mapi_stub array."""
481 for ent
in self
.entries_sorted_by_names
:
482 stubs
.append('%s{ (void *) %d, %d, NULL }' % (
483 self
.indent
, pool_offsets
[ent
], ent
.slot
))
485 return ',\n'.join(stubs
)
487 def c_noop_functions(self
, prefix
, warn_prefix
):
488 """Return the noop functions."""
490 for ent
in self
.entries
:
494 proto
= self
._c
_decl
(ent
, prefix
, False, 'static')
496 stmt1
= self
.indent
+ '%s(%s);' % (self
.noop_warn
,
497 self
._c
_function
(ent
, warn_prefix
, False, True))
500 stmt2
= self
.indent
+ 'return (%s) 0;' % (ent
.ret
)
501 noop
= '%s\n{\n%s\n%s\n}' % (proto
, stmt1
, stmt2
)
503 noop
= '%s\n{\n%s\n}' % (proto
, stmt1
)
507 return '\n\n'.join(noops
)
509 def c_noop_initializer(self
, prefix
, use_generic
):
510 """Return an initializer for the noop dispatch table."""
511 entries
= [self
._c
_function
(ent
, prefix
)
512 for ent
in self
.entries
if not ent
.alias
]
514 entries
= [self
.noop_generic
] * len(entries
)
516 entries
.extend([self
.noop_generic
] * ABI_NUM_DYNAMIC_ENTRIES
)
518 pre
= self
.indent
+ '(mapi_func) '
519 return pre
+ (',\n' + pre
).join(entries
)
521 def c_asm_gcc(self
, prefix
):
524 asm
.append('__asm__(')
525 for ent
in self
.entries
:
526 if not self
.need_entry_point(ent
):
529 name
= self
._c
_function
(ent
, prefix
, True, True)
535 asm
.append('".hidden "%s"\\n"' % (name
))
538 asm
.append('".globl "%s"\\n"' % (name
))
539 asm
.append('".set "%s", "%s"\\n"' % (name
,
540 self
._c
_function
(ent
.alias
, prefix
, True, True)))
542 asm
.append('STUB_ASM_ENTRY(%s)"\\n"' % (name
))
543 asm
.append('"\\t"STUB_ASM_CODE("%d")"\\n"' % (ent
.slot
))
550 return "\n".join(asm
)
552 def output_for_lib(self
):
553 print self
.c_notice()
555 print '#ifdef MAPI_TMP_DEFINES'
556 print self
.c_public_includes()
558 print self
.c_public_declarations(self
.prefix_lib
)
559 print '#undef MAPI_TMP_DEFINES'
560 print '#endif /* MAPI_TMP_DEFINES */'
562 if self
.lib_need_table_size
:
564 print '#ifdef MAPI_TMP_TABLE'
565 print self
.c_mapi_table()
566 print '#undef MAPI_TMP_TABLE'
567 print '#endif /* MAPI_TMP_TABLE */'
569 if self
.lib_need_noop_array
:
571 print '#ifdef MAPI_TMP_NOOP_ARRAY'
574 print self
.c_noop_functions(self
.prefix_noop
, self
.prefix_lib
)
576 print 'const mapi_func table_%s_array[] = {' % (self
.prefix_noop
)
577 print self
.c_noop_initializer(self
.prefix_noop
, False)
580 print '#else /* DEBUG */'
582 print 'const mapi_func table_%s_array[] = {' % (self
.prefix_noop
)
583 print self
.c_noop_initializer(self
.prefix_noop
, True)
586 print '#endif /* DEBUG */'
587 print '#undef MAPI_TMP_NOOP_ARRAY'
588 print '#endif /* MAPI_TMP_NOOP_ARRAY */'
590 if self
.lib_need_stubs
:
591 pool
, pool_offsets
= self
.c_stub_string_pool()
593 print '#ifdef MAPI_TMP_PUBLIC_STUBS'
594 print 'static const char public_string_pool[] ='
597 print 'static const struct mapi_stub public_stubs[] = {'
598 print self
.c_stub_initializer(self
.prefix_lib
, pool_offsets
)
600 print '#undef MAPI_TMP_PUBLIC_STUBS'
601 print '#endif /* MAPI_TMP_PUBLIC_STUBS */'
603 if self
.lib_need_entries
:
605 print '#ifdef MAPI_TMP_PUBLIC_ENTRIES'
606 print self
.c_public_dispatches(self
.prefix_lib
)
608 print 'static const mapi_func public_entries[] = {'
609 print self
.c_public_initializer(self
.prefix_lib
)
611 print '#undef MAPI_TMP_PUBLIC_ENTRIES'
612 print '#endif /* MAPI_TMP_PUBLIC_ENTRIES */'
615 print '#ifdef MAPI_TMP_STUB_ASM_GCC'
616 print self
.c_asm_gcc(self
.prefix_lib
)
617 print '#undef MAPI_TMP_STUB_ASM_GCC'
618 print '#endif /* MAPI_TMP_STUB_ASM_GCC */'
620 def output_for_app(self
):
621 print self
.c_notice()
623 print self
.c_private_declarations(self
.prefix_app
)
625 print '#ifdef API_TMP_DEFINE_SPEC'
627 print 'static const char %s_spec[] =' % (self
.prefix_app
)
628 print self
.c_mapi_table_spec()
630 print 'static const mapi_proc %s_procs[] = {' % (self
.prefix_app
)
631 print self
.c_mapi_table_initializer(self
.prefix_app
)
634 print '#endif /* API_TMP_DEFINE_SPEC */'
636 class GLAPIPrinter(ABIPrinter
):
637 """OpenGL API Printer"""
639 def __init__(self
, entries
):
640 super(GLAPIPrinter
, self
).__init
__(entries
)
642 self
.api_defines
= ['GL_GLEXT_PROTOTYPES']
643 self
.api_headers
= ['"GL/gl.h"', '"GL/glext.h"']
644 self
.api_call
= 'GLAPI'
645 self
.api_entry
= 'APIENTRY'
648 self
.prefix_lib
= 'gl'
649 self
.prefix_app
= '_mesa_'
650 self
.prefix_noop
= 'noop'
652 def output_for_app(self
):
656 class ES1APIPrinter(GLAPIPrinter
):
657 """OpenGL ES 1.x API Printer"""
659 def __init__(self
, entries
):
660 super(ES1APIPrinter
, self
).__init
__(entries
)
662 self
.api_headers
= ['"GLES/gl.h"', '"GLES/glext.h"']
663 self
.api_call
= 'GL_API'
664 self
.api_entry
= 'GL_APIENTRY'
666 class ES2APIPrinter(GLAPIPrinter
):
667 """OpenGL ES 2.x API Printer"""
669 def __init__(self
, entries
):
670 super(ES2APIPrinter
, self
).__init
__(entries
)
672 self
.api_headers
= ['"GLES2/gl2.h"', '"GLES2/gl2ext.h"']
673 self
.api_call
= 'GL_APICALL'
674 self
.api_entry
= 'GL_APIENTRY'
676 class VGAPIPrinter(ABIPrinter
):
677 """OpenVG API Printer"""
679 def __init__(self
, entries
):
680 super(VGAPIPrinter
, self
).__init
__(entries
)
682 self
.api_defines
= ['VG_VGEXT_PROTOTYPES']
683 self
.api_headers
= ['"VG/openvg.h"', '"VG/vgext.h"']
684 self
.api_call
= 'VG_API_CALL'
685 self
.api_entry
= 'VG_API_ENTRY'
686 self
.api_attrs
= 'VG_API_EXIT'
688 self
.prefix_lib
= 'vg'
689 self
.prefix_app
= 'vega'
690 self
.prefix_noop
= 'noop'
693 printers
= ['glapi', 'es1api', 'es2api', 'vgapi']
694 modes
= ['lib', 'app']
696 parser
= OptionParser(usage
='usage: %prog [options] <filename>')
697 parser
.add_option('-p', '--printer', dest
='printer',
698 help='printer to use: %s' % (", ".join(printers
)))
699 parser
.add_option('-m', '--mode', dest
='mode',
700 help='target user: %s' % (", ".join(modes
)))
702 options
, args
= parser
.parse_args()
703 if not args
or options
.printer
not in printers
or \
704 options
.mode
not in modes
:
708 return (args
[0], options
)
712 'vgapi': VGAPIPrinter
,
713 'glapi': GLAPIPrinter
,
714 'es1api': ES1APIPrinter
,
715 'es2api': ES2APIPrinter
718 filename
, options
= parse_args()
720 if filename
.endswith('.xml'):
721 entries
= abi_parse_xml(filename
)
723 entries
= abi_parse(filename
)
724 abi_sanity_check(entries
)
726 printer
= printers
[options
.printer
](entries
)
727 if options
.mode
== 'lib':
728 printer
.output_for_lib()
730 printer
.output_for_app()
732 if __name__
== '__main__':