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