mapi: Move mapi_func typedef to entry.h.
[mesa.git] / src / mapi / mapi / mapi_abi.py
1 #!/usr/bin/env python
2
3 # Mesa 3-D graphics library
4 # Version: 7.9
5 #
6 # Copyright (C) 2010 LunarG Inc.
7 #
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:
14 #
15 # The above copyright notice and this permission notice shall be included
16 # in all copies or substantial portions of the Software.
17 #
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.
25 #
26 # Authors:
27 # Chia-I Wu <olv@lunarg.com>
28
29 import sys
30 import re
31 from optparse import OptionParser
32
33 # number of dynamic entries
34 ABI_NUM_DYNAMIC_ENTRIES = 256
35
36 class ABIEntry(object):
37 """Represent an ABI entry."""
38
39 _match_c_param = re.compile(
40 '^(?P<type>[\w\s*]+?)(?P<name>\w+)(\[(?P<array>\d+)\])?$')
41
42 def __init__(self, cols, attrs):
43 self._parse(cols)
44
45 self.slot = attrs['slot']
46 self.hidden = attrs['hidden']
47 self.alias = attrs['alias']
48 self.handcode = attrs['handcode']
49
50 def c_prototype(self):
51 return '%s %s(%s)' % (self.c_return(), self.name, self.c_params())
52
53 def c_return(self):
54 ret = self.ret
55 if not ret:
56 ret = 'void'
57
58 return ret
59
60 def c_params(self):
61 """Return the parameter list used in the entry prototype."""
62 c_params = []
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)
67 if not c_params:
68 c_params.append('void')
69
70 return ", ".join(c_params)
71
72 def c_args(self):
73 """Return the argument list used in the entry invocation."""
74 c_args = []
75 for t, n, a in self.params:
76 c_args.append(n)
77
78 return ", ".join(c_args)
79
80 def _parse(self, cols):
81 ret = cols.pop(0)
82 if ret == 'void':
83 ret = None
84
85 name = cols.pop(0)
86
87 params = []
88 if not cols:
89 raise Exception(cols)
90 elif len(cols) == 1 and cols[0] == 'void':
91 pass
92 else:
93 for val in cols:
94 params.append(self._parse_param(val))
95
96 self.ret = ret
97 self.name = name
98 self.params = params
99
100 def _parse_param(self, c_param):
101 m = self._match_c_param.match(c_param)
102 if not m:
103 raise Exception('unrecognized param ' + c_param)
104
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
109
110 return (c_type, c_name, c_array)
111
112 def __str__(self):
113 return self.c_prototype()
114
115 def __cmp__(self, other):
116 # compare slot, alias, and then name
117 res = cmp(self.slot, other.slot)
118 if not res:
119 if not self.alias:
120 res = -1
121 elif not other.alias:
122 res = 1
123
124 if not res:
125 res = cmp(self.name, other.name)
126
127 return res
128
129 def abi_parse_line(line):
130 cols = [col.strip() for col in line.split(',')]
131
132 attrs = {
133 'slot': -1,
134 'hidden': False,
135 'alias': None,
136 'handcode': None,
137 }
138
139 # extract attributes from the first column
140 vals = cols[0].split(':')
141 while len(vals) > 1:
142 val = vals.pop(0)
143 if val.startswith('slot='):
144 attrs['slot'] = int(val[5:])
145 elif val == 'hidden':
146 attrs['hidden'] = True
147 elif val.startswith('alias='):
148 attrs['alias'] = val[6:]
149 elif val.startswith('handcode='):
150 attrs['handcode'] = val[9:]
151 elif not val:
152 pass
153 else:
154 raise Exception('unknown attribute %s' % val)
155 cols[0] = vals[0]
156
157 return (attrs, cols)
158
159 def abi_parse(filename):
160 """Parse a CSV file for ABI entries."""
161 fp = open(filename) if filename != '-' else sys.stdin
162 lines = [line.strip() for line in fp.readlines()
163 if not line.startswith('#') and line.strip()]
164
165 entry_dict = {}
166 next_slot = 0
167 for line in lines:
168 attrs, cols = abi_parse_line(line)
169
170 # post-process attributes
171 if attrs['alias']:
172 try:
173 alias = entry_dict[attrs['alias']]
174 except KeyError:
175 raise Exception('failed to alias %s' % attrs['alias'])
176 if alias.alias:
177 raise Exception('recursive alias %s' % ent.name)
178 slot = alias.slot
179 attrs['alias'] = alias
180 else:
181 slot = next_slot
182 next_slot += 1
183
184 if attrs['slot'] < 0:
185 attrs['slot'] = slot
186 elif attrs['slot'] != slot:
187 raise Exception('invalid slot in %s' % (line))
188
189 ent = ABIEntry(cols, attrs)
190 if entry_dict.has_key(ent.name):
191 raise Exception('%s is duplicated' % (ent.name))
192 entry_dict[ent.name] = ent
193
194 entries = entry_dict.values()
195 entries.sort()
196
197 # sanity check
198 i = 0
199 for slot in xrange(next_slot):
200 if entries[i].slot != slot:
201 raise Exception('entries are not ordered by slots')
202 if entries[i].alias:
203 raise Exception('first entry of slot %d aliases %s'
204 % (slot, entries[i].alias.name))
205 handcode = None
206 while i < len(entries) and entries[i].slot == slot:
207 ent = entries[i]
208 if not handcode and ent.handcode:
209 handcode = ent.handcode
210 elif ent.handcode != handcode:
211 raise Exception('two aliases with handcode %s != %s',
212 ent.handcode, handcode)
213 i += 1
214 if i < len(entries):
215 raise Exception('there are %d invalid entries' % (len(entries) - 1))
216
217 return entries
218
219 class ABIPrinter(object):
220 """MAPI Printer"""
221
222 def __init__(self, entries):
223 self.entries = entries
224
225 # sort entries by their names
226 self.entries_sorted_by_names = self.entries[:]
227 self.entries_sorted_by_names.sort(lambda x, y: cmp(x.name, y.name))
228
229 self.indent = ' ' * 3
230 self.noop_warn = 'noop_warn'
231 self.noop_generic = 'noop_generic'
232
233 self.api_defines = []
234 self.api_headers = ['"KHR/khrplatform.h"']
235 self.api_call = 'KHRONOS_APICALL'
236 self.api_entry = 'KHRONOS_APIENTRY'
237 self.api_attrs = 'KHRONOS_APIATTRIBUTES'
238
239 self.lib_need_table_size = True
240 self.lib_need_noop_array = True
241 self.lib_need_stubs = True
242 self.lib_need_entries = True
243
244 def c_notice(self):
245 return '/* This file is automatically generated by mapi_abi.py. Do not modify. */'
246
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)
252
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)
257 return not use_alias
258
259 def c_public_declarations(self, prefix):
260 """Return the declarations of public entry points."""
261 decls = []
262 for ent in self.entries:
263 if not self.need_entry_point(ent):
264 continue
265 export = self.api_call if not ent.hidden else ''
266 decls.append(self._c_decl(ent, prefix, True, export) + ';')
267
268 return "\n".join(decls)
269
270 def c_mapi_table(self):
271 """Return defines of the dispatch table size."""
272 num_static_entries = 0
273 for ent in self.entries:
274 if not ent.alias:
275 num_static_entries += 1
276
277 return ('#define MAPI_TABLE_NUM_STATIC %d\n' + \
278 '#define MAPI_TABLE_NUM_DYNAMIC %d') % (
279 num_static_entries, ABI_NUM_DYNAMIC_ENTRIES)
280
281 def c_mapi_table_initializer(self, prefix):
282 """Return the array initializer for mapi_table_fill."""
283 entries = [self._c_function(ent, prefix)
284 for ent in self.entries if not ent.alias]
285 pre = self.indent + '(mapi_proc) '
286 return pre + (',\n' + pre).join(entries)
287
288 def c_mapi_table_spec(self):
289 """Return the spec for mapi_init."""
290 specv1 = []
291 line = '"1'
292 for ent in self.entries:
293 if not ent.alias:
294 line += '\\0"\n'
295 specv1.append(line)
296 line = '"'
297 line += '%s\\0' % ent.name
298 line += '";'
299 specv1.append(line)
300
301 return self.indent + self.indent.join(specv1)
302
303 def _c_function(self, ent, prefix, mangle=False, stringify=False):
304 """Return the function name of an entry."""
305 formats = { True: '"%s%s"', False: '%s%s' }
306 fmt = formats[stringify]
307 name = ent.name
308 if mangle and ent.hidden:
309 name = '_dispatch_stub_' + str(ent.slot)
310 return fmt % (prefix, name)
311
312 def _c_function_call(self, ent, prefix):
313 """Return the function name used for calling."""
314 if ent.handcode:
315 # _c_function does not handle this case
316 fmt = '%s%s'
317 name = fmt % (prefix, ent.handcode)
318 elif self.need_entry_point(ent):
319 name = self._c_function(ent, prefix, True)
320 else:
321 name = self._c_function(ent.alias, prefix, True)
322 return name
323
324 def _c_decl(self, ent, prefix, mangle=False, export=''):
325 """Return the C declaration for the entry."""
326 decl = '%s %s %s(%s)' % (ent.c_return(), self.api_entry,
327 self._c_function(ent, prefix, mangle), ent.c_params())
328 if export:
329 decl = export + ' ' + decl
330 if self.api_attrs:
331 decl += ' ' + self.api_attrs
332
333 return decl
334
335 def _c_cast(self, ent):
336 """Return the C cast for the entry."""
337 cast = '%s (%s *)(%s)' % (
338 ent.c_return(), self.api_entry, ent.c_params())
339
340 return cast
341
342 def c_private_declarations(self, prefix):
343 """Return the declarations of private functions."""
344 decls = [self._c_decl(ent, prefix) + ';'
345 for ent in self.entries if not ent.alias]
346
347 return "\n".join(decls)
348
349 def c_public_dispatches(self, prefix):
350 """Return the public dispatch functions."""
351 dispatches = []
352 for ent in self.entries:
353 if not self.need_entry_point(ent):
354 continue
355
356 export = self.api_call if not ent.hidden else ''
357
358 proto = self._c_decl(ent, prefix, True, export)
359 cast = self._c_cast(ent)
360
361 ret = ''
362 if ent.ret:
363 ret = 'return '
364 stmt1 = self.indent
365 stmt1 += 'const struct mapi_table *tbl = u_current_get();'
366 stmt2 = self.indent
367 stmt2 += 'mapi_func func = ((const mapi_func *) tbl)[%d];' % (
368 ent.slot)
369 stmt3 = self.indent
370 stmt3 += '%s((%s) func)(%s);' % (ret, cast, ent.c_args())
371
372 disp = '%s\n{\n%s\n%s\n%s\n}' % (proto, stmt1, stmt2, stmt3)
373
374 if ent.handcode:
375 disp = '#if 0\n' + disp + '\n#endif'
376
377 dispatches.append(disp)
378
379 return '\n\n'.join(dispatches)
380
381 def c_public_initializer(self, prefix):
382 """Return the initializer for public dispatch functions."""
383 names = []
384 for ent in self.entries:
385 if ent.alias:
386 continue
387
388 name = '%s(mapi_func) %s' % (self.indent,
389 self._c_function_call(ent, prefix))
390 names.append(name)
391
392 return ',\n'.join(names)
393
394 def c_stub_string_pool(self):
395 """Return the string pool for use by stubs."""
396 # sort entries by their names
397 sorted_entries = self.entries[:]
398 sorted_entries.sort(lambda x, y: cmp(x.name, y.name))
399
400 pool = []
401 offsets = {}
402 count = 0
403 for ent in sorted_entries:
404 offsets[ent] = count
405 pool.append('%s' % (ent.name))
406 count += len(ent.name) + 1
407
408 pool_str = self.indent + '"' + \
409 ('\\0"\n' + self.indent + '"').join(pool) + '";'
410 return (pool_str, offsets)
411
412 def c_stub_initializer(self, prefix, pool_offsets):
413 """Return the initializer for struct mapi_stub array."""
414 stubs = []
415 for ent in self.entries_sorted_by_names:
416 stubs.append('%s{ (void *) %d, %d, NULL }' % (
417 self.indent, pool_offsets[ent], ent.slot))
418
419 return ',\n'.join(stubs)
420
421 def c_noop_functions(self, prefix, warn_prefix):
422 """Return the noop functions."""
423 noops = []
424 for ent in self.entries:
425 if ent.alias:
426 continue
427
428 proto = self._c_decl(ent, prefix, False, 'static')
429
430 stmt1 = self.indent + '%s(%s);' % (self.noop_warn,
431 self._c_function(ent, warn_prefix, False, True))
432
433 if ent.ret:
434 stmt2 = self.indent + 'return (%s) 0;' % (ent.ret)
435 noop = '%s\n{\n%s\n%s\n}' % (proto, stmt1, stmt2)
436 else:
437 noop = '%s\n{\n%s\n}' % (proto, stmt1)
438
439 noops.append(noop)
440
441 return '\n\n'.join(noops)
442
443 def c_noop_initializer(self, prefix, use_generic):
444 """Return an initializer for the noop dispatch table."""
445 entries = [self._c_function(ent, prefix)
446 for ent in self.entries if not ent.alias]
447 if use_generic:
448 entries = [self.noop_generic] * len(entries)
449
450 entries.extend([self.noop_generic] * ABI_NUM_DYNAMIC_ENTRIES)
451
452 pre = self.indent + '(mapi_func) '
453 return pre + (',\n' + pre).join(entries)
454
455 def c_asm_gcc(self, prefix):
456 asm = []
457
458 asm.append('__asm__(')
459 for ent in self.entries:
460 if not self.need_entry_point(ent):
461 continue
462
463 name = self._c_function(ent, prefix, True, True)
464
465 if ent.handcode:
466 asm.append('#if 0')
467
468 if ent.hidden:
469 asm.append('".hidden "%s"\\n"' % (name))
470
471 if ent.alias:
472 asm.append('".globl "%s"\\n"' % (name))
473 asm.append('".set "%s", "%s"\\n"' % (name,
474 self._c_function(ent.alias, prefix, True, True)))
475 else:
476 asm.append('STUB_ASM_ENTRY(%s)"\\n"' % (name))
477 asm.append('"\\t"STUB_ASM_CODE("%d")"\\n"' % (ent.slot))
478
479 if ent.handcode:
480 asm.append('#endif')
481 asm.append('')
482 asm.append(');')
483
484 return "\n".join(asm)
485
486 def output_for_lib(self):
487 print self.c_notice()
488 print
489 print '#ifdef MAPI_TMP_DEFINES'
490 print self.c_public_includes()
491 print
492 print self.c_public_declarations(self.prefix_lib)
493 print '#undef MAPI_TMP_DEFINES'
494 print '#endif /* MAPI_TMP_DEFINES */'
495
496 if self.lib_need_table_size:
497 print
498 print '#ifdef MAPI_TMP_TABLE'
499 print self.c_mapi_table()
500 print '#undef MAPI_TMP_TABLE'
501 print '#endif /* MAPI_TMP_TABLE */'
502
503 if self.lib_need_noop_array:
504 print
505 print '#ifdef MAPI_TMP_NOOP_ARRAY'
506 print '#ifdef DEBUG'
507 print
508 print self.c_noop_functions(self.prefix_noop, self.prefix_lib)
509 print
510 print 'const mapi_func table_%s_array[] = {' % (self.prefix_noop)
511 print self.c_noop_initializer(self.prefix_noop, False)
512 print '};'
513 print
514 print '#else /* DEBUG */'
515 print
516 print 'const mapi_func table_%s_array[] = {' % (self.prefix_noop)
517 print self.c_noop_initializer(self.prefix_noop, True)
518 print '};'
519 print
520 print '#endif /* DEBUG */'
521 print '#undef MAPI_TMP_NOOP_ARRAY'
522 print '#endif /* MAPI_TMP_NOOP_ARRAY */'
523
524 if self.lib_need_stubs:
525 pool, pool_offsets = self.c_stub_string_pool()
526 print
527 print '#ifdef MAPI_TMP_PUBLIC_STUBS'
528 print 'static const char public_string_pool[] ='
529 print pool
530 print
531 print 'static const struct mapi_stub public_stubs[] = {'
532 print self.c_stub_initializer(self.prefix_lib, pool_offsets)
533 print '};'
534 print '#undef MAPI_TMP_PUBLIC_STUBS'
535 print '#endif /* MAPI_TMP_PUBLIC_STUBS */'
536
537 if self.lib_need_entries:
538 print
539 print '#ifdef MAPI_TMP_PUBLIC_ENTRIES'
540 print self.c_public_dispatches(self.prefix_lib)
541 print
542 print 'static const mapi_func public_entries[] = {'
543 print self.c_public_initializer(self.prefix_lib)
544 print '};'
545 print '#undef MAPI_TMP_PUBLIC_ENTRIES'
546 print '#endif /* MAPI_TMP_PUBLIC_ENTRIES */'
547
548 print
549 print '#ifdef MAPI_TMP_STUB_ASM_GCC'
550 print self.c_asm_gcc(self.prefix_lib)
551 print '#undef MAPI_TMP_STUB_ASM_GCC'
552 print '#endif /* MAPI_TMP_STUB_ASM_GCC */'
553
554 def output_for_app(self):
555 print self.c_notice()
556 print
557 print self.c_private_declarations(self.prefix_app)
558 print
559 print '#ifdef API_TMP_DEFINE_SPEC'
560 print
561 print 'static const char %s_spec[] =' % (self.prefix_app)
562 print self.c_mapi_table_spec()
563 print
564 print 'static const mapi_proc %s_procs[] = {' % (self.prefix_app)
565 print self.c_mapi_table_initializer(self.prefix_app)
566 print '};'
567 print
568 print '#endif /* API_TMP_DEFINE_SPEC */'
569
570 class GLAPIPrinter(ABIPrinter):
571 """OpenGL API Printer"""
572
573 def __init__(self, entries):
574 super(GLAPIPrinter, self).__init__(entries)
575
576 self.api_defines = ['GL_GLEXT_PROTOTYPES']
577 self.api_headers = ['"GL/gl.h"', '"GL/glext.h"']
578 self.api_call = 'GLAPI'
579 self.api_entry = 'APIENTRY'
580 self.api_attrs = ''
581
582 self.prefix_lib = 'gl'
583 self.prefix_app = '_mesa_'
584 self.prefix_noop = 'noop'
585
586 def output_for_app(self):
587 # not used
588 pass
589
590 class ES1APIPrinter(GLAPIPrinter):
591 """OpenGL ES 1.x API Printer"""
592
593 def __init__(self, entries):
594 super(ES1APIPrinter, self).__init__(entries)
595
596 self.api_headers = ['"GLES/gl.h"', '"GLES/glext.h"']
597 self.api_call = 'GL_API'
598 self.api_entry = 'GL_APIENTRY'
599
600 class ES2APIPrinter(GLAPIPrinter):
601 """OpenGL ES 2.x API Printer"""
602
603 def __init__(self, entries):
604 super(ES2APIPrinter, self).__init__(entries)
605
606 self.api_headers = ['"GLES2/gl2.h"', '"GLES2/gl2ext.h"']
607 self.api_call = 'GL_APICALL'
608 self.api_entry = 'GL_APIENTRY'
609
610 class VGAPIPrinter(ABIPrinter):
611 """OpenVG API Printer"""
612
613 def __init__(self, entries):
614 super(VGAPIPrinter, self).__init__(entries)
615
616 self.api_defines = ['VG_VGEXT_PROTOTYPES']
617 self.api_headers = ['"VG/openvg.h"', '"VG/vgext.h"']
618 self.api_call = 'VG_API_CALL'
619 self.api_entry = 'VG_API_ENTRY'
620 self.api_attrs = 'VG_API_EXIT'
621
622 self.prefix_lib = 'vg'
623 self.prefix_app = 'vega'
624 self.prefix_noop = 'noop'
625
626 def parse_args():
627 printers = ['glapi', 'es1api', 'es2api', 'vgapi']
628 modes = ['lib', 'app']
629
630 parser = OptionParser(usage='usage: %prog [options] <filename>')
631 parser.add_option('-p', '--printer', dest='printer',
632 help='printer to use: %s' % (", ".join(printers)))
633 parser.add_option('-m', '--mode', dest='mode',
634 help='target user: %s' % (", ".join(modes)))
635
636 options, args = parser.parse_args()
637 if not args or options.printer not in printers or \
638 options.mode not in modes:
639 parser.print_help()
640 sys.exit(1)
641
642 return (args[0], options)
643
644 def main():
645 printers = {
646 'vgapi': VGAPIPrinter,
647 'glapi': GLAPIPrinter,
648 'es1api': ES1APIPrinter,
649 'es2api': ES2APIPrinter
650 }
651
652 filename, options = parse_args()
653
654 entries = abi_parse(filename)
655 printer = printers[options.printer](entries)
656 if options.mode == 'lib':
657 printer.output_for_lib()
658 else:
659 printer.output_for_app()
660
661 if __name__ == '__main__':
662 main()