mapi: Minor ABIPrinter refactoring.
[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
49 def c_prototype(self):
50 return '%s %s(%s)' % (self.c_return(), self.name, self.c_params())
51
52 def c_return(self):
53 ret = self.ret
54 if not ret:
55 ret = 'void'
56
57 return ret
58
59 def c_params(self):
60 """Return the parameter list used in the entry prototype."""
61 c_params = []
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)
66 if not c_params:
67 c_params.append('void')
68
69 return ", ".join(c_params)
70
71 def c_args(self):
72 """Return the argument list used in the entry invocation."""
73 c_args = []
74 for t, n, a in self.params:
75 c_args.append(n)
76
77 return ", ".join(c_args)
78
79 def _parse(self, cols):
80 ret = cols.pop(0)
81 if ret == 'void':
82 ret = None
83
84 name = cols.pop(0)
85
86 params = []
87 if not cols:
88 raise Exception(cols)
89 elif len(cols) == 1 and cols[0] == 'void':
90 pass
91 else:
92 for val in cols:
93 params.append(self._parse_param(val))
94
95 self.ret = ret
96 self.name = name
97 self.params = params
98
99 def _parse_param(self, c_param):
100 m = self._match_c_param.match(c_param)
101 if not m:
102 raise Exception('unrecognized param ' + c_param)
103
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
108
109 return (c_type, c_name, c_array)
110
111 def __str__(self):
112 return self.c_prototype()
113
114 def __cmp__(self, other):
115 # compare slot, alias, and then name
116 res = cmp(self.slot, other.slot)
117 if not res:
118 if not self.alias:
119 res = -1
120 elif not other.alias:
121 res = 1
122
123 if not res:
124 res = cmp(self.name, other.name)
125
126 return res
127
128 def abi_parse_line(line):
129 cols = [col.strip() for col in line.split(',')]
130
131 attrs = {
132 'slot': -1,
133 'hidden': False,
134 'alias': None,
135 }
136
137 # extract attributes from the first column
138 vals = cols[0].split(':')
139 while len(vals) > 1:
140 val = vals.pop(0)
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:]
147 elif not val:
148 pass
149 else:
150 raise Exception('unknown attribute %s' % val)
151 cols[0] = vals[0]
152
153 return (attrs, cols)
154
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()]
160
161 entry_dict = {}
162 next_slot = 0
163 for line in lines:
164 attrs, cols = abi_parse_line(line)
165
166 # post-process attributes
167 if attrs['alias']:
168 try:
169 alias = entry_dict[attrs['alias']]
170 except KeyError:
171 raise Exception('failed to alias %s' % attrs['alias'])
172 if alias.alias:
173 raise Exception('recursive alias %s' % ent.name)
174 slot = alias.slot
175 attrs['alias'] = alias
176 else:
177 slot = next_slot
178 next_slot += 1
179
180 if attrs['slot'] < 0:
181 attrs['slot'] = slot
182 elif attrs['slot'] != slot:
183 raise Exception('invalid slot in %s' % (line))
184
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
189
190 entries = entry_dict.values()
191 entries.sort()
192
193 # sanity check
194 i = 0
195 for slot in xrange(next_slot):
196 if entries[i].slot != slot:
197 raise Exception('entries are not ordered by slots')
198 if entries[i].alias:
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:
202 i += 1
203 if i < len(entries):
204 raise Exception('there are %d invalid entries' % (len(entries) - 1))
205
206 return entries
207
208 class ABIPrinter(object):
209 """MAPI Printer"""
210
211 def __init__(self, entries):
212 self.entries = entries
213
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))
217
218 self.indent = ' ' * 3
219 self.noop_warn = 'noop_warn'
220 self.noop_generic = 'noop_generic'
221
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'
227
228 def c_header(self):
229 return '/* This file is automatically generated by mapi_abi.py. Do not modify. */'
230
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)
236
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:
241 if not ent.alias:
242 num_static_entries += 1
243
244 return ('#define MAPI_TABLE_NUM_STATIC %d\n' + \
245 '#define MAPI_TABLE_NUM_DYNAMIC %d') % (
246 num_static_entries, ABI_NUM_DYNAMIC_ENTRIES)
247
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)
254
255 def c_mapi_table_spec(self):
256 """Return the spec for mapi_init."""
257 specv1 = []
258 line = '"1'
259 for ent in self.entries:
260 if not ent.alias:
261 line += '\\0"\n'
262 specv1.append(line)
263 line = '"'
264 line += '%s\\0' % ent.name
265 line += '";'
266 specv1.append(line)
267
268 return self.indent + self.indent.join(specv1)
269
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)
275
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())
280 if export:
281 decl = export + ' ' + decl
282 if self.api_attrs:
283 decl += ' ' + self.api_attrs
284
285 return decl
286
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())
291
292 return cast
293
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]
298
299 return "\n".join(decls)
300
301 def c_public_dispatches(self, prefix):
302 """Return the public dispatch functions."""
303 dispatches = []
304 for ent in self.entries:
305 if ent.hidden:
306 continue
307
308 proto = self._c_decl(ent, prefix, self.api_call)
309 cast = self._c_cast(ent)
310
311 ret = ''
312 if ent.ret:
313 ret = 'return '
314 stmt1 = self.indent
315 stmt1 += 'const struct mapi_table *tbl = u_current_get();'
316 stmt2 = self.indent
317 stmt2 += 'mapi_func func = ((const mapi_func *) tbl)[%d];' % (
318 ent.slot)
319 stmt3 = self.indent
320 stmt3 += '%s((%s) func)(%s);' % (ret, cast, ent.c_args())
321
322 disp = '%s\n{\n%s\n%s\n%s\n}' % (proto, stmt1, stmt2, stmt3)
323 dispatches.append(disp)
324
325 return '\n\n'.join(dispatches)
326
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))
332
333 pool = []
334 offsets = {}
335 count = 0
336 for ent in sorted_entries:
337 offsets[ent] = count
338 pool.append('%s' % (ent.name))
339 count += len(ent.name) + 1
340
341 pool_str = self.indent + '"' + \
342 ('\\0"\n' + self.indent + '"').join(pool) + '";'
343 return (pool_str, offsets)
344
345 def c_stub_initializer(self, prefix, pool_offsets):
346 """Return the initializer for struct mapi_stub array."""
347 stubs = []
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]))
352
353 return ',\n'.join(stubs)
354
355 def c_noop_functions(self, prefix, warn_prefix):
356 """Return the noop functions."""
357 noops = []
358 for ent in self.entries:
359 if ent.alias:
360 continue
361
362 proto = self._c_decl(ent, prefix, 'static')
363
364 stmt1 = self.indent + '%s(%s);' % (self.noop_warn,
365 self._c_function(ent, warn_prefix, True))
366
367 if ent.ret:
368 stmt2 = self.indent + 'return (%s) 0;' % (ent.ret)
369 noop = '%s\n{\n%s\n%s\n}' % (proto, stmt1, stmt2)
370 else:
371 noop = '%s\n{\n%s\n}' % (proto, stmt1)
372
373 noops.append(noop)
374
375 return '\n\n'.join(noops)
376
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]
381 if use_generic:
382 entries = [self.noop_generic] * len(entries)
383
384 entries.extend([self.noop_generic] * ABI_NUM_DYNAMIC_ENTRIES)
385
386 pre = self.indent + '(mapi_func) '
387 return pre + (',\n' + pre).join(entries)
388
389 def c_asm_gcc(self, prefix):
390 asm = []
391
392 asm.append('__asm__(')
393 for ent in self.entries:
394 name = self._c_function(ent, prefix, True)
395
396 if ent.hidden:
397 asm.append('".hidden "%s"\\n"' % (name))
398
399 if ent.alias:
400 asm.append('".globl "%s"\\n"' % (name))
401 asm.append('".set "%s", "%s"\\n"' % (name,
402 self._c_function(ent.alias, prefix, True)))
403 else:
404 asm.append('STUB_ASM_ENTRY(%s)"\\n"' % (name))
405 asm.append('"\\t"STUB_ASM_CODE("%d")"\\n"' % (ent.slot))
406 asm.append(');')
407
408 return "\n".join(asm)
409
410 def output_for_lib(self):
411 print self.c_header()
412 print
413 print '#ifdef MAPI_TMP_DEFINES'
414 print self.c_includes()
415 print '#undef MAPI_TMP_DEFINES'
416 print '#endif /* MAPI_TMP_DEFINES */'
417 print
418 print '#ifdef MAPI_TMP_TABLE'
419 print self.c_mapi_table()
420 print '#undef MAPI_TMP_TABLE'
421 print '#endif /* MAPI_TMP_TABLE */'
422 print
423
424 pool, pool_offsets = self.c_stub_string_pool()
425 print '#ifdef MAPI_TMP_PUBLIC_STUBS'
426 print 'static const char public_string_pool[] ='
427 print pool
428 print
429 print 'static const struct mapi_stub public_stubs[] = {'
430 print self.c_stub_initializer(self.prefix_lib, pool_offsets)
431 print '};'
432 print '#undef MAPI_TMP_PUBLIC_STUBS'
433 print '#endif /* MAPI_TMP_PUBLIC_STUBS */'
434 print
435
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 */'
440 print
441
442 print '#ifdef MAPI_TMP_NOOP_ARRAY'
443 print '#ifdef DEBUG'
444 print
445 print self.c_noop_functions(self.prefix_noop, self.prefix_lib)
446 print
447 print 'const mapi_func table_%s_array[] = {' % (self.prefix_noop)
448 print self.c_noop_initializer(self.prefix_noop, False)
449 print '};'
450 print
451 print '#else /* DEBUG */'
452 print
453 print 'const mapi_func table_%s_array[] = {' % (self.prefix_noop)
454 print self.c_noop_initializer(self.prefix_noop, True)
455 print '};'
456 print '#endif /* DEBUG */'
457 print '#undef MAPI_TMP_NOOP_ARRAY'
458 print '#endif /* MAPI_TMP_NOOP_ARRAY */'
459 print
460
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 */'
465
466 def output_for_app(self):
467 print self.c_header()
468 print
469 print self.c_private_declarations(self.prefix_app)
470 print
471 print '#ifdef API_TMP_DEFINE_SPEC'
472 print
473 print 'static const char %s_spec[] =' % (self.prefix_app)
474 print self.c_mapi_table_spec()
475 print
476 print 'static const mapi_proc %s_procs[] = {' % (self.prefix_app)
477 print self.c_mapi_table_initializer(self.prefix_app)
478 print '};'
479 print
480 print '#endif /* API_TMP_DEFINE_SPEC */'
481
482 class GLAPIPrinter(ABIPrinter):
483 """OpenGL API Printer"""
484
485 def __init__(self, entries):
486 super(GLAPIPrinter, self).__init__(entries)
487
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'
492 self.api_attrs = ''
493
494 self.prefix_lib = 'gl'
495 self.prefix_app = '_mesa_'
496 self.prefix_noop = 'noop'
497
498 def output_for_app(self):
499 # not used
500 pass
501
502 class ES1APIPrinter(GLAPIPrinter):
503 """OpenGL ES 1.x API Printer"""
504
505 def __init__(self, entries):
506 super(ES1APIPrinter, self).__init__(entries)
507
508 self.api_headers = ['"GLES/gl.h"', '"GLES/glext.h"']
509 self.api_call = 'GL_API'
510 self.api_entry = 'GL_APIENTRY'
511
512 class ES2APIPrinter(GLAPIPrinter):
513 """OpenGL ES 2.x API Printer"""
514
515 def __init__(self, entries):
516 super(ES2APIPrinter, self).__init__(entries)
517
518 self.api_headers = ['"GLES2/gl2.h"', '"GLES2/gl2ext.h"']
519 self.api_call = 'GL_APICALL'
520 self.api_entry = 'GL_APIENTRY'
521
522 class VGAPIPrinter(ABIPrinter):
523 """OpenVG API Printer"""
524
525 def __init__(self, entries):
526 super(VGAPIPrinter, self).__init__(entries)
527
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'
533
534 self.prefix_lib = 'vg'
535 self.prefix_app = 'vega'
536 self.prefix_noop = 'noop'
537
538 def parse_args():
539 printers = ['glapi', 'es1api', 'es2api', 'vgapi']
540 modes = ['lib', 'app']
541
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)))
547
548 options, args = parser.parse_args()
549 if not args or options.printer not in printers or \
550 options.mode not in modes:
551 parser.print_help()
552 sys.exit(1)
553
554 return (args[0], options)
555
556 def main():
557 printers = {
558 'vgapi': VGAPIPrinter,
559 'glapi': GLAPIPrinter,
560 'es1api': ES1APIPrinter,
561 'es2api': ES2APIPrinter
562 }
563
564 filename, options = parse_args()
565
566 entries = abi_parse(filename)
567 printer = printers[options.printer](entries)
568 if options.mode == 'lib':
569 printer.output_for_lib()
570 else:
571 printer.output_for_app()
572
573 if __name__ == '__main__':
574 main()