1#!/usr/bin/env python 2 3# (C) Copyright IBM Corporation 2004, 2005 4# All Rights Reserved. 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# on the rights to use, copy, modify, merge, publish, distribute, sub 10# license, and/or sell copies of the Software, and to permit persons to whom 11# the Software is furnished to do so, subject to the following conditions: 12# 13# The above copyright notice and this permission notice (including the next 14# paragraph) shall be included in all copies or substantial portions of the 15# Software. 16# 17# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19# FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL 20# IBM AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 22# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 23# IN THE SOFTWARE. 24# 25# Authors: 26# Ian Romanick <idr@us.ibm.com> 27 28from decimal import Decimal 29import xml.etree.ElementTree as ET 30import re, sys, string 31import os.path 32import typeexpr 33import static_data 34 35 36def parse_GL_API( file_name, factory = None ): 37 38 if not factory: 39 factory = gl_item_factory() 40 41 api = factory.create_api() 42 api.parse_file( file_name ) 43 44 # After the XML has been processed, we need to go back and assign 45 # dispatch offsets to the functions that request that their offsets 46 # be assigned by the scripts. Typically this means all functions 47 # that are not part of the ABI. 48 49 for func in api.functionIterateByCategory(): 50 if func.assign_offset: 51 func.offset = api.next_offset; 52 api.next_offset += 1 53 54 return api 55 56 57def is_attr_true( element, name, default = "false" ): 58 """Read a name value from an element's attributes. 59 60 The value read from the attribute list must be either 'true' or 61 'false'. If the value is 'false', zero will be returned. If the 62 value is 'true', non-zero will be returned. An exception will be 63 raised for any other value.""" 64 65 value = element.get( name, default ) 66 if value == "true": 67 return 1 68 elif value == "false": 69 return 0 70 else: 71 raise RuntimeError('Invalid value "%s" for boolean "%s".' % (value, name)) 72 73 74class gl_print_base(object): 75 """Base class of all API pretty-printers. 76 77 In the model-view-controller pattern, this is the view. Any derived 78 class will want to over-ride the printBody, printRealHader, and 79 printRealFooter methods. Some derived classes may want to over-ride 80 printHeader and printFooter, or even Print (though this is unlikely). 81 """ 82 83 def __init__(self): 84 # Name of the script that is generating the output file. 85 # Every derived class should set this to the name of its 86 # source file. 87 88 self.name = "a" 89 90 91 # License on the *generated* source file. This may differ 92 # from the license on the script that is generating the file. 93 # Every derived class should set this to some reasonable 94 # value. 95 # 96 # See license.py for an example of a reasonable value. 97 98 self.license = "The license for this file is unspecified." 99 100 101 # The header_tag is the name of the C preprocessor define 102 # used to prevent multiple inclusion. Typically only 103 # generated C header files need this to be set. Setting it 104 # causes code to be generated automatically in printHeader 105 # and printFooter. 106 107 self.header_tag = None 108 109 110 # List of file-private defines that must be undefined at the 111 # end of the file. This can be used in header files to define 112 # names for use in the file, then undefine them at the end of 113 # the header file. 114 115 self.undef_list = [] 116 return 117 118 119 def Print(self, api): 120 self.printHeader() 121 self.printBody(api) 122 self.printFooter() 123 return 124 125 126 def printHeader(self): 127 """Print the header associated with all files and call the printRealHeader method.""" 128 129 print '/* DO NOT EDIT - This file generated automatically by %s script */' \ 130 % (self.name) 131 print '' 132 print '/*' 133 print (' * ' + self.license.replace('\n', '\n * ')).replace(' \n', '\n') 134 print ' */' 135 print '' 136 if self.header_tag: 137 print '#if !defined( %s )' % (self.header_tag) 138 print '# define %s' % (self.header_tag) 139 print '' 140 self.printRealHeader(); 141 return 142 143 144 def printFooter(self): 145 """Print the header associated with all files and call the printRealFooter method.""" 146 147 self.printRealFooter() 148 149 if self.undef_list: 150 print '' 151 for u in self.undef_list: 152 print "# undef %s" % (u) 153 154 if self.header_tag: 155 print '' 156 print '#endif /* !defined( %s ) */' % (self.header_tag) 157 158 159 def printRealHeader(self): 160 """Print the "real" header for the created file. 161 162 In the base class, this function is empty. All derived 163 classes should over-ride this function.""" 164 return 165 166 167 def printRealFooter(self): 168 """Print the "real" footer for the created file. 169 170 In the base class, this function is empty. All derived 171 classes should over-ride this function.""" 172 return 173 174 175 def printPure(self): 176 """Conditionally define `PURE' function attribute. 177 178 Conditionally defines a preprocessor macro `PURE' that wraps 179 GCC's `pure' function attribute. The conditional code can be 180 easilly adapted to other compilers that support a similar 181 feature. 182 183 The name is also added to the file's undef_list. 184 """ 185 self.undef_list.append("PURE") 186 print """# if defined(__GNUC__) || (defined(__SUNPRO_C) && (__SUNPRO_C >= 0x590)) 187# define PURE __attribute__((pure)) 188# else 189# define PURE 190# endif""" 191 return 192 193 194 def printFastcall(self): 195 """Conditionally define `FASTCALL' function attribute. 196 197 Conditionally defines a preprocessor macro `FASTCALL' that 198 wraps GCC's `fastcall' function attribute. The conditional 199 code can be easilly adapted to other compilers that support a 200 similar feature. 201 202 The name is also added to the file's undef_list. 203 """ 204 205 self.undef_list.append("FASTCALL") 206 print """# if defined(__i386__) && defined(__GNUC__) && !defined(__CYGWIN__) && !defined(__MINGW32__) 207# define FASTCALL __attribute__((fastcall)) 208# else 209# define FASTCALL 210# endif""" 211 return 212 213 214 def printVisibility(self, S, s): 215 """Conditionally define visibility function attribute. 216 217 Conditionally defines a preprocessor macro name S that wraps 218 GCC's visibility function attribute. The visibility used is 219 the parameter s. The conditional code can be easilly adapted 220 to other compilers that support a similar feature. 221 222 The name is also added to the file's undef_list. 223 """ 224 225 self.undef_list.append(S) 226 print """# if defined(__GNUC__) && !defined(__CYGWIN__) && !defined(__MINGW32__) 227# define %s __attribute__((visibility("%s"))) 228# else 229# define %s 230# endif""" % (S, s, S) 231 return 232 233 234 def printNoinline(self): 235 """Conditionally define `NOINLINE' function attribute. 236 237 Conditionally defines a preprocessor macro `NOINLINE' that 238 wraps GCC's `noinline' function attribute. The conditional 239 code can be easilly adapted to other compilers that support a 240 similar feature. 241 242 The name is also added to the file's undef_list. 243 """ 244 245 self.undef_list.append("NOINLINE") 246 print """# if defined(__GNUC__) 247# define NOINLINE __attribute__((noinline)) 248# else 249# define NOINLINE 250# endif""" 251 return 252 253 254def real_function_name(element): 255 name = element.get( "name" ) 256 alias = element.get( "alias" ) 257 258 if alias: 259 return alias 260 else: 261 return name 262 263 264def real_category_name(c): 265 if re.compile("[1-9][0-9]*[.][0-9]+").match(c): 266 return "GL_VERSION_" + c.replace(".", "_") 267 else: 268 return c 269 270 271def classify_category(name, number): 272 """Based on the category name and number, select a numerical class for it. 273 274 Categories are divided into four classes numbered 0 through 3. The 275 classes are: 276 277 0. Core GL versions, sorted by version number. 278 1. ARB extensions, sorted by extension number. 279 2. Non-ARB extensions, sorted by extension number. 280 3. Un-numbered extensions, sorted by extension name. 281 """ 282 283 try: 284 core_version = float(name) 285 except Exception,e: 286 core_version = 0.0 287 288 if core_version > 0.0: 289 cat_type = 0 290 key = name 291 elif name.startswith("GL_ARB_") or name.startswith("GLX_ARB_") or name.startswith("WGL_ARB_"): 292 cat_type = 1 293 key = int(number) 294 else: 295 if number != None: 296 cat_type = 2 297 key = int(number) 298 else: 299 cat_type = 3 300 key = name 301 302 303 return [cat_type, key] 304 305 306def create_parameter_string(parameters, include_names): 307 """Create a parameter string from a list of gl_parameters.""" 308 309 list = [] 310 for p in parameters: 311 if p.is_padding: 312 continue 313 314 if include_names: 315 list.append( p.string() ) 316 else: 317 list.append( p.type_string() ) 318 319 if len(list) == 0: list = ["void"] 320 321 return string.join(list, ", ") 322 323 324class gl_item(object): 325 def __init__(self, element, context, category): 326 self.context = context 327 self.name = element.get( "name" ) 328 self.category = real_category_name( category ) 329 330 return 331 332 333class gl_type( gl_item ): 334 def __init__(self, element, context, category): 335 gl_item.__init__(self, element, context, category) 336 self.size = int( element.get( "size" ), 0 ) 337 338 te = typeexpr.type_expression( None ) 339 tn = typeexpr.type_node() 340 tn.size = int( element.get( "size" ), 0 ) 341 tn.integer = not is_attr_true( element, "float" ) 342 tn.unsigned = is_attr_true( element, "unsigned" ) 343 tn.pointer = is_attr_true( element, "pointer" ) 344 tn.name = "GL" + self.name 345 te.set_base_type_node( tn ) 346 347 self.type_expr = te 348 return 349 350 351 def get_type_expression(self): 352 return self.type_expr 353 354 355class gl_enum( gl_item ): 356 def __init__(self, element, context, category): 357 gl_item.__init__(self, element, context, category) 358 self.value = int( element.get( "value" ), 0 ) 359 360 temp = element.get( "count" ) 361 if not temp or temp == "?": 362 self.default_count = -1 363 else: 364 try: 365 c = int(temp) 366 except Exception,e: 367 raise RuntimeError('Invalid count value "%s" for enum "%s" in function "%s" when an integer was expected.' % (temp, self.name, n)) 368 369 self.default_count = c 370 371 return 372 373 374 def priority(self): 375 """Calculate a 'priority' for this enum name. 376 377 When an enum is looked up by number, there may be many 378 possible names, but only one is the 'prefered' name. The 379 priority is used to select which name is the 'best'. 380 381 Highest precedence is given to core GL name. ARB extension 382 names have the next highest, followed by EXT extension names. 383 Vendor extension names are the lowest. 384 """ 385 386 if self.name.endswith( "_BIT" ): 387 bias = 1 388 else: 389 bias = 0 390 391 if self.category.startswith( "GL_VERSION_" ): 392 priority = 0 393 elif self.category.startswith( "GL_ARB_" ): 394 priority = 2 395 elif self.category.startswith( "GL_EXT_" ): 396 priority = 4 397 else: 398 priority = 6 399 400 return priority + bias 401 402 403 404class gl_parameter(object): 405 def __init__(self, element, context): 406 self.name = element.get( "name" ) 407 408 ts = element.get( "type" ) 409 self.type_expr = typeexpr.type_expression( ts, context ) 410 411 temp = element.get( "variable_param" ) 412 if temp: 413 self.count_parameter_list = temp.split( ' ' ) 414 else: 415 self.count_parameter_list = [] 416 417 # The count tag can be either a numeric string or the name of 418 # a variable. If it is the name of a variable, the int(c) 419 # statement will throw an exception, and the except block will 420 # take over. 421 422 c = element.get( "count" ) 423 try: 424 count = int(c) 425 self.count = count 426 self.counter = None 427 except Exception,e: 428 count = 1 429 self.count = 0 430 self.counter = c 431 432 self.count_scale = int(element.get( "count_scale", "1" )) 433 434 elements = (count * self.count_scale) 435 if elements == 1: 436 elements = 0 437 438 #if ts == "GLdouble": 439 # print '/* stack size -> %s = %u (before)*/' % (self.name, self.type_expr.get_stack_size()) 440 # print '/* # elements = %u */' % (elements) 441 self.type_expr.set_elements( elements ) 442 #if ts == "GLdouble": 443 # print '/* stack size -> %s = %u (after) */' % (self.name, self.type_expr.get_stack_size()) 444 445 self.is_client_only = is_attr_true( element, 'client_only' ) 446 self.is_counter = is_attr_true( element, 'counter' ) 447 self.is_output = is_attr_true( element, 'output' ) 448 449 450 # Pixel data has special parameters. 451 452 self.width = element.get('img_width') 453 self.height = element.get('img_height') 454 self.depth = element.get('img_depth') 455 self.extent = element.get('img_extent') 456 457 self.img_xoff = element.get('img_xoff') 458 self.img_yoff = element.get('img_yoff') 459 self.img_zoff = element.get('img_zoff') 460 self.img_woff = element.get('img_woff') 461 462 self.img_format = element.get('img_format') 463 self.img_type = element.get('img_type') 464 self.img_target = element.get('img_target') 465 466 self.img_pad_dimensions = is_attr_true( element, 'img_pad_dimensions' ) 467 self.img_null_flag = is_attr_true( element, 'img_null_flag' ) 468 self.img_send_null = is_attr_true( element, 'img_send_null' ) 469 470 self.is_padding = is_attr_true( element, 'padding' ) 471 return 472 473 474 def compatible(self, other): 475 return 1 476 477 478 def is_array(self): 479 return self.is_pointer() 480 481 482 def is_pointer(self): 483 return self.type_expr.is_pointer() 484 485 486 def is_image(self): 487 if self.width: 488 return 1 489 else: 490 return 0 491 492 493 def is_variable_length(self): 494 return len(self.count_parameter_list) or self.counter 495 496 497 def is_64_bit(self): 498 count = self.type_expr.get_element_count() 499 if count: 500 if (self.size() / count) == 8: 501 return 1 502 else: 503 if self.size() == 8: 504 return 1 505 506 return 0 507 508 509 def string(self): 510 return self.type_expr.original_string + " " + self.name 511 512 513 def type_string(self): 514 return self.type_expr.original_string 515 516 517 def get_base_type_string(self): 518 return self.type_expr.get_base_name() 519 520 521 def get_dimensions(self): 522 if not self.width: 523 return [ 0, "0", "0", "0", "0" ] 524 525 dim = 1 526 w = self.width 527 h = "1" 528 d = "1" 529 e = "1" 530 531 if self.height: 532 dim = 2 533 h = self.height 534 535 if self.depth: 536 dim = 3 537 d = self.depth 538 539 if self.extent: 540 dim = 4 541 e = self.extent 542 543 return [ dim, w, h, d, e ] 544 545 546 def get_stack_size(self): 547 return self.type_expr.get_stack_size() 548 549 550 def size(self): 551 if self.is_image(): 552 return 0 553 else: 554 return self.type_expr.get_element_size() 555 556 557 def get_element_count(self): 558 c = self.type_expr.get_element_count() 559 if c == 0: 560 return 1 561 562 return c 563 564 565 def size_string(self, use_parens = 1): 566 s = self.size() 567 if self.counter or self.count_parameter_list: 568 list = [ "compsize" ] 569 570 if self.counter and self.count_parameter_list: 571 list.append( self.counter ) 572 elif self.counter: 573 list = [ self.counter ] 574 575 if s > 1: 576 list.append( str(s) ) 577 578 if len(list) > 1 and use_parens : 579 return "safe_mul(%s)" % (string.join(list, ", ")) 580 else: 581 return string.join(list, " * ") 582 583 elif self.is_image(): 584 return "compsize" 585 else: 586 return str(s) 587 588 589 def format_string(self): 590 if self.type_expr.original_string == "GLenum": 591 return "0x%x" 592 else: 593 return self.type_expr.format_string() 594 595 596class gl_function( gl_item ): 597 def __init__(self, element, context): 598 self.context = context 599 self.name = None 600 601 self.entry_points = [] 602 self.return_type = "void" 603 self.parameters = [] 604 self.offset = -1 605 self.initialized = 0 606 self.images = [] 607 self.exec_flavor = 'mesa' 608 self.desktop = True 609 self.deprecated = None 610 611 # self.entry_point_api_map[name][api] is a decimal value 612 # indicating the earliest version of the given API in which 613 # each entry point exists. Every entry point is included in 614 # the first level of the map; the second level of the map only 615 # lists APIs which contain the entry point in at least one 616 # version. For example, 617 # self.entry_point_api_map['ClipPlanex'] == { 'es1': 618 # Decimal('1.1') }. 619 self.entry_point_api_map = {} 620 621 # self.api_map[api] is a decimal value indicating the earliest 622 # version of the given API in which ANY alias for the function 623 # exists. The map only lists APIs which contain the function 624 # in at least one version. For example, for the ClipPlanex 625 # function, self.entry_point_api_map == { 'es1': 626 # Decimal('1.1') }. 627 self.api_map = {} 628 629 self.assign_offset = False 630 631 self.static_entry_points = [] 632 633 # Track the parameter string (for the function prototype) 634 # for each entry-point. This is done because some functions 635 # change their prototype slightly when promoted from extension 636 # to ARB extension to core. glTexImage3DEXT and glTexImage3D 637 # are good examples of this. Scripts that need to generate 638 # code for these differing aliases need to real prototype 639 # for each entry-point. Otherwise, they may generate code 640 # that won't compile. 641 642 self.entry_point_parameters = {} 643 644 self.process_element( element ) 645 646 return 647 648 649 def process_element(self, element): 650 name = element.get( "name" ) 651 alias = element.get( "alias" ) 652 653 if name in static_data.functions: 654 self.static_entry_points.append(name) 655 656 self.entry_points.append( name ) 657 658 self.entry_point_api_map[name] = {} 659 for api in ('es1', 'es2'): 660 version_str = element.get(api, 'none') 661 assert version_str is not None 662 if version_str != 'none': 663 version_decimal = Decimal(version_str) 664 self.entry_point_api_map[name][api] = version_decimal 665 if api not in self.api_map or \ 666 version_decimal < self.api_map[api]: 667 self.api_map[api] = version_decimal 668 669 exec_flavor = element.get('exec') 670 if exec_flavor: 671 self.exec_flavor = exec_flavor 672 673 deprecated = element.get('deprecated', 'none') 674 if deprecated != 'none': 675 self.deprecated = Decimal(deprecated) 676 677 if not is_attr_true(element, 'desktop', 'true'): 678 self.desktop = False 679 680 if alias: 681 true_name = alias 682 else: 683 true_name = name 684 685 # Only try to set the offset when a non-alias entry-point 686 # is being processed. 687 688 if name in static_data.offsets: 689 self.offset = static_data.offsets[name] 690 else: 691 self.offset = -1 692 self.assign_offset = self.exec_flavor != "skip" or name in static_data.unused_functions 693 694 if not self.name: 695 self.name = true_name 696 elif self.name != true_name: 697 raise RuntimeError("Function true name redefined. Was %s, now %s." % (self.name, true_name)) 698 699 700 # There are two possible cases. The first time an entry-point 701 # with data is seen, self.initialized will be 0. On that 702 # pass, we just fill in the data. The next time an 703 # entry-point with data is seen, self.initialized will be 1. 704 # On that pass we have to make that the new values match the 705 # valuse from the previous entry-point. 706 707 parameters = [] 708 return_type = "void" 709 for child in element.getchildren(): 710 if child.tag == "return": 711 return_type = child.get( "type", "void" ) 712 elif child.tag == "param": 713 param = self.context.factory.create_parameter(child, self.context) 714 parameters.append( param ) 715 716 717 if self.initialized: 718 if self.return_type != return_type: 719 raise RuntimeError( "Return type changed in %s. Was %s, now %s." % (name, self.return_type, return_type)) 720 721 if len(parameters) != len(self.parameters): 722 raise RuntimeError( "Parameter count mismatch in %s. Was %d, now %d." % (name, len(self.parameters), len(parameters))) 723 724 for j in range(0, len(parameters)): 725 p1 = parameters[j] 726 p2 = self.parameters[j] 727 if not p1.compatible( p2 ): 728 raise RuntimeError( 'Parameter type mismatch in %s. "%s" was "%s", now "%s".' % (name, p2.name, p2.type_expr.original_string, p1.type_expr.original_string)) 729 730 731 if true_name == name or not self.initialized: 732 self.return_type = return_type 733 self.parameters = parameters 734 735 for param in self.parameters: 736 if param.is_image(): 737 self.images.append( param ) 738 739 if element.getchildren(): 740 self.initialized = 1 741 self.entry_point_parameters[name] = parameters 742 else: 743 self.entry_point_parameters[name] = [] 744 745 return 746 747 def filter_entry_points(self, entry_point_list): 748 """Filter out entry points not in entry_point_list.""" 749 if not self.initialized: 750 raise RuntimeError('%s is not initialized yet' % self.name) 751 752 entry_points = [] 753 for ent in self.entry_points: 754 if ent not in entry_point_list: 755 if ent in self.static_entry_points: 756 self.static_entry_points.remove(ent) 757 self.entry_point_parameters.pop(ent) 758 else: 759 entry_points.append(ent) 760 761 if not entry_points: 762 raise RuntimeError('%s has no entry point after filtering' % self.name) 763 764 self.entry_points = entry_points 765 if self.name not in entry_points: 766 # use the first remaining entry point 767 self.name = entry_points[0] 768 self.parameters = self.entry_point_parameters[entry_points[0]] 769 770 def get_images(self): 771 """Return potentially empty list of input images.""" 772 return self.images 773 774 775 def parameterIterator(self, name = None): 776 if name is not None: 777 return self.entry_point_parameters[name].__iter__(); 778 else: 779 return self.parameters.__iter__(); 780 781 782 def get_parameter_string(self, entrypoint = None): 783 if entrypoint: 784 params = self.entry_point_parameters[ entrypoint ] 785 else: 786 params = self.parameters 787 788 return create_parameter_string( params, 1 ) 789 790 def get_called_parameter_string(self): 791 p_string = "" 792 comma = "" 793 794 for p in self.parameterIterator(): 795 if p.is_padding: 796 continue 797 p_string = p_string + comma + p.name 798 comma = ", " 799 800 return p_string 801 802 803 def is_abi(self): 804 return (self.offset >= 0 and not self.assign_offset) 805 806 def is_static_entry_point(self, name): 807 return name in self.static_entry_points 808 809 def dispatch_name(self): 810 if self.name in self.static_entry_points: 811 return self.name 812 else: 813 return "_dispatch_stub_%u" % (self.offset) 814 815 def static_name(self, name): 816 if name in self.static_entry_points: 817 return name 818 else: 819 return "_dispatch_stub_%u" % (self.offset) 820 821 def entry_points_for_api_version(self, api, version = None): 822 """Return a list of the entry point names for this function 823 which are supported in the given API (and optionally, version). 824 825 Use the decimal.Decimal type to precisely express non-integer 826 versions. 827 """ 828 result = [] 829 for entry_point, api_to_ver in self.entry_point_api_map.iteritems(): 830 if api not in api_to_ver: 831 continue 832 if version is not None and version < api_to_ver[api]: 833 continue 834 result.append(entry_point) 835 return result 836 837 838class gl_item_factory(object): 839 """Factory to create objects derived from gl_item.""" 840 841 def create_function(self, element, context): 842 return gl_function(element, context) 843 844 def create_type(self, element, context, category): 845 return gl_type(element, context, category) 846 847 def create_enum(self, element, context, category): 848 return gl_enum(element, context, category) 849 850 def create_parameter(self, element, context): 851 return gl_parameter(element, context) 852 853 def create_api(self): 854 return gl_api(self) 855 856 857class gl_api(object): 858 def __init__(self, factory): 859 self.functions_by_name = {} 860 self.enums_by_name = {} 861 self.types_by_name = {} 862 863 self.category_dict = {} 864 self.categories = [{}, {}, {}, {}] 865 866 self.factory = factory 867 868 self.next_offset = 0 869 870 typeexpr.create_initial_types() 871 return 872 873 def filter_functions(self, entry_point_list): 874 """Filter out entry points not in entry_point_list.""" 875 functions_by_name = {} 876 for func in self.functions_by_name.itervalues(): 877 entry_points = [ent for ent in func.entry_points if ent in entry_point_list] 878 if entry_points: 879 func.filter_entry_points(entry_points) 880 functions_by_name[func.name] = func 881 882 self.functions_by_name = functions_by_name 883 884 def filter_functions_by_api(self, api, version = None): 885 """Filter out entry points not in the given API (or 886 optionally, not in the given version of the given API). 887 """ 888 functions_by_name = {} 889 for func in self.functions_by_name.itervalues(): 890 entry_points = func.entry_points_for_api_version(api, version) 891 if entry_points: 892 func.filter_entry_points(entry_points) 893 functions_by_name[func.name] = func 894 895 self.functions_by_name = functions_by_name 896 897 898 def parse_file(self, file_name): 899 doc = ET.parse( file_name ) 900 self.process_element(file_name, doc) 901 902 903 def process_element(self, file_name, doc): 904 element = doc.getroot() 905 if element.tag == "OpenGLAPI": 906 self.process_OpenGLAPI(file_name, element) 907 return 908 909 910 def process_OpenGLAPI(self, file_name, element): 911 for child in element.getchildren(): 912 if child.tag == "category": 913 self.process_category( child ) 914 elif child.tag == "OpenGLAPI": 915 self.process_OpenGLAPI( file_name, child ) 916 elif child.tag == '{http://www.w3.org/2001/XInclude}include': 917 href = child.get('href') 918 href = os.path.join(os.path.dirname(file_name), href) 919 self.parse_file(href) 920 921 return 922 923 924 def process_category(self, cat): 925 cat_name = cat.get( "name" ) 926 cat_number = cat.get( "number" ) 927 928 [cat_type, key] = classify_category(cat_name, cat_number) 929 self.categories[cat_type][key] = [cat_name, cat_number] 930 931 for child in cat.getchildren(): 932 if child.tag == "function": 933 func_name = real_function_name( child ) 934 935 temp_name = child.get( "name" ) 936 self.category_dict[ temp_name ] = [cat_name, cat_number] 937 938 if self.functions_by_name.has_key( func_name ): 939 func = self.functions_by_name[ func_name ] 940 func.process_element( child ) 941 else: 942 func = self.factory.create_function( child, self ) 943 self.functions_by_name[ func_name ] = func 944 945 if func.offset >= self.next_offset: 946 self.next_offset = func.offset + 1 947 948 949 elif child.tag == "enum": 950 enum = self.factory.create_enum( child, self, cat_name ) 951 self.enums_by_name[ enum.name ] = enum 952 elif child.tag == "type": 953 t = self.factory.create_type( child, self, cat_name ) 954 self.types_by_name[ "GL" + t.name ] = t 955 956 return 957 958 959 def functionIterateByCategory(self, cat = None): 960 """Iterate over functions by category. 961 962 If cat is None, all known functions are iterated in category 963 order. See classify_category for details of the ordering. 964 Within a category, functions are sorted by name. If cat is 965 not None, then only functions in that category are iterated. 966 """ 967 lists = [{}, {}, {}, {}] 968 969 for func in self.functionIterateAll(): 970 [cat_name, cat_number] = self.category_dict[func.name] 971 972 if (cat == None) or (cat == cat_name): 973 [func_cat_type, key] = classify_category(cat_name, cat_number) 974 975 if not lists[func_cat_type].has_key(key): 976 lists[func_cat_type][key] = {} 977 978 lists[func_cat_type][key][func.name] = func 979 980 981 functions = [] 982 for func_cat_type in range(0,4): 983 keys = lists[func_cat_type].keys() 984 keys.sort() 985 986 for key in keys: 987 names = lists[func_cat_type][key].keys() 988 names.sort() 989 990 for name in names: 991 functions.append(lists[func_cat_type][key][name]) 992 993 return functions.__iter__() 994 995 996 def functionIterateByOffset(self): 997 max_offset = -1 998 for func in self.functions_by_name.itervalues(): 999 if func.offset > max_offset: 1000 max_offset = func.offset 1001 1002 1003 temp = [None for i in range(0, max_offset + 1)] 1004 for func in self.functions_by_name.itervalues(): 1005 if func.offset != -1: 1006 temp[ func.offset ] = func 1007 1008 1009 list = [] 1010 for i in range(0, max_offset + 1): 1011 if temp[i]: 1012 list.append(temp[i]) 1013 1014 return list.__iter__(); 1015 1016 1017 def functionIterateAll(self): 1018 return self.functions_by_name.itervalues() 1019 1020 1021 def enumIterateByName(self): 1022 keys = self.enums_by_name.keys() 1023 keys.sort() 1024 1025 list = [] 1026 for enum in keys: 1027 list.append( self.enums_by_name[ enum ] ) 1028 1029 return list.__iter__() 1030 1031 1032 def categoryIterate(self): 1033 """Iterate over categories. 1034 1035 Iterate over all known categories in the order specified by 1036 classify_category. Each iterated value is a tuple of the 1037 name and number (which may be None) of the category. 1038 """ 1039 1040 list = [] 1041 for cat_type in range(0,4): 1042 keys = self.categories[cat_type].keys() 1043 keys.sort() 1044 1045 for key in keys: 1046 list.append(self.categories[cat_type][key]) 1047 1048 return list.__iter__() 1049 1050 1051 def get_category_for_name( self, name ): 1052 if self.category_dict.has_key(name): 1053 return self.category_dict[name] 1054 else: 1055 return ["<unknown category>", None] 1056 1057 1058 def typeIterate(self): 1059 return self.types_by_name.itervalues() 1060 1061 1062 def find_type( self, type_name ): 1063 if type_name in self.types_by_name: 1064 return self.types_by_name[ type_name ].type_expr 1065 else: 1066 print "Unable to find base type matching \"%s\"." % (type_name) 1067 return None 1068