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>
28 from xml
.sax
import saxutils
29 from xml
.sax
import make_parser
30 from xml
.sax
.handler
import feature_namespaces
35 def is_attr_true( attrs
, name
):
36 """Read a name value from an element's attributes.
38 The value read from the attribute list must be either 'true' or
39 'false'. If the value is 'false', zero will be returned. If the
40 value is 'true', non-zero will be returned. An exception will be
41 raised for any other value."""
43 value
= attrs
.get((None, name
), "false")
46 elif value
== "false":
49 raise RuntimeError('Invalid value "%s" for boolean "%s".' % (value
, name
))
52 def parse_GL_API( handler
, file_name
):
53 """Boiler-plate code to create an XML parser and use it.
55 Creates an XML parser and uses that parser with the application
56 supplied SAX callback, which should be derived from
60 parser
= make_parser()
61 parser
.setFeature(feature_namespaces
, 1)
62 parser
.setContentHandler( handler
)
66 if not file_name
or file_name
== "-":
67 parser
.parse( sys
.stdin
)
69 parser
.parse( file_name
)
76 """Generic class on which all other API entity types are based."""
78 def __init__(self
, tag_name
, name
, context
):
80 self
.category
= context
.get_category_define()
81 self
.context
= context
82 self
.tag_name
= tag_name
84 context
.append(tag_name
, self
)
87 def startElementNS(self
, name
, qname
, attrs
):
88 """Generic startElement handler.
90 The startElement handler is called for all elements except
91 the one that starts the object. For a foo element, the
92 XML "<foo><bar/></foo>" would cause the startElement handler
93 to be called once, but the endElement handler would be called
97 def endElementNS(self
, name
, qname
):
98 """Generic endElement handler.
100 Generic endElement handler. Returns 1 if the tag containing
101 the object is complete. Otherwise 0 is returned. All
102 derived class endElement handlers should call this method. If
103 the name of the ending tag is the same as the tag that
104 started this object, the object is assumed to be complete.
106 This fails if a tag can contain another tag with the same
107 name. The XML "<foo><foo/><bar/></foo>" would fail. The
108 object would end before the bar tag was processed.
110 The endElement handler is called for every end element
111 associated with an object, even the element that started the
112 object. See the description of startElement an example."""
114 if name
== (None, self
.tag_name
):
119 def get_category_define(self
):
123 class glEnum( glItem
):
124 """Subclass of glItem for representing GL enumerants.
126 This class is not complete, and is not really used yet."""
128 def __init__(self
, context
, name
, attrs
):
129 self
.value
= int(attrs
.get((None, 'value'), "0x0000"), 0)
131 enum_name
= "GL_" + attrs
.get((None, 'name'), None)
132 glItem
.__init
__(self
, name
, enum_name
, context
)
134 temp
= attrs
.get((None, 'count'), None)
135 self
.default_count
= 0
137 self
.default_count
= -1
142 raise RuntimeError('Invalid count value "%s" for enum "%s" in function "%s" when an integer was expected.' % (temp
, self
.name
, n
))
144 self
.default_count
= c
148 def process_attributes(self
, attrs
):
149 name
= attrs
.get((None, 'name'), None)
151 temp
= attrs
.get((None, 'count'), None)
153 c
= self
.default_count
158 raise RuntimeError('Invalid count value "%s" for enum "%s" in function "%s" when an integer was expected.' % (temp
, self
.name
, n
))
160 mode_str
= attrs
.get((None, 'mode'), "set")
161 if mode_str
== "set":
163 elif mode_str
== "get":
166 raise RuntimeError("Invalid mode '%s' for function '%s' in enum '%s'." % (mode_str
, self
.context
.name
, self
.name
))
168 return [name
, c
, mode
]
171 class glType( glItem
):
172 """Subclass of glItem for representing GL types."""
174 def __init__(self
, context
, name
, attrs
):
175 self
.size
= int(attrs
.get((None, 'size'), "0"))
176 self
.glx_name
= attrs
.get((None, 'glx_name'), "")
178 type_name
= "GL" + attrs
.get((None, 'name'), None)
179 glItem
.__init
__(self
, name
, type_name
, context
)
182 class glParameter( glItem
):
183 """Parameter of a glFunction."""
192 def __init__(self
, context
, name
, attrs
):
193 p_name
= attrs
.get((None, 'name'), None)
194 self
.p_type_string
= attrs
.get((None, 'type'), None)
196 temp
= attrs
.get((None, 'variable_param'), None)
198 self
.count_parameter_list
= temp
.split( ' ' )
200 self
.count_parameter_list
= []
202 self
.p_type
= context
.context
.find_type(self
.p_type_string
)
203 if self
.p_type
== None:
204 raise RuntimeError("Unknown type '%s' in function '%s'." % (self
.p_type_string
, context
.name
))
207 # The count tag can be either a numeric string or the name of
208 # a variable. If it is the name of a variable, the int(c)
209 # statement will throw an exception, and the except block will
212 c
= attrs
.get((None, 'count'), "0")
214 self
.p_count
= int(c
)
220 self
.count_scale
= int(attrs
.get((None, 'count_scale'), "1"))
222 self
.is_counter
= is_attr_true( attrs
, 'counter' )
223 self
.is_output
= is_attr_true( attrs
, 'output' )
226 # Pixel data has special parameters.
228 self
.width
= attrs
.get((None, 'img_width'), None)
229 self
.height
= attrs
.get((None, 'img_height'), None)
230 self
.depth
= attrs
.get((None, 'img_depth'), None)
231 self
.extent
= attrs
.get((None, 'img_extent'), None)
233 self
.img_xoff
= attrs
.get((None, 'img_xoff'), None)
234 self
.img_yoff
= attrs
.get((None, 'img_yoff'), None)
235 self
.img_zoff
= attrs
.get((None, 'img_zoff'), None)
236 self
.img_woff
= attrs
.get((None, 'img_woff'), None)
238 self
.img_format
= attrs
.get((None, 'img_format'), None)
239 self
.img_type
= attrs
.get((None, 'img_type'), None)
240 self
.img_target
= attrs
.get((None, 'img_target'), None)
242 self
.img_pad_dimensions
= is_attr_true( attrs
, 'img_pad_dimensions' )
243 self
.img_null_flag
= is_attr_true( attrs
, 'img_null_flag' )
244 self
.img_send_null
= is_attr_true( attrs
, 'img_send_null' )
246 if self
.p_count
> 0 or self
.counter
or self
.count_parameter_list
:
252 # If there is a * anywhere in the parameter's type, then it
255 if re
.compile("[*]").search(self
.p_type_string
):
256 # We could do some other validation here. For
257 # example, an output parameter should not be const,
258 # but every non-output parameter should.
262 # If a parameter is not a pointer, then there cannot
263 # be an associated count (either fixed size or
264 # variable) and the parameter cannot be an output.
266 if has_count
or self
.is_output
:
267 raise RuntimeError("Non-pointer type has count or is output.")
270 glItem
.__init
__(self
, name
, p_name
, context
)
274 def is_variable_length_array(self
):
275 """Determine if a parameter is a variable length array.
277 A parameter is considered to be a variable length array if
278 its size depends on the value of another parameter that is
279 an enumerant. The params parameter to glTexEnviv is an
280 example of a variable length array parameter. Arrays whose
281 size depends on a count variable, such as the lists parameter
282 to glCallLists, are not variable length arrays in this
285 return self
.count_parameter_list
or self
.counter
or self
.width
289 return self
.is_pointer
292 def count_string(self
):
293 """Return a string representing the number of items
295 Returns a string representing the number of items in a
296 parameter. For scalar types this will always be "1". For
297 vector types, it will depend on whether or not it is a
298 fixed length vector (like the parameter of glVertex3fv),
299 a counted length (like the vector parameter of
300 glDeleteTextures), or a general variable length vector."""
303 if self
.count_parameter_list
:
305 elif self
.counter
!= None:
308 return str(self
.p_count
)
314 if self
.count_parameter_list
or self
.counter
or self
.width
or self
.is_output
:
316 elif self
.p_count
== 0:
317 return self
.p_type
.size
319 return self
.p_type
.size
* self
.p_count
* self
.count_scale
321 def size_string(self
):
325 b_prod
= self
.p_type
.size
327 # Handle functions like glCompressedTexImage2D that
328 # have a counted 'void *' parameter.
330 if b_prod
== 0: b_prod
= 1
332 if not self
.count_parameter_list
and self
.counter
!= None:
333 if self
.count_scale
> 1:
334 a_prod
= '(%s * %u)' % (self
.counter
, self
.count_scale
)
336 a_prod
= self
.counter
337 elif self
.count_parameter_list
and self
.counter
== None:
339 elif self
.count_parameter_list
and self
.counter
!= None:
340 if self
.count_scale
> 1:
341 b_prod
= '(%s * %u)' % (self
.counter
, self
.count_scale
)
343 b_prod
= self
.counter
347 raise RuntimeError("Parameter '%s' to function '%s' has size 0." % (self
.name
, self
.context
.name
))
349 return "(%s * %s)" % (a_prod
, b_prod
)
354 class glParameterIterator
:
355 """Class to iterate over a list of glParameters.
357 Objects of this class are returned by the parameterIterator method of
358 the glFunction class. They are used to iterate over the list of
359 parameters to the function."""
361 def __init__(self
, data
):
369 if self
.index
== len( self
.data
):
376 class glFunction( glItem
):
377 def __init__(self
, context
, name
, attrs
):
378 self
.fn_alias
= attrs
.get((None, 'alias'), None)
379 self
.fn_parameters
= []
381 self
.count_parameter_list
= []
382 self
.fn_return_type
= "void"
384 temp
= attrs
.get((None, 'offset'), None)
385 if temp
== None or temp
== "?":
388 self
.fn_offset
= int(temp
)
390 fn_name
= attrs
.get((None, 'name'), None)
391 if self
.fn_alias
!= None:
392 self
.real_name
= self
.fn_alias
394 self
.real_name
= fn_name
396 self
.parameters_by_name
= {}
397 self
.variable_length_parameters
= []
399 glItem
.__init
__(self
, name
, fn_name
, context
)
403 def parameterIterator(self
):
404 return glParameterIterator(self
.fn_parameters
)
407 def startElementNS(self
, name
, qname
, attrs
):
408 [uri
, true_name
] = name
409 if true_name
== "param":
411 self
.context
.factory
.create(self
, true_name
, attrs
)
413 print "Error with parameter '%s' in function '%s'." \
414 % (attrs
.get((None, 'name'),'(unknown)'), self
.name
)
416 elif true_name
== "return":
417 self
.set_return_type(attrs
.get((None, 'type'), None))
420 def endElementNS(self
, name
, qname
):
421 """Handle the end of a <function> element.
423 At the end of a <function> element, there is some semantic
424 checking that can be done. This prevents some possible
425 exceptions from being thrown elsewhere in the code.
428 [uri
, true_name
] = name
429 if true_name
== "function":
430 for p
in self
.variable_length_parameters
:
432 counter
= self
.parameters_by_name
[ p
.counter
]
433 if not self
.parameters_by_name
.has_key( p
.counter
):
434 raise RuntimeError("Parameter '%s' of function '%s' has counter '%s', but function has no such parameter." % (p
.name
, self
.name
, p
.counter
))
435 elif not self
.parameters_by_name
[ p
.counter
].is_counter
:
436 raise RuntimeError("Parameter '%s' of function '%s' has counter '%s', but '%s' is not marked as a counter." % (p
.name
, self
.name
, p
.counter
, p
.counter
))
438 for n
in p
.count_parameter_list
:
439 if not self
.parameters_by_name
.has_key( n
):
440 raise RuntimeError("Parameter '%s' of function '%s' has size parameter '%s', but function has no such parameter." % (p
.name
, self
.name
, n
))
447 def append(self
, tag_name
, p
):
448 if tag_name
!= "param":
449 raise RuntimeError("Trying to append '%s' to parameter list of function '%s'." % (tag_name
, self
.name
))
454 self
.fn_parameters
.append(p
)
455 if p
.count_parameter_list
!= []:
456 self
.count_parameter_list
.extend( p
.count_parameter_list
)
458 if p
.is_variable_length_array():
459 self
.variable_length_parameters
.append(p
)
461 self
.parameters_by_name
[ p
.name
] = p
464 def set_return_type(self
, t
):
465 self
.fn_return_type
= t
468 def get_parameter_string(self
):
471 for p
in glFunction
.parameterIterator(self
):
472 arg_string
= arg_string
+ comma
+ p
.p_type_string
+ " " + p
.name
482 """Factory to create objects derived from glItem."""
484 def create(self
, context
, name
, attrs
):
485 if name
== "function":
486 return glFunction(context
, name
, attrs
)
488 return glType(context
, name
, attrs
)
490 return glEnum(context
, name
, attrs
)
491 elif name
== "param":
492 return glParameter(context
, name
, attrs
)
497 class glFunctionIterator
:
498 """Class to iterate over a list of glFunctions
500 Objects of this classare returned by
501 FilterGLAPISpecBase::functionIterator. This default version
502 iterates over the functions in order of dispatch table offset. All
503 of the "true" functions are iterated first, followed by the alias
506 def __init__(self
, context
):
507 self
.context
= context
508 self
.keys
= context
.functions
.keys()
514 for self
.index
in range(0, len(self
.keys
)):
515 if self
.keys
[ self
.index
] >= 0: break
517 if self
.index
== len(self
.keys
):
521 self
.split
= self
.index
- 1
533 k
= self
.keys
[ self
.index
]
535 #if self.context.functions[k].fn_alias == None:
536 # if k != self.prevk + 1:
537 # print 'Missing offset %d' % (prevk)
538 # self.prevk = int(k)
540 self
.index
+= self
.direction
542 if self
.index
== len(self
.keys
):
543 self
.index
= self
.split
546 return self
.context
.functions
[k
]
549 class FilterGLAPISpecBase(saxutils
.XMLFilterBase
):
551 license
= "The license for this file is unspecified."
553 current_object
= None
556 saxutils
.XMLFilterBase
.__init
__(self
)
559 self
.functions_by_name
= {}
560 self
.factory
= glItemFactory()
561 self
.header_tag
= None
563 self
.current_category
= ""
566 def find_type(self
,type_name
):
568 if re
.compile(t
).search(type_name
):
570 print "Unable to find base type matching \"%s\"." % (type_name
)
574 def find_function(self
,function_name
):
575 return self
.functions_by_name
[function_name
]
578 def functionIterator(self
):
579 return glFunctionIterator(self
)
582 def printFunctions(self
):
583 for f
in self
.functionIterator():
584 self
.printFunction(f
)
588 def printHeader(self
):
589 """Print the header associated with all files and call the printRealHeader method."""
591 print '/* DO NOT EDIT - This file generated automatically by %s script */' \
595 print ' * ' + self
.license
.replace('\n', '\n * ')
599 print '#if !defined( %s )' % (self
.header_tag
)
600 print '# define %s' % (self
.header_tag
)
602 self
.printRealHeader();
606 def printFooter(self
):
607 """Print the header associated with all files and call the printRealFooter method."""
609 self
.printFunctions()
610 self
.printRealFooter()
614 for u
in self
.undef_list
:
615 print "# undef %s" % (u
)
617 print '#endif /* !defined( %s ) */' % (self
.header_tag
)
620 def get_category_define(self
):
621 """Convert the category name to the #define that would be found in glext.h"""
623 if re
.compile("[1-9][0-9]*[.][0-9]+").match(self
.current_category
):
624 s
= self
.current_category
625 return "GL_VERSION_" + s
.replace(".", "_")
627 return self
.current_category
630 def append(self
, object_type
, obj
):
631 if object_type
== "function":
632 # If the function is not an alias and has a negative
633 # offset, then we do not need to track it. These are
634 # functions that don't have an assigned offset
636 if not self
.functions_by_name
.has_key(obj
.name
):
637 self
.functions_by_name
[obj
.name
] = obj
639 if obj
.fn_offset
>= 0 or obj
.fn_alias
!= None:
640 if obj
.fn_offset
>= 0:
641 index
= obj
.fn_offset
643 index
= self
.next_alias
646 self
.functions
[index
] = obj
648 # We should do some checking here to make
649 # sure the functions are an identical match.
652 elif object_type
== "type":
653 self
.types
[obj
.name
] = obj
658 def startElementNS(self
, name
, qname
, attrs
):
659 """Start a new element in the XML stream.
661 Starts a new element. There are three types of elements that
662 are specially handled by this function. When a "category"
663 element is encountered, the name of the category is saved.
664 If an element is encountered and no API object is
665 in-progress, a new object is created using the API factory.
666 Any future elements, until that API object is closed, are
667 passed to the current objects startElement method.
669 This paradigm was chosen becuase it allows subclasses of the
670 basic API types (i.e., glFunction, glEnum, etc.) to handle
671 additional XML data, GLX protocol information, that the base
672 classes do not know about."""
674 [uri
, true_name
] = name
676 if self
.current_object
!= None:
677 self
.current_object
.startElementNS(name
, qname
, attrs
)
678 elif true_name
== "category":
679 self
.current_category
= attrs
.get((None, 'name'), "")
680 elif true_name
== "include":
681 self
.next_include
= attrs
.get((None, 'name'), "")
683 self
.current_object
= self
.factory
.create(self
, true_name
, attrs
)
687 def endElementNS(self
, name
, qname
):
688 if self
.current_object
!= None:
689 if self
.current_object
.endElementNS(name
, qname
):
690 self
.current_object
= None
691 elif name
== "include":
692 parser
= make_parser()
693 parser
.setFeature(feature_namespaces
, 1)
694 parser
.setContentHandler(self
)
696 f
= open(self
.next_include
)
703 self
.undef_list
.append("PURE")
704 print """# if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 96)
705 # define PURE __attribute__((pure))
710 def printFastcall(self
):
711 self
.undef_list
.append("FASTCALL")
712 print """# if defined(__i386__) && defined(__GNUC__)
713 # define FASTCALL __attribute__((fastcall))
718 def printVisibility(self
, S
, s
):
719 self
.undef_list
.append(S
)
720 print """# if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 3)
721 # define %s __attribute__((visibility("%s")))
724 # endif""" % (S
, s
, S
)
726 def printNoinline(self
):
727 self
.undef_list
.append("NOINLINE")
728 print """# if defined(__GNUC__)
729 # define NOINLINE __attribute__((noinline))
734 def printHaveAlias(self
):
735 self
.undef_list
.append("HAVE_ALIAS")
736 print """# if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95)
740 def printFunction(self
,offset
):
741 """Print a single function.
743 In the base class, this function is empty. All derived
744 classes should over-ride this function."""
748 def printRealHeader(self
):
749 """Print the "real" header for the created file.
751 In the base class, this function is empty. All derived
752 classes should over-ride this function."""
756 def printRealFooter(self
):
757 """Print the "real" footer for the created file.
759 In the base class, this function is empty. All derived
760 classes should over-ride this function."""