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