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']
49 def c_prototype(self
):
50 return '%s %s(%s)' % (self
.c_return(), self
.name
, self
.c_params())
60 """Return the parameter list used in the entry prototype."""
62 for t
, n
, a
in self
.params
:
63 sep
= '' if t
.endswith('*') else ' '
64 arr
= '[%d]' % a
if a
else ''
65 c_params
.append(t
+ sep
+ n
+ arr
)
67 c_params
.append('void')
69 return ", ".join(c_params
)
72 """Return the argument list used in the entry invocation."""
74 for t
, n
, a
in self
.params
:
77 return ", ".join(c_args
)
79 def _parse(self
, cols
):
89 elif len(cols
) == 1 and cols
[0] == 'void':
93 params
.append(self
._parse
_param
(val
))
99 def _parse_param(self
, c_param
):
100 m
= self
._match
_c
_param
.match(c_param
)
102 raise Exception('unrecognized param ' + c_param
)
104 c_type
= m
.group('type').strip()
105 c_name
= m
.group('name')
106 c_array
= m
.group('array')
107 c_array
= int(c_array
) if c_array
else 0
109 return (c_type
, c_name
, c_array
)
112 return self
.c_prototype()
114 def __cmp__(self
, other
):
115 # compare slot, alias, and then name
116 res
= cmp(self
.slot
, other
.slot
)
120 elif not other
.alias
:
124 res
= cmp(self
.name
, other
.name
)
128 def abi_parse_line(line
):
129 cols
= [col
.strip() for col
in line
.split(',')]
137 # extract attributes from the first column
138 vals
= cols
[0].split(':')
141 if val
.startswith('slot='):
142 attrs
['slot'] = int(val
[5:])
143 elif val
== 'hidden':
144 attrs
['hidden'] = True
145 elif val
.startswith('alias='):
146 attrs
['alias'] = val
[6:]
150 raise Exception('unknown attribute %s' % val
)
155 def abi_parse(filename
):
156 """Parse a CSV file for ABI entries."""
157 fp
= open(filename
) if filename
!= '-' else sys
.stdin
158 lines
= [line
.strip() for line
in fp
.readlines()
159 if not line
.startswith('#') and line
.strip()]
164 attrs
, cols
= abi_parse_line(line
)
166 # post-process attributes
169 alias
= entry_dict
[attrs
['alias']]
171 raise Exception('failed to alias %s' % attrs
['alias'])
173 raise Exception('recursive alias %s' % ent
.name
)
175 attrs
['alias'] = alias
180 if attrs
['slot'] < 0:
182 elif attrs
['slot'] != slot
:
183 raise Exception('invalid slot in %s' % (line
))
185 ent
= ABIEntry(cols
, attrs
)
186 if entry_dict
.has_key(ent
.name
):
187 raise Exception('%s is duplicated' % (ent
.name
))
188 entry_dict
[ent
.name
] = ent
190 entries
= entry_dict
.values()
195 for slot
in xrange(next_slot
):
196 if entries
[i
].slot
!= slot
:
197 raise Exception('entries are not ordered by slots')
199 raise Exception('first entry of slot %d aliases %s'
200 % (slot
, entries
[i
].alias
.name
))
201 while i
< len(entries
) and entries
[i
].slot
== slot
:
204 raise Exception('there are %d invalid entries' % (len(entries
) - 1))
208 class ABIPrinter(object):
211 def __init__(self
, entries
):
212 self
.entries
= entries
214 # sort entries by their names
215 self
.entries_sorted_by_names
= self
.entries
[:]
216 self
.entries_sorted_by_names
.sort(lambda x
, y
: cmp(x
.name
, y
.name
))
218 self
.indent
= ' ' * 3
219 self
.noop_warn
= 'noop_warn'
220 self
.noop_generic
= 'noop_generic'
222 self
.api_defines
= []
223 self
.api_headers
= ['"KHR/khrplatform.h"']
224 self
.api_call
= 'KHRONOS_APICALL'
225 self
.api_entry
= 'KHRONOS_APIENTRY'
226 self
.api_attrs
= 'KHRONOS_APIATTRIBUTES'
229 return '/* This file is automatically generated by mapi_abi.py. Do not modify. */'
231 def c_includes(self
):
232 """Return includes of the client API headers."""
233 defines
= ['#define ' + d
for d
in self
.api_defines
]
234 includes
= ['#include ' + h
for h
in self
.api_headers
]
235 return "\n".join(defines
+ includes
)
237 def c_mapi_table(self
):
238 """Return defines of the dispatch table size."""
239 num_static_entries
= 0
240 for ent
in self
.entries
:
242 num_static_entries
+= 1
244 return ('#define MAPI_TABLE_NUM_STATIC %d\n' + \
245 '#define MAPI_TABLE_NUM_DYNAMIC %d') % (
246 num_static_entries
, ABI_NUM_DYNAMIC_ENTRIES
)
248 def c_mapi_table_initializer(self
, prefix
):
249 """Return the array initializer for mapi_table_fill."""
250 entries
= [self
._c
_function
(ent
, prefix
)
251 for ent
in self
.entries
if not ent
.alias
]
252 pre
= self
.indent
+ '(mapi_proc) '
253 return pre
+ (',\n' + pre
).join(entries
)
255 def c_mapi_table_spec(self
):
256 """Return the spec for mapi_init."""
259 for ent
in self
.entries
:
264 line
+= '%s\\0' % ent
.name
268 return self
.indent
+ self
.indent
.join(specv1
)
270 def _c_function(self
, ent
, prefix
, stringify
=False):
271 """Return the function name of an entry."""
272 formats
= { True: '"%s%s"', False: '%s%s' }
273 fmt
= formats
[stringify
]
274 return fmt
% (prefix
, ent
.name
)
276 def _c_decl(self
, ent
, prefix
, export
=''):
277 """Return the C declaration for the entry."""
278 decl
= '%s %s %s(%s)' % (ent
.c_return(), self
.api_entry
,
279 self
._c
_function
(ent
, prefix
), ent
.c_params())
281 decl
= export
+ ' ' + decl
283 decl
+= ' ' + self
.api_attrs
287 def _c_cast(self
, ent
):
288 """Return the C cast for the entry."""
289 cast
= '%s (%s *)(%s)' % (
290 ent
.c_return(), self
.api_entry
, ent
.c_params())
294 def c_private_declarations(self
, prefix
):
295 """Return the declarations of private functions."""
296 decls
= [self
._c
_decl
(ent
, prefix
) + ';'
297 for ent
in self
.entries
if not ent
.alias
]
299 return "\n".join(decls
)
301 def c_public_dispatches(self
, prefix
):
302 """Return the public dispatch functions."""
304 for ent
in self
.entries
:
308 proto
= self
._c
_decl
(ent
, prefix
, self
.api_call
)
309 cast
= self
._c
_cast
(ent
)
315 stmt1
+= 'const struct mapi_table *tbl = u_current_get();'
317 stmt2
+= 'mapi_func func = ((const mapi_func *) tbl)[%d];' % (
320 stmt3
+= '%s((%s) func)(%s);' % (ret
, cast
, ent
.c_args())
322 disp
= '%s\n{\n%s\n%s\n%s\n}' % (proto
, stmt1
, stmt2
, stmt3
)
323 dispatches
.append(disp
)
325 return '\n\n'.join(dispatches
)
327 def c_stub_string_pool(self
):
328 """Return the string pool for use by stubs."""
329 # sort entries by their names
330 sorted_entries
= self
.entries
[:]
331 sorted_entries
.sort(lambda x
, y
: cmp(x
.name
, y
.name
))
336 for ent
in sorted_entries
:
338 pool
.append('%s' % (ent
.name
))
339 count
+= len(ent
.name
) + 1
341 pool_str
= self
.indent
+ '"' + \
342 ('\\0"\n' + self
.indent
+ '"').join(pool
) + '";'
343 return (pool_str
, offsets
)
345 def c_stub_initializer(self
, prefix
, pool_offsets
):
346 """Return the initializer for struct mapi_stub array."""
348 for ent
in self
.entries_sorted_by_names
:
349 stubs
.append('%s{ (mapi_func) %s, %d, (void *) %d }' % (
350 self
.indent
, self
._c
_function
(ent
, prefix
),
351 ent
.slot
, pool_offsets
[ent
]))
353 return ',\n'.join(stubs
)
355 def c_noop_functions(self
, prefix
, warn_prefix
):
356 """Return the noop functions."""
358 for ent
in self
.entries
:
362 proto
= self
._c
_decl
(ent
, prefix
, 'static')
364 stmt1
= self
.indent
+ '%s(%s);' % (self
.noop_warn
,
365 self
._c
_function
(ent
, warn_prefix
, True))
368 stmt2
= self
.indent
+ 'return (%s) 0;' % (ent
.ret
)
369 noop
= '%s\n{\n%s\n%s\n}' % (proto
, stmt1
, stmt2
)
371 noop
= '%s\n{\n%s\n}' % (proto
, stmt1
)
375 return '\n\n'.join(noops
)
377 def c_noop_initializer(self
, prefix
, use_generic
):
378 """Return an initializer for the noop dispatch table."""
379 entries
= [self
._c
_function
(ent
, prefix
)
380 for ent
in self
.entries
if not ent
.alias
]
382 entries
= [self
.noop_generic
] * len(entries
)
384 entries
.extend([self
.noop_generic
] * ABI_NUM_DYNAMIC_ENTRIES
)
386 pre
= self
.indent
+ '(mapi_func) '
387 return pre
+ (',\n' + pre
).join(entries
)
389 def c_asm_gcc(self
, prefix
):
392 asm
.append('__asm__(')
393 for ent
in self
.entries
:
394 name
= self
._c
_function
(ent
, prefix
, True)
397 asm
.append('".hidden "%s"\\n"' % (name
))
400 asm
.append('".globl "%s"\\n"' % (name
))
401 asm
.append('".set "%s", "%s"\\n"' % (name
,
402 self
._c
_function
(ent
.alias
, prefix
, True)))
404 asm
.append('STUB_ASM_ENTRY(%s)"\\n"' % (name
))
405 asm
.append('"\\t"STUB_ASM_CODE("%d")"\\n"' % (ent
.slot
))
408 return "\n".join(asm
)
410 def output_for_lib(self
):
411 print self
.c_header()
413 print '#ifdef MAPI_TMP_DEFINES'
414 print self
.c_includes()
415 print '#undef MAPI_TMP_DEFINES'
416 print '#endif /* MAPI_TMP_DEFINES */'
418 print '#ifdef MAPI_TMP_TABLE'
419 print self
.c_mapi_table()
420 print '#undef MAPI_TMP_TABLE'
421 print '#endif /* MAPI_TMP_TABLE */'
424 pool
, pool_offsets
= self
.c_stub_string_pool()
425 print '#ifdef MAPI_TMP_PUBLIC_STUBS'
426 print 'static const char public_string_pool[] ='
429 print 'static const struct mapi_stub public_stubs[] = {'
430 print self
.c_stub_initializer(self
.prefix_lib
, pool_offsets
)
432 print '#undef MAPI_TMP_PUBLIC_STUBS'
433 print '#endif /* MAPI_TMP_PUBLIC_STUBS */'
436 print '#ifdef MAPI_TMP_PUBLIC_ENTRIES'
437 print self
.c_public_dispatches(self
.prefix_lib
)
438 print '#undef MAPI_TMP_PUBLIC_ENTRIES'
439 print '#endif /* MAPI_TMP_PUBLIC_ENTRIES */'
442 print '#ifdef MAPI_TMP_NOOP_ARRAY'
445 print self
.c_noop_functions(self
.prefix_noop
, self
.prefix_lib
)
447 print 'const mapi_func table_%s_array[] = {' % (self
.prefix_noop
)
448 print self
.c_noop_initializer(self
.prefix_noop
, False)
451 print '#else /* DEBUG */'
453 print 'const mapi_func table_%s_array[] = {' % (self
.prefix_noop
)
454 print self
.c_noop_initializer(self
.prefix_noop
, True)
456 print '#endif /* DEBUG */'
457 print '#undef MAPI_TMP_NOOP_ARRAY'
458 print '#endif /* MAPI_TMP_NOOP_ARRAY */'
461 print '#ifdef MAPI_TMP_STUB_ASM_GCC'
462 print self
.c_asm_gcc(self
.prefix_lib
)
463 print '#undef MAPI_TMP_STUB_ASM_GCC'
464 print '#endif /* MAPI_TMP_STUB_ASM_GCC */'
466 def output_for_app(self
):
467 print self
.c_header()
469 print self
.c_private_declarations(self
.prefix_app
)
471 print '#ifdef API_TMP_DEFINE_SPEC'
473 print 'static const char %s_spec[] =' % (self
.prefix_app
)
474 print self
.c_mapi_table_spec()
476 print 'static const mapi_proc %s_procs[] = {' % (self
.prefix_app
)
477 print self
.c_mapi_table_initializer(self
.prefix_app
)
480 print '#endif /* API_TMP_DEFINE_SPEC */'
482 class GLAPIPrinter(ABIPrinter
):
483 """OpenGL API Printer"""
485 def __init__(self
, entries
):
486 super(GLAPIPrinter
, self
).__init
__(entries
)
488 self
.api_defines
= ['GL_GLEXT_PROTOTYPES']
489 self
.api_headers
= ['"GL/gl.h"', '"GL/glext.h"']
490 self
.api_call
= 'GLAPI'
491 self
.api_entry
= 'APIENTRY'
494 self
.prefix_lib
= 'gl'
495 self
.prefix_app
= '_mesa_'
496 self
.prefix_noop
= 'noop'
498 def output_for_app(self
):
502 class ES1APIPrinter(GLAPIPrinter
):
503 """OpenGL ES 1.x API Printer"""
505 def __init__(self
, entries
):
506 super(ES1APIPrinter
, self
).__init
__(entries
)
508 self
.api_headers
= ['"GLES/gl.h"', '"GLES/glext.h"']
509 self
.api_call
= 'GL_API'
510 self
.api_entry
= 'GL_APIENTRY'
512 class ES2APIPrinter(GLAPIPrinter
):
513 """OpenGL ES 2.x API Printer"""
515 def __init__(self
, entries
):
516 super(ES2APIPrinter
, self
).__init
__(entries
)
518 self
.api_headers
= ['"GLES2/gl2.h"', '"GLES2/gl2ext.h"']
519 self
.api_call
= 'GL_APICALL'
520 self
.api_entry
= 'GL_APIENTRY'
522 class VGAPIPrinter(ABIPrinter
):
523 """OpenVG API Printer"""
525 def __init__(self
, entries
):
526 super(VGAPIPrinter
, self
).__init
__(entries
)
528 self
.api_defines
= ['VG_VGEXT_PROTOTYPES']
529 self
.api_headers
= ['"VG/openvg.h"', '"VG/vgext.h"']
530 self
.api_call
= 'VG_API_CALL'
531 self
.api_entry
= 'VG_API_ENTRY'
532 self
.api_attrs
= 'VG_API_EXIT'
534 self
.prefix_lib
= 'vg'
535 self
.prefix_app
= 'vega'
536 self
.prefix_noop
= 'noop'
539 printers
= ['glapi', 'es1api', 'es2api', 'vgapi']
540 modes
= ['lib', 'app']
542 parser
= OptionParser(usage
='usage: %prog [options] <filename>')
543 parser
.add_option('-p', '--printer', dest
='printer',
544 help='printer to use: %s' % (", ".join(printers
)))
545 parser
.add_option('-m', '--mode', dest
='mode',
546 help='target user: %s' % (", ".join(modes
)))
548 options
, args
= parser
.parse_args()
549 if not args
or options
.printer
not in printers
or \
550 options
.mode
not in modes
:
554 return (args
[0], options
)
558 'vgapi': VGAPIPrinter
,
559 'glapi': GLAPIPrinter
,
560 'es1api': ES1APIPrinter
,
561 'es2api': ES2APIPrinter
564 filename
, options
= parse_args()
566 entries
= abi_parse(filename
)
567 printer
= printers
[options
.printer
](entries
)
568 if options
.mode
== 'lib':
569 printer
.output_for_lib()
571 printer
.output_for_app()
573 if __name__
== '__main__':