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."""
313 list.append( p
.string() )
315 list.append( p
.type_string() )
317 if len(list) == 0: list = ["void"]
319 return string
.join(list, ", ")
323 def __init__(self
, element
, context
):
324 self
.context
= context
325 self
.name
= element
.nsProp( "name", None )
326 self
.category
= real_category_name( element
.parent
.nsProp( "name", None ) )
330 class gl_type( gl_item
):
331 def __init__(self
, element
, context
):
332 gl_item
.__init
__(self
, element
, context
)
333 self
.size
= int( element
.nsProp( "size", None ), 0 )
335 te
= typeexpr
.type_expression( None )
336 tn
= typeexpr
.type_node()
337 tn
.size
= int( element
.nsProp( "size", None ), 0 )
338 tn
.integer
= not is_attr_true( element
, "float" )
339 tn
.unsigned
= is_attr_true( element
, "unsigned" )
340 tn
.name
= "GL" + self
.name
341 te
.set_base_type_node( tn
)
347 def get_type_expression(self
):
348 return self
.type_expr
351 class gl_enum( gl_item
):
352 def __init__(self
, element
, context
):
353 gl_item
.__init
__(self
, element
, context
)
354 self
.value
= int( element
.nsProp( "value", None ), 0 )
356 temp
= element
.nsProp( "count", None )
357 if not temp
or temp
== "?":
358 self
.default_count
= -1
363 raise RuntimeError('Invalid count value "%s" for enum "%s" in function "%s" when an integer was expected.' % (temp
, self
.name
, n
))
365 self
.default_count
= c
371 """Calculate a 'priority' for this enum name.
373 When an enum is looked up by number, there may be many
374 possible names, but only one is the 'prefered' name. The
375 priority is used to select which name is the 'best'.
377 Highest precedence is given to core GL name. ARB extension
378 names have the next highest, followed by EXT extension names.
379 Vendor extension names are the lowest.
382 if self
.name
.endswith( "_BIT" ):
387 if self
.category
.startswith( "GL_VERSION_" ):
389 elif self
.category
.startswith( "GL_ARB_" ):
391 elif self
.category
.startswith( "GL_EXT_" ):
396 return priority
+ bias
401 def __init__(self
, element
, context
):
402 self
.name
= element
.nsProp( "name", None )
404 ts
= element
.nsProp( "type", None )
405 self
.type_expr
= typeexpr
.type_expression( ts
, context
)
407 temp
= element
.nsProp( "variable_param", None )
409 self
.count_parameter_list
= temp
.split( ' ' )
411 self
.count_parameter_list
= []
413 # The count tag can be either a numeric string or the name of
414 # a variable. If it is the name of a variable, the int(c)
415 # statement will throw an exception, and the except block will
418 c
= element
.nsProp( "count", None )
428 self
.count_scale
= int(element
.nsProp( "count_scale", None ))
430 elements
= (count
* self
.count_scale
)
434 #if ts == "GLdouble":
435 # print '/* stack size -> %s = %u (before)*/' % (self.name, self.type_expr.get_stack_size())
436 # print '/* # elements = %u */' % (elements)
437 self
.type_expr
.set_elements( elements
)
438 #if ts == "GLdouble":
439 # print '/* stack size -> %s = %u (after) */' % (self.name, self.type_expr.get_stack_size())
441 self
.is_client_only
= is_attr_true( element
, 'client_only' )
442 self
.is_counter
= is_attr_true( element
, 'counter' )
443 self
.is_output
= is_attr_true( element
, 'output' )
446 # Pixel data has special parameters.
448 self
.width
= element
.nsProp('img_width', None)
449 self
.height
= element
.nsProp('img_height', None)
450 self
.depth
= element
.nsProp('img_depth', None)
451 self
.extent
= element
.nsProp('img_extent', None)
453 self
.img_xoff
= element
.nsProp('img_xoff', None)
454 self
.img_yoff
= element
.nsProp('img_yoff', None)
455 self
.img_zoff
= element
.nsProp('img_zoff', None)
456 self
.img_woff
= element
.nsProp('img_woff', None)
458 self
.img_format
= element
.nsProp('img_format', None)
459 self
.img_type
= element
.nsProp('img_type', None)
460 self
.img_target
= element
.nsProp('img_target', None)
462 self
.img_pad_dimensions
= is_attr_true( element
, 'img_pad_dimensions' )
463 self
.img_null_flag
= is_attr_true( element
, 'img_null_flag' )
464 self
.img_send_null
= is_attr_true( element
, 'img_send_null' )
469 def compatible(self
, other
):
474 return self
.is_pointer()
477 def is_pointer(self
):
478 return self
.type_expr
.is_pointer()
488 def is_variable_length(self
):
489 return len(self
.count_parameter_list
) or self
.counter
493 count
= self
.type_expr
.get_element_count()
495 if (self
.size() / count
) == 8:
505 return self
.type_expr
.original_string
+ " " + self
.name
508 def type_string(self
):
509 return self
.type_expr
.original_string
512 def get_base_type_string(self
):
513 return self
.type_expr
.get_base_name()
516 def get_dimensions(self
):
518 return [ 0, "0", "0", "0", "0" ]
538 return [ dim
, w
, h
, d
, e
]
541 def get_stack_size(self
):
542 return self
.type_expr
.get_stack_size()
549 return self
.type_expr
.get_element_size()
552 def get_element_count(self
):
553 c
= self
.type_expr
.get_element_count()
560 def size_string(self
, use_parens
= 1):
562 if self
.counter
or self
.count_parameter_list
:
563 list = [ "compsize" ]
565 if self
.counter
and self
.count_parameter_list
:
566 list.append( self
.counter
)
568 list = [ self
.counter
]
571 list.append( str(s
) )
573 if len(list) > 1 and use_parens
:
574 return "(%s)" % (string
.join(list, " * "))
576 return string
.join(list, " * ")
578 elif self
.is_image():
584 def format_string(self
):
585 if self
.type_expr
.original_string
== "GLenum":
588 return self
.type_expr
.format_string()
592 class gl_function( gl_item
):
593 def __init__(self
, element
, context
):
594 self
.context
= context
597 self
.entry_points
= []
598 self
.return_type
= "void"
604 self
.assign_offset
= 0
606 self
.static_entry_points
= []
608 # Track the parameter string (for the function prototype)
609 # for each entry-point. This is done because some functions
610 # change their prototype slightly when promoted from extension
611 # to ARB extension to core. glTexImage3DEXT and glTexImage3D
612 # are good examples of this. Scripts that need to generate
613 # code for these differing aliases need to real prototype
614 # for each entry-point. Otherwise, they may generate code
615 # that won't compile.
617 self
.parameter_strings
= {}
619 self
.process_element( element
)
624 def process_element(self
, element
):
625 name
= element
.nsProp( "name", None )
626 alias
= element
.nsProp( "alias", None )
628 if is_attr_true(element
, "static_dispatch"):
629 self
.static_entry_points
.append(name
)
631 self
.entry_points
.append( name
)
637 # Only try to set the offset when a non-alias
638 # entry-point is being processes.
640 offset
= element
.nsProp( "offset", None )
647 if offset
== "assign":
648 self
.assign_offset
= 1
652 self
.name
= true_name
653 elif self
.name
!= true_name
:
654 raise RuntimeError("Function true name redefined. Was %s, now %s." % (self
.name
, true_name
))
657 # There are two possible cases. The first time an entry-point
658 # with data is seen, self.initialized will be 0. On that
659 # pass, we just fill in the data. The next time an
660 # entry-point with data is seen, self.initialized will be 1.
661 # On that pass we have to make that the new values match the
662 # valuse from the previous entry-point.
666 child
= element
.children
668 if child
.type == "element":
669 if child
.name
== "return":
670 return_type
= child
.nsProp( "type", None )
671 elif child
.name
== "param":
672 param
= self
.context
.factory
.create_item( "parameter", child
, self
.context
)
673 parameters
.append( param
)
679 if self
.return_type
!= return_type
:
680 raise RuntimeError( "Return type changed in %s. Was %s, now %s." % (name
, self
.return_type
, return_type
))
682 if len(parameters
) != len(self
.parameters
):
683 raise RuntimeError( "Parameter count mismatch in %s. Was %d, now %d." % (name
, len(self
.parameters
), len(parameters
)))
685 for j
in range(0, len(parameters
)):
687 p2
= self
.parameters
[j
]
688 if not p1
.compatible( p2
):
689 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
))
692 if true_name
== name
or not self
.initialized
:
693 self
.return_type
= return_type
694 self
.parameters
= parameters
696 for param
in self
.parameters
:
698 self
.images
.append( param
)
702 self
.parameter_strings
[name
] = create_parameter_string(parameters
, 1)
704 self
.parameter_strings
[name
] = None
709 def get_images(self
):
710 """Return potentially empty list of input images."""
714 def parameterIterator(self
):
715 return self
.parameters
.__iter
__();
718 def get_parameter_string(self
, entrypoint
= None):
720 s
= self
.parameter_strings
[ entrypoint
]
724 return create_parameter_string( self
.parameters
, 1 )
726 def get_called_parameter_string(self
):
730 for p
in self
.parameterIterator():
731 p_string
= p_string
+ comma
+ p
.name
737 def is_static_entry_point(self
, name
):
738 return name
in self
.static_entry_points
740 def dispatch_name(self
):
741 if self
.name
in self
.static_entry_points
:
744 return "_dispatch_stub_%u" % (self
.offset
)
746 def static_name(self
, name
):
747 if name
in self
.static_entry_points
:
750 return "_dispatch_stub_%u" % (self
.offset
)
753 class gl_item_factory
:
754 """Factory to create objects derived from gl_item."""
756 def create_item(self
, item_name
, element
, context
):
757 if item_name
== "function":
758 return gl_function(element
, context
)
759 if item_name
== "type":
760 return gl_type(element
, context
)
761 elif item_name
== "enum":
762 return gl_enum(element
, context
)
763 elif item_name
== "parameter":
764 return gl_parameter(element
, context
)
765 elif item_name
== "api":
772 def __init__(self
, factory
):
773 self
.functions_by_name
= {}
774 self
.enums_by_name
= {}
775 self
.types_by_name
= {}
777 self
.category_dict
= {}
778 self
.categories
= [{}, {}, {}, {}]
780 self
.factory
= factory
784 typeexpr
.create_initial_types()
788 def process_element(self
, doc
):
789 element
= doc
.children
790 while element
.type != "element" or element
.name
!= "OpenGLAPI":
791 element
= element
.next
794 self
.process_OpenGLAPI(element
)
798 def process_OpenGLAPI(self
, element
):
799 child
= element
.children
801 if child
.type == "element":
802 if child
.name
== "category":
803 self
.process_category( child
)
804 elif child
.name
== "OpenGLAPI":
805 self
.process_OpenGLAPI( child
)
812 def process_category(self
, cat
):
813 cat_name
= cat
.nsProp( "name", None )
814 cat_number
= cat
.nsProp( "number", None )
816 [cat_type
, key
] = classify_category(cat_name
, cat_number
)
817 self
.categories
[cat_type
][key
] = [cat_name
, cat_number
]
821 if child
.type == "element":
822 if child
.name
== "function":
823 func_name
= real_function_name( child
)
825 temp_name
= child
.nsProp( "name", None )
826 self
.category_dict
[ temp_name
] = [cat_name
, cat_number
]
828 if self
.functions_by_name
.has_key( func_name
):
829 func
= self
.functions_by_name
[ func_name
]
830 func
.process_element( child
)
832 func
= self
.factory
.create_item( "function", child
, self
)
833 self
.functions_by_name
[ func_name
] = func
835 if func
.offset
>= self
.next_offset
:
836 self
.next_offset
= func
.offset
+ 1
839 elif child
.name
== "enum":
840 enum
= self
.factory
.create_item( "enum", child
, self
)
841 self
.enums_by_name
[ enum
.name
] = enum
842 elif child
.name
== "type":
843 t
= self
.factory
.create_item( "type", child
, self
)
844 self
.types_by_name
[ "GL" + t
.name
] = t
852 def functionIterateByCategory(self
, cat
= None):
853 """Iterate over functions by category.
855 If cat is None, all known functions are iterated in category
856 order. See classify_category for details of the ordering.
857 Within a category, functions are sorted by name. If cat is
858 not None, then only functions in that category are iterated.
860 lists
= [{}, {}, {}, {}]
862 for func
in self
.functionIterateAll():
863 [cat_name
, cat_number
] = self
.category_dict
[func
.name
]
865 if (cat
== None) or (cat
== cat_name
):
866 [func_cat_type
, key
] = classify_category(cat_name
, cat_number
)
868 if not lists
[func_cat_type
].has_key(key
):
869 lists
[func_cat_type
][key
] = {}
871 lists
[func_cat_type
][key
][func
.name
] = func
875 for func_cat_type
in range(0,4):
876 keys
= lists
[func_cat_type
].keys()
880 names
= lists
[func_cat_type
][key
].keys()
884 functions
.append(lists
[func_cat_type
][key
][name
])
886 return functions
.__iter
__()
889 def functionIterateByOffset(self
):
891 for func
in self
.functions_by_name
.itervalues():
892 if func
.offset
> max_offset
:
893 max_offset
= func
.offset
896 temp
= [None for i
in range(0, max_offset
+ 1)]
897 for func
in self
.functions_by_name
.itervalues():
898 if func
.offset
!= -1:
899 temp
[ func
.offset
] = func
903 for i
in range(0, max_offset
+ 1):
907 return list.__iter
__();
910 def functionIterateAll(self
):
911 return self
.functions_by_name
.itervalues()
914 def enumIterateByName(self
):
915 keys
= self
.enums_by_name
.keys()
920 list.append( self
.enums_by_name
[ enum
] )
922 return list.__iter
__()
925 def categoryIterate(self
):
926 """Iterate over categories.
928 Iterate over all known categories in the order specified by
929 classify_category. Each iterated value is a tuple of the
930 name and number (which may be None) of the category.
934 for cat_type
in range(0,4):
935 keys
= self
.categories
[cat_type
].keys()
939 list.append(self
.categories
[cat_type
][key
])
941 return list.__iter
__()
944 def get_category_for_name( self
, name
):
945 if self
.category_dict
.has_key(name
):
946 return self
.category_dict
[name
]
948 return ["<unknown category>", None]
951 def typeIterate(self
):
952 return self
.types_by_name
.itervalues()
955 def find_type( self
, type_name
):
956 if type_name
in self
.types_by_name
:
957 return self
.types_by_name
[ type_name
].type_expr
959 print "Unable to find base type matching \"%s\"." % (type_name
)