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
))
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 __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 96)
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 (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 3)) && 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__)
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, ", ")
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
.name
= "GL" + self
.name
344 te
.set_base_type_node( tn
)
350 def get_type_expression(self
):
351 return self
.type_expr
354 class gl_enum( gl_item
):
355 def __init__(self
, element
, context
):
356 gl_item
.__init
__(self
, element
, context
)
357 self
.value
= int( element
.nsProp( "value", None ), 0 )
359 temp
= element
.nsProp( "count", None )
360 if not temp
or temp
== "?":
361 self
.default_count
= -1
366 raise RuntimeError('Invalid count value "%s" for enum "%s" in function "%s" when an integer was expected.' % (temp
, self
.name
, n
))
368 self
.default_count
= c
374 """Calculate a 'priority' for this enum name.
376 When an enum is looked up by number, there may be many
377 possible names, but only one is the 'prefered' name. The
378 priority is used to select which name is the 'best'.
380 Highest precedence is given to core GL name. ARB extension
381 names have the next highest, followed by EXT extension names.
382 Vendor extension names are the lowest.
385 if self
.name
.endswith( "_BIT" ):
390 if self
.category
.startswith( "GL_VERSION_" ):
392 elif self
.category
.startswith( "GL_ARB_" ):
394 elif self
.category
.startswith( "GL_EXT_" ):
399 return priority
+ bias
404 def __init__(self
, element
, context
):
405 self
.name
= element
.nsProp( "name", None )
407 ts
= element
.nsProp( "type", None )
408 self
.type_expr
= typeexpr
.type_expression( ts
, context
)
410 temp
= element
.nsProp( "variable_param", None )
412 self
.count_parameter_list
= temp
.split( ' ' )
414 self
.count_parameter_list
= []
416 # The count tag can be either a numeric string or the name of
417 # a variable. If it is the name of a variable, the int(c)
418 # statement will throw an exception, and the except block will
421 c
= element
.nsProp( "count", None )
431 self
.count_scale
= int(element
.nsProp( "count_scale", None ))
433 elements
= (count
* self
.count_scale
)
437 #if ts == "GLdouble":
438 # print '/* stack size -> %s = %u (before)*/' % (self.name, self.type_expr.get_stack_size())
439 # print '/* # elements = %u */' % (elements)
440 self
.type_expr
.set_elements( elements
)
441 #if ts == "GLdouble":
442 # print '/* stack size -> %s = %u (after) */' % (self.name, self.type_expr.get_stack_size())
444 self
.is_client_only
= is_attr_true( element
, 'client_only' )
445 self
.is_counter
= is_attr_true( element
, 'counter' )
446 self
.is_output
= is_attr_true( element
, 'output' )
449 # Pixel data has special parameters.
451 self
.width
= element
.nsProp('img_width', None)
452 self
.height
= element
.nsProp('img_height', None)
453 self
.depth
= element
.nsProp('img_depth', None)
454 self
.extent
= element
.nsProp('img_extent', None)
456 self
.img_xoff
= element
.nsProp('img_xoff', None)
457 self
.img_yoff
= element
.nsProp('img_yoff', None)
458 self
.img_zoff
= element
.nsProp('img_zoff', None)
459 self
.img_woff
= element
.nsProp('img_woff', None)
461 self
.img_format
= element
.nsProp('img_format', None)
462 self
.img_type
= element
.nsProp('img_type', None)
463 self
.img_target
= element
.nsProp('img_target', None)
465 self
.img_pad_dimensions
= is_attr_true( element
, 'img_pad_dimensions' )
466 self
.img_null_flag
= is_attr_true( element
, 'img_null_flag' )
467 self
.img_send_null
= is_attr_true( element
, 'img_send_null' )
469 self
.is_padding
= is_attr_true( element
, 'padding' )
473 def compatible(self
, other
):
478 return self
.is_pointer()
481 def is_pointer(self
):
482 return self
.type_expr
.is_pointer()
492 def is_variable_length(self
):
493 return len(self
.count_parameter_list
) or self
.counter
497 count
= self
.type_expr
.get_element_count()
499 if (self
.size() / count
) == 8:
509 return self
.type_expr
.original_string
+ " " + self
.name
512 def type_string(self
):
513 return self
.type_expr
.original_string
516 def get_base_type_string(self
):
517 return self
.type_expr
.get_base_name()
520 def get_dimensions(self
):
522 return [ 0, "0", "0", "0", "0" ]
542 return [ dim
, w
, h
, d
, e
]
545 def get_stack_size(self
):
546 return self
.type_expr
.get_stack_size()
553 return self
.type_expr
.get_element_size()
556 def get_element_count(self
):
557 c
= self
.type_expr
.get_element_count()
564 def size_string(self
, use_parens
= 1):
566 if self
.counter
or self
.count_parameter_list
:
567 list = [ "compsize" ]
569 if self
.counter
and self
.count_parameter_list
:
570 list.append( self
.counter
)
572 list = [ self
.counter
]
575 list.append( str(s
) )
577 if len(list) > 1 and use_parens
:
578 return "(%s)" % (string
.join(list, " * "))
580 return string
.join(list, " * ")
582 elif self
.is_image():
588 def format_string(self
):
589 if self
.type_expr
.original_string
== "GLenum":
592 return self
.type_expr
.format_string()
596 class gl_function( gl_item
):
597 def __init__(self
, element
, context
):
598 self
.context
= context
601 self
.entry_points
= []
602 self
.return_type
= "void"
608 self
.assign_offset
= 0
610 self
.static_entry_points
= []
612 # Track the parameter string (for the function prototype)
613 # for each entry-point. This is done because some functions
614 # change their prototype slightly when promoted from extension
615 # to ARB extension to core. glTexImage3DEXT and glTexImage3D
616 # are good examples of this. Scripts that need to generate
617 # code for these differing aliases need to real prototype
618 # for each entry-point. Otherwise, they may generate code
619 # that won't compile.
621 self
.parameter_strings
= {}
623 self
.process_element( element
)
628 def process_element(self
, element
):
629 name
= element
.nsProp( "name", None )
630 alias
= element
.nsProp( "alias", None )
632 if is_attr_true(element
, "static_dispatch"):
633 self
.static_entry_points
.append(name
)
635 self
.entry_points
.append( name
)
641 # Only try to set the offset when a non-alias
642 # entry-point is being processes.
644 offset
= element
.nsProp( "offset", None )
651 if offset
== "assign":
652 self
.assign_offset
= 1
656 self
.name
= true_name
657 elif self
.name
!= true_name
:
658 raise RuntimeError("Function true name redefined. Was %s, now %s." % (self
.name
, true_name
))
661 # There are two possible cases. The first time an entry-point
662 # with data is seen, self.initialized will be 0. On that
663 # pass, we just fill in the data. The next time an
664 # entry-point with data is seen, self.initialized will be 1.
665 # On that pass we have to make that the new values match the
666 # valuse from the previous entry-point.
670 child
= element
.children
672 if child
.type == "element":
673 if child
.name
== "return":
674 return_type
= child
.nsProp( "type", None )
675 elif child
.name
== "param":
676 param
= self
.context
.factory
.create_item( "parameter", child
, self
.context
)
677 parameters
.append( param
)
683 if self
.return_type
!= return_type
:
684 raise RuntimeError( "Return type changed in %s. Was %s, now %s." % (name
, self
.return_type
, return_type
))
686 if len(parameters
) != len(self
.parameters
):
687 raise RuntimeError( "Parameter count mismatch in %s. Was %d, now %d." % (name
, len(self
.parameters
), len(parameters
)))
689 for j
in range(0, len(parameters
)):
691 p2
= self
.parameters
[j
]
692 if not p1
.compatible( p2
):
693 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
))
696 if true_name
== name
or not self
.initialized
:
697 self
.return_type
= return_type
698 self
.parameters
= parameters
700 for param
in self
.parameters
:
702 self
.images
.append( param
)
706 self
.parameter_strings
[name
] = create_parameter_string(parameters
, 1)
708 self
.parameter_strings
[name
] = None
713 def get_images(self
):
714 """Return potentially empty list of input images."""
718 def parameterIterator(self
):
719 return self
.parameters
.__iter
__();
722 def get_parameter_string(self
, entrypoint
= None):
724 s
= self
.parameter_strings
[ entrypoint
]
728 return create_parameter_string( self
.parameters
, 1 )
730 def get_called_parameter_string(self
):
734 for p
in self
.parameterIterator():
735 p_string
= p_string
+ comma
+ p
.name
741 def is_static_entry_point(self
, name
):
742 return name
in self
.static_entry_points
744 def dispatch_name(self
):
745 if self
.name
in self
.static_entry_points
:
748 return "_dispatch_stub_%u" % (self
.offset
)
750 def static_name(self
, name
):
751 if name
in self
.static_entry_points
:
754 return "_dispatch_stub_%u" % (self
.offset
)
757 class gl_item_factory
:
758 """Factory to create objects derived from gl_item."""
760 def create_item(self
, item_name
, element
, context
):
761 if item_name
== "function":
762 return gl_function(element
, context
)
763 if item_name
== "type":
764 return gl_type(element
, context
)
765 elif item_name
== "enum":
766 return gl_enum(element
, context
)
767 elif item_name
== "parameter":
768 return gl_parameter(element
, context
)
769 elif item_name
== "api":
776 def __init__(self
, factory
):
777 self
.functions_by_name
= {}
778 self
.enums_by_name
= {}
779 self
.types_by_name
= {}
781 self
.category_dict
= {}
782 self
.categories
= [{}, {}, {}, {}]
784 self
.factory
= factory
788 typeexpr
.create_initial_types()
792 def process_element(self
, doc
):
793 element
= doc
.children
794 while element
.type != "element" or element
.name
!= "OpenGLAPI":
795 element
= element
.next
798 self
.process_OpenGLAPI(element
)
802 def process_OpenGLAPI(self
, element
):
803 child
= element
.children
805 if child
.type == "element":
806 if child
.name
== "category":
807 self
.process_category( child
)
808 elif child
.name
== "OpenGLAPI":
809 self
.process_OpenGLAPI( child
)
816 def process_category(self
, cat
):
817 cat_name
= cat
.nsProp( "name", None )
818 cat_number
= cat
.nsProp( "number", None )
820 [cat_type
, key
] = classify_category(cat_name
, cat_number
)
821 self
.categories
[cat_type
][key
] = [cat_name
, cat_number
]
825 if child
.type == "element":
826 if child
.name
== "function":
827 func_name
= real_function_name( child
)
829 temp_name
= child
.nsProp( "name", None )
830 self
.category_dict
[ temp_name
] = [cat_name
, cat_number
]
832 if self
.functions_by_name
.has_key( func_name
):
833 func
= self
.functions_by_name
[ func_name
]
834 func
.process_element( child
)
836 func
= self
.factory
.create_item( "function", child
, self
)
837 self
.functions_by_name
[ func_name
] = func
839 if func
.offset
>= self
.next_offset
:
840 self
.next_offset
= func
.offset
+ 1
843 elif child
.name
== "enum":
844 enum
= self
.factory
.create_item( "enum", child
, self
)
845 self
.enums_by_name
[ enum
.name
] = enum
846 elif child
.name
== "type":
847 t
= self
.factory
.create_item( "type", child
, self
)
848 self
.types_by_name
[ "GL" + t
.name
] = t
856 def functionIterateByCategory(self
, cat
= None):
857 """Iterate over functions by category.
859 If cat is None, all known functions are iterated in category
860 order. See classify_category for details of the ordering.
861 Within a category, functions are sorted by name. If cat is
862 not None, then only functions in that category are iterated.
864 lists
= [{}, {}, {}, {}]
866 for func
in self
.functionIterateAll():
867 [cat_name
, cat_number
] = self
.category_dict
[func
.name
]
869 if (cat
== None) or (cat
== cat_name
):
870 [func_cat_type
, key
] = classify_category(cat_name
, cat_number
)
872 if not lists
[func_cat_type
].has_key(key
):
873 lists
[func_cat_type
][key
] = {}
875 lists
[func_cat_type
][key
][func
.name
] = func
879 for func_cat_type
in range(0,4):
880 keys
= lists
[func_cat_type
].keys()
884 names
= lists
[func_cat_type
][key
].keys()
888 functions
.append(lists
[func_cat_type
][key
][name
])
890 return functions
.__iter
__()
893 def functionIterateByOffset(self
):
895 for func
in self
.functions_by_name
.itervalues():
896 if func
.offset
> max_offset
:
897 max_offset
= func
.offset
900 temp
= [None for i
in range(0, max_offset
+ 1)]
901 for func
in self
.functions_by_name
.itervalues():
902 if func
.offset
!= -1:
903 temp
[ func
.offset
] = func
907 for i
in range(0, max_offset
+ 1):
911 return list.__iter
__();
914 def functionIterateAll(self
):
915 return self
.functions_by_name
.itervalues()
918 def enumIterateByName(self
):
919 keys
= self
.enums_by_name
.keys()
924 list.append( self
.enums_by_name
[ enum
] )
926 return list.__iter
__()
929 def categoryIterate(self
):
930 """Iterate over categories.
932 Iterate over all known categories in the order specified by
933 classify_category. Each iterated value is a tuple of the
934 name and number (which may be None) of the category.
938 for cat_type
in range(0,4):
939 keys
= self
.categories
[cat_type
].keys()
943 list.append(self
.categories
[cat_type
][key
])
945 return list.__iter
__()
948 def get_category_for_name( self
, name
):
949 if self
.category_dict
.has_key(name
):
950 return self
.category_dict
[name
]
952 return ["<unknown category>", None]
955 def typeIterate(self
):
956 return self
.types_by_name
.itervalues()
959 def find_type( self
, type_name
):
960 if type_name
in self
.types_by_name
:
961 return self
.types_by_name
[ type_name
].type_expr
963 print "Unable to find base type matching \"%s\"." % (type_name
)