3 # (C) Copyright IBM Corporation 2004, 2005
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:
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
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
26 # Ian Romanick <idr@us.ibm.com>
29 import re
, sys
, string
33 def parse_GL_API( file_name
, factory
= None ):
34 doc
= libxml2
.readFile( file_name
, None, libxml2
.XML_PARSE_XINCLUDE
+ libxml2
.XML_PARSE_NOBLANKS
+ libxml2
.XML_PARSE_DTDVALID
+ libxml2
.XML_PARSE_DTDATTR
+ libxml2
.XML_PARSE_DTDLOAD
+ libxml2
.XML_PARSE_NOENT
)
35 ret
= doc
.xincludeProcess()
38 factory
= gl_item_factory()
40 api
= factory
.create_item( "api", None, None )
41 api
.process_element( doc
)
43 # After the XML has been processed, we need to go back and assign
44 # dispatch offsets to the functions that request that their offsets
45 # be assigned by the scripts. Typically this means all functions
46 # that are not part of the ABI.
48 for func
in api
.functionIterateByCategory():
49 if func
.assign_offset
:
50 func
.offset
= api
.next_offset
;
58 def is_attr_true( element
, name
):
59 """Read a name value from an element's attributes.
61 The value read from the attribute list must be either 'true' or
62 'false'. If the value is 'false', zero will be returned. If the
63 value is 'true', non-zero will be returned. An exception will be
64 raised for any other value."""
66 value
= element
.nsProp( name
, None )
69 elif value
== "false":
72 raise RuntimeError('Invalid value "%s" for boolean "%s".' % (value
, name
))
75 class gl_print_base(object):
76 """Base class of all API pretty-printers.
78 In the model-view-controller pattern, this is the view. Any derived
79 class will want to over-ride the printBody, printRealHader, and
80 printRealFooter methods. Some derived classes may want to over-ride
81 printHeader and printFooter, or even Print (though this is unlikely).
85 # Name of the script that is generating the output file.
86 # Every derived class should set this to the name of its
92 # License on the *generated* source file. This may differ
93 # from the license on the script that is generating the file.
94 # Every derived class should set this to some reasonable
97 # See license.py for an example of a reasonable value.
99 self
.license
= "The license for this file is unspecified."
102 # The header_tag is the name of the C preprocessor define
103 # used to prevent multiple inclusion. Typically only
104 # generated C header files need this to be set. Setting it
105 # causes code to be generated automatically in printHeader
108 self
.header_tag
= None
111 # List of file-private defines that must be undefined at the
112 # end of the file. This can be used in header files to define
113 # names for use in the file, then undefine them at the end of
120 def Print(self
, api
):
127 def printHeader(self
):
128 """Print the header associated with all files and call the printRealHeader method."""
130 print '/* DO NOT EDIT - This file generated automatically by %s script */' \
134 print ' * ' + self
.license
.replace('\n', '\n * ')
138 print '#if !defined( %s )' % (self
.header_tag
)
139 print '# define %s' % (self
.header_tag
)
141 self
.printRealHeader();
145 def printFooter(self
):
146 """Print the header associated with all files and call the printRealFooter method."""
148 self
.printRealFooter()
152 for u
in self
.undef_list
:
153 print "# undef %s" % (u
)
157 print '#endif /* !defined( %s ) */' % (self
.header_tag
)
160 def printRealHeader(self
):
161 """Print the "real" header for the created file.
163 In the base class, this function is empty. All derived
164 classes should over-ride this function."""
168 def printRealFooter(self
):
169 """Print the "real" footer for the created file.
171 In the base class, this function is empty. All derived
172 classes should over-ride this function."""
177 """Conditionally define `PURE' function attribute.
179 Conditionally defines a preprocessor macro `PURE' that wraps
180 GCC's `pure' function attribute. The conditional code can be
181 easilly adapted to other compilers that support a similar
184 The name is also added to the file's undef_list.
186 self
.undef_list
.append("PURE")
187 print """# if defined(__GNUC__) || (defined(__SUNPRO_C) && (__SUNPRO_C >= 0x590))
188 # define PURE __attribute__((pure))
195 def printFastcall(self
):
196 """Conditionally define `FASTCALL' function attribute.
198 Conditionally defines a preprocessor macro `FASTCALL' that
199 wraps GCC's `fastcall' function attribute. The conditional
200 code can be easilly adapted to other compilers that support a
203 The name is also added to the file's undef_list.
206 self
.undef_list
.append("FASTCALL")
207 print """# if defined(__i386__) && defined(__GNUC__) && !defined(__CYGWIN__) && !defined(__MINGW32__)
208 # define FASTCALL __attribute__((fastcall))
215 def printVisibility(self
, S
, s
):
216 """Conditionally define visibility function attribute.
218 Conditionally defines a preprocessor macro name S that wraps
219 GCC's visibility function attribute. The visibility used is
220 the parameter s. The conditional code can be easilly adapted
221 to other compilers that support a similar feature.
223 The name is also added to the file's undef_list.
226 self
.undef_list
.append(S
)
227 print """# if (defined(__GNUC__) && !defined(__CYGWIN__) && !defined(__MINGW32__)) || (defined(__SUNPRO_C) && (__SUNPRO_C >= 0x590) && defined(__ELF__))
228 # define %s __attribute__((visibility("%s")))
231 # endif""" % (S
, s
, S
)
235 def printNoinline(self
):
236 """Conditionally define `NOINLINE' function attribute.
238 Conditionally defines a preprocessor macro `NOINLINE' that
239 wraps GCC's `noinline' function attribute. The conditional
240 code can be easilly adapted to other compilers that support a
243 The name is also added to the file's undef_list.
246 self
.undef_list
.append("NOINLINE")
247 print """# if defined(__GNUC__) || (defined(__SUNPRO_C) && (__SUNPRO_C >= 0x590))
248 # define NOINLINE __attribute__((noinline))
255 def real_function_name(element
):
256 name
= element
.nsProp( "name", None )
257 alias
= element
.nsProp( "alias", None )
265 def real_category_name(c
):
266 if re
.compile("[1-9][0-9]*[.][0-9]+").match(c
):
267 return "GL_VERSION_" + c
.replace(".", "_")
272 def classify_category(name
, number
):
273 """Based on the category name and number, select a numerical class for it.
275 Categories are divided into four classes numbered 0 through 3. The
278 0. Core GL versions, sorted by version number.
279 1. ARB extensions, sorted by extension number.
280 2. Non-ARB extensions, sorted by extension number.
281 3. Un-numbered extensions, sorted by extension name.
285 core_version
= float(name
)
289 if core_version
> 0.0:
292 elif name
.startswith("GL_ARB_") or name
.startswith("GLX_ARB_") or name
.startswith("WGL_ARB_"):
304 return [cat_type
, key
]
307 def create_parameter_string(parameters
, include_names
):
308 """Create a parameter string from a list of gl_parameters."""
316 list.append( p
.string() )
318 list.append( p
.type_string() )
320 if len(list) == 0: list = ["void"]
322 return string
.join(list, ", ")
325 class gl_item(object):
326 def __init__(self
, element
, context
):
327 self
.context
= context
328 self
.name
= element
.nsProp( "name", None )
329 self
.category
= real_category_name( element
.parent
.nsProp( "name", None ) )
333 class gl_type( gl_item
):
334 def __init__(self
, element
, context
):
335 gl_item
.__init
__(self
, element
, context
)
336 self
.size
= int( element
.nsProp( "size", None ), 0 )
338 te
= typeexpr
.type_expression( None )
339 tn
= typeexpr
.type_node()
340 tn
.size
= int( element
.nsProp( "size", None ), 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
)
351 def get_type_expression(self
):
352 return self
.type_expr
355 class gl_enum( gl_item
):
356 def __init__(self
, element
, context
):
357 gl_item
.__init
__(self
, element
, context
)
358 self
.value
= int( element
.nsProp( "value", None ), 0 )
360 temp
= element
.nsProp( "count", None )
361 if not temp
or temp
== "?":
362 self
.default_count
= -1
367 raise RuntimeError('Invalid count value "%s" for enum "%s" in function "%s" when an integer was expected.' % (temp
, self
.name
, n
))
369 self
.default_count
= c
375 """Calculate a 'priority' for this enum name.
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'.
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.
386 if self
.name
.endswith( "_BIT" ):
391 if self
.category
.startswith( "GL_VERSION_" ):
393 elif self
.category
.startswith( "GL_ARB_" ):
395 elif self
.category
.startswith( "GL_EXT_" ):
400 return priority
+ bias
404 class gl_parameter(object):
405 def __init__(self
, element
, context
):
406 self
.name
= element
.nsProp( "name", None )
408 ts
= element
.nsProp( "type", None )
409 self
.type_expr
= typeexpr
.type_expression( ts
, context
)
411 temp
= element
.nsProp( "variable_param", None )
413 self
.count_parameter_list
= temp
.split( ' ' )
415 self
.count_parameter_list
= []
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
422 c
= element
.nsProp( "count", None )
432 self
.count_scale
= int(element
.nsProp( "count_scale", None ))
434 elements
= (count
* self
.count_scale
)
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())
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' )
450 # Pixel data has special parameters.
452 self
.width
= element
.nsProp('img_width', None)
453 self
.height
= element
.nsProp('img_height', None)
454 self
.depth
= element
.nsProp('img_depth', None)
455 self
.extent
= element
.nsProp('img_extent', None)
457 self
.img_xoff
= element
.nsProp('img_xoff', None)
458 self
.img_yoff
= element
.nsProp('img_yoff', None)
459 self
.img_zoff
= element
.nsProp('img_zoff', None)
460 self
.img_woff
= element
.nsProp('img_woff', None)
462 self
.img_format
= element
.nsProp('img_format', None)
463 self
.img_type
= element
.nsProp('img_type', None)
464 self
.img_target
= element
.nsProp('img_target', None)
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' )
470 self
.is_padding
= is_attr_true( element
, 'padding' )
474 def compatible(self
, other
):
479 return self
.is_pointer()
482 def is_pointer(self
):
483 return self
.type_expr
.is_pointer()
493 def is_variable_length(self
):
494 return len(self
.count_parameter_list
) or self
.counter
498 count
= self
.type_expr
.get_element_count()
500 if (self
.size() / count
) == 8:
510 return self
.type_expr
.original_string
+ " " + self
.name
513 def type_string(self
):
514 return self
.type_expr
.original_string
517 def get_base_type_string(self
):
518 return self
.type_expr
.get_base_name()
521 def get_dimensions(self
):
523 return [ 0, "0", "0", "0", "0" ]
543 return [ dim
, w
, h
, d
, e
]
546 def get_stack_size(self
):
547 return self
.type_expr
.get_stack_size()
554 return self
.type_expr
.get_element_size()
557 def get_element_count(self
):
558 c
= self
.type_expr
.get_element_count()
565 def size_string(self
, use_parens
= 1):
567 if self
.counter
or self
.count_parameter_list
:
568 list = [ "compsize" ]
570 if self
.counter
and self
.count_parameter_list
:
571 list.append( self
.counter
)
573 list = [ self
.counter
]
576 list.append( str(s
) )
578 if len(list) > 1 and use_parens
:
579 return "(%s)" % (string
.join(list, " * "))
581 return string
.join(list, " * ")
583 elif self
.is_image():
589 def format_string(self
):
590 if self
.type_expr
.original_string
== "GLenum":
593 return self
.type_expr
.format_string()
597 class gl_function( gl_item
):
598 def __init__(self
, element
, context
):
599 self
.context
= context
602 self
.entry_points
= []
603 self
.return_type
= "void"
609 self
.assign_offset
= 0
611 self
.static_entry_points
= []
613 # Track the parameter string (for the function prototype)
614 # for each entry-point. This is done because some functions
615 # change their prototype slightly when promoted from extension
616 # to ARB extension to core. glTexImage3DEXT and glTexImage3D
617 # are good examples of this. Scripts that need to generate
618 # code for these differing aliases need to real prototype
619 # for each entry-point. Otherwise, they may generate code
620 # that won't compile.
622 self
.entry_point_parameters
= {}
624 self
.process_element( element
)
629 def process_element(self
, element
):
630 name
= element
.nsProp( "name", None )
631 alias
= element
.nsProp( "alias", None )
633 if is_attr_true(element
, "static_dispatch"):
634 self
.static_entry_points
.append(name
)
636 self
.entry_points
.append( name
)
642 # Only try to set the offset when a non-alias
643 # entry-point is being processes.
645 offset
= element
.nsProp( "offset", None )
652 if offset
== "assign":
653 self
.assign_offset
= 1
657 self
.name
= true_name
658 elif self
.name
!= true_name
:
659 raise RuntimeError("Function true name redefined. Was %s, now %s." % (self
.name
, true_name
))
662 # There are two possible cases. The first time an entry-point
663 # with data is seen, self.initialized will be 0. On that
664 # pass, we just fill in the data. The next time an
665 # entry-point with data is seen, self.initialized will be 1.
666 # On that pass we have to make that the new values match the
667 # valuse from the previous entry-point.
671 child
= element
.children
673 if child
.type == "element":
674 if child
.name
== "return":
675 return_type
= child
.nsProp( "type", None )
676 elif child
.name
== "param":
677 param
= self
.context
.factory
.create_item( "parameter", child
, self
.context
)
678 parameters
.append( param
)
684 if self
.return_type
!= return_type
:
685 raise RuntimeError( "Return type changed in %s. Was %s, now %s." % (name
, self
.return_type
, return_type
))
687 if len(parameters
) != len(self
.parameters
):
688 raise RuntimeError( "Parameter count mismatch in %s. Was %d, now %d." % (name
, len(self
.parameters
), len(parameters
)))
690 for j
in range(0, len(parameters
)):
692 p2
= self
.parameters
[j
]
693 if not p1
.compatible( p2
):
694 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
))
697 if true_name
== name
or not self
.initialized
:
698 self
.return_type
= return_type
699 self
.parameters
= parameters
701 for param
in self
.parameters
:
703 self
.images
.append( param
)
707 self
.entry_point_parameters
[name
] = parameters
709 self
.entry_point_parameters
[name
] = []
713 def filter_entry_points(self
, entry_point_list
):
714 """Filter out entry points not in entry_point_list."""
715 if not self
.initialized
:
716 raise RuntimeError('%s is not initialized yet' % self
.name
)
719 for ent
in self
.entry_points
:
720 if ent
not in entry_point_list
:
721 if ent
in self
.static_entry_points
:
722 self
.static_entry_points
.remove(ent
)
723 self
.entry_point_parameters
.pop(ent
)
725 entry_points
.append(ent
)
728 raise RuntimeError('%s has no entry point after filtering' % self
.name
)
730 self
.entry_points
= entry_points
731 if self
.name
not in entry_points
:
732 # use the first remaining entry point
733 self
.name
= entry_points
[0]
734 self
.parameters
= self
.entry_point_parameters
[entry_points
[0]]
736 def get_images(self
):
737 """Return potentially empty list of input images."""
741 def parameterIterator(self
):
742 return self
.parameters
.__iter
__();
745 def get_parameter_string(self
, entrypoint
= None):
747 params
= self
.entry_point_parameters
[ entrypoint
]
749 params
= self
.parameters
751 return create_parameter_string( params
, 1 )
753 def get_called_parameter_string(self
):
757 for p
in self
.parameterIterator():
758 p_string
= p_string
+ comma
+ p
.name
765 return (self
.offset
>= 0 and not self
.assign_offset
)
767 def is_static_entry_point(self
, name
):
768 return name
in self
.static_entry_points
770 def dispatch_name(self
):
771 if self
.name
in self
.static_entry_points
:
774 return "_dispatch_stub_%u" % (self
.offset
)
776 def static_name(self
, name
):
777 if name
in self
.static_entry_points
:
780 return "_dispatch_stub_%u" % (self
.offset
)
783 class gl_item_factory(object):
784 """Factory to create objects derived from gl_item."""
786 def create_item(self
, item_name
, element
, context
):
787 if item_name
== "function":
788 return gl_function(element
, context
)
789 if item_name
== "type":
790 return gl_type(element
, context
)
791 elif item_name
== "enum":
792 return gl_enum(element
, context
)
793 elif item_name
== "parameter":
794 return gl_parameter(element
, context
)
795 elif item_name
== "api":
801 class gl_api(object):
802 def __init__(self
, factory
):
803 self
.functions_by_name
= {}
804 self
.enums_by_name
= {}
805 self
.types_by_name
= {}
807 self
.category_dict
= {}
808 self
.categories
= [{}, {}, {}, {}]
810 self
.factory
= factory
814 typeexpr
.create_initial_types()
817 def filter_functions(self
, entry_point_list
):
818 """Filter out entry points not in entry_point_list."""
819 functions_by_name
= {}
820 for func
in self
.functions_by_name
.itervalues():
821 entry_points
= [ent
for ent
in func
.entry_points
if ent
in entry_point_list
]
823 func
.filter_entry_points(entry_points
)
824 functions_by_name
[func
.name
] = func
826 self
.functions_by_name
= functions_by_name
828 def process_element(self
, doc
):
829 element
= doc
.children
830 while element
.type != "element" or element
.name
!= "OpenGLAPI":
831 element
= element
.next
834 self
.process_OpenGLAPI(element
)
838 def process_OpenGLAPI(self
, element
):
839 child
= element
.children
841 if child
.type == "element":
842 if child
.name
== "category":
843 self
.process_category( child
)
844 elif child
.name
== "OpenGLAPI":
845 self
.process_OpenGLAPI( child
)
852 def process_category(self
, cat
):
853 cat_name
= cat
.nsProp( "name", None )
854 cat_number
= cat
.nsProp( "number", None )
856 [cat_type
, key
] = classify_category(cat_name
, cat_number
)
857 self
.categories
[cat_type
][key
] = [cat_name
, cat_number
]
861 if child
.type == "element":
862 if child
.name
== "function":
863 func_name
= real_function_name( child
)
865 temp_name
= child
.nsProp( "name", None )
866 self
.category_dict
[ temp_name
] = [cat_name
, cat_number
]
868 if self
.functions_by_name
.has_key( func_name
):
869 func
= self
.functions_by_name
[ func_name
]
870 func
.process_element( child
)
872 func
= self
.factory
.create_item( "function", child
, self
)
873 self
.functions_by_name
[ func_name
] = func
875 if func
.offset
>= self
.next_offset
:
876 self
.next_offset
= func
.offset
+ 1
879 elif child
.name
== "enum":
880 enum
= self
.factory
.create_item( "enum", child
, self
)
881 self
.enums_by_name
[ enum
.name
] = enum
882 elif child
.name
== "type":
883 t
= self
.factory
.create_item( "type", child
, self
)
884 self
.types_by_name
[ "GL" + t
.name
] = t
892 def functionIterateByCategory(self
, cat
= None):
893 """Iterate over functions by category.
895 If cat is None, all known functions are iterated in category
896 order. See classify_category for details of the ordering.
897 Within a category, functions are sorted by name. If cat is
898 not None, then only functions in that category are iterated.
900 lists
= [{}, {}, {}, {}]
902 for func
in self
.functionIterateAll():
903 [cat_name
, cat_number
] = self
.category_dict
[func
.name
]
905 if (cat
== None) or (cat
== cat_name
):
906 [func_cat_type
, key
] = classify_category(cat_name
, cat_number
)
908 if not lists
[func_cat_type
].has_key(key
):
909 lists
[func_cat_type
][key
] = {}
911 lists
[func_cat_type
][key
][func
.name
] = func
915 for func_cat_type
in range(0,4):
916 keys
= lists
[func_cat_type
].keys()
920 names
= lists
[func_cat_type
][key
].keys()
924 functions
.append(lists
[func_cat_type
][key
][name
])
926 return functions
.__iter
__()
929 def functionIterateByOffset(self
):
931 for func
in self
.functions_by_name
.itervalues():
932 if func
.offset
> max_offset
:
933 max_offset
= func
.offset
936 temp
= [None for i
in range(0, max_offset
+ 1)]
937 for func
in self
.functions_by_name
.itervalues():
938 if func
.offset
!= -1:
939 temp
[ func
.offset
] = func
943 for i
in range(0, max_offset
+ 1):
947 return list.__iter
__();
950 def functionIterateAll(self
):
951 return self
.functions_by_name
.itervalues()
954 def enumIterateByName(self
):
955 keys
= self
.enums_by_name
.keys()
960 list.append( self
.enums_by_name
[ enum
] )
962 return list.__iter
__()
965 def categoryIterate(self
):
966 """Iterate over categories.
968 Iterate over all known categories in the order specified by
969 classify_category. Each iterated value is a tuple of the
970 name and number (which may be None) of the category.
974 for cat_type
in range(0,4):
975 keys
= self
.categories
[cat_type
].keys()
979 list.append(self
.categories
[cat_type
][key
])
981 return list.__iter
__()
984 def get_category_for_name( self
, name
):
985 if self
.category_dict
.has_key(name
):
986 return self
.category_dict
[name
]
988 return ["<unknown category>", None]
991 def typeIterate(self
):
992 return self
.types_by_name
.itervalues()
995 def find_type( self
, type_name
):
996 if type_name
in self
.types_by_name
:
997 return self
.types_by_name
[ type_name
].type_expr
999 print "Unable to find base type matching \"%s\"." % (type_name
)