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