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 """Generic class on which all other API entity types are based."""
37 def __init__(self
, tag_name
, name
, context
):
39 self
.category
= context
.get_category_define()
40 self
.context
= context
41 self
.tag_name
= tag_name
43 context
.append(tag_name
, self
)
46 def startElement(self
, name
, attrs
):
47 """Generic startElement handler.
49 The startElement handler is called for all elements except
50 the one that starts the object. For a foo element, the
51 XML "<foo><bar/></foo>" would cause the startElement handler
52 to be called once, but the endElement handler would be called
56 def endElement(self
, name
):
57 """Generic endElement handler.
59 Generic endElement handler. Returns 1 if the tag containing
60 the object is complete. Otherwise 0 is returned. All
61 derived class endElement handlers should call this method. If
62 the name of the ending tag is the same as the tag that
63 started this object, the object is assumed to be complete.
65 This fails if a tag can contain another tag with the same
66 name. The XML "<foo><foo/><bar/></foo>" would fail. The
67 object would end before the bar tag was processed.
69 The endElement handler is called for every end element
70 associated with an object, even the element that started the
71 object. See the description of startElement an example."""
73 if name
== self
.tag_name
:
78 def get_category_define(self
):
82 class glEnum( glItem
):
83 """Subclass of glItem for representing GL enumerants.
85 This class is not complete, and is not really used yet."""
87 def __init__(self
, context
, name
, attrs
):
88 self
.value
= int(attrs
.get('value', "0x0000"), 0)
90 enum_name
= "GL_" + attrs
.get('name', None)
91 glItem
.__init
__(self
, name
, enum_name
, context
)
93 temp
= attrs
.get('count', None)
95 self
.default_count
= 0
100 raise RuntimeError('Invalid count value "%s" for enum "%s" in function "%s" when an integer was expected.' % (temp
, self
.name
, n
))
102 self
.default_count
= c
106 def process_attributes(self
, attrs
):
107 name
= attrs
.get('name', None)
109 temp
= attrs
.get('count', None)
111 c
= self
.default_count
116 raise RuntimeError('Invalid count value "%s" for enum "%s" in function "%s" when an integer was expected.' % (temp
, self
.name
, n
))
118 mode_str
= attrs
.get('mode', "set")
119 if mode_str
== "set":
121 elif mode_str
== "get":
124 raise RuntimeError("Invalid mode '%s' for function '%s' in enum '%s'." % (mode_str
, self
.context
.name
, self
.name
))
126 return [name
, c
, mode
]
129 class glType( glItem
):
130 """Subclass of glItem for representing GL types."""
132 def __init__(self
, context
, name
, attrs
):
133 self
.size
= int(attrs
.get('size', "0"))
134 self
.glx_name
= attrs
.get('glx_name', "")
136 type_name
= "GL" + attrs
.get('name', None)
137 glItem
.__init
__(self
, name
, type_name
, context
)
140 class glParameter( glItem
):
141 """Parameter of a glFunction."""
150 def __init__(self
, context
, name
, attrs
):
151 p_name
= attrs
.get('name', None)
152 self
.p_type_string
= attrs
.get('type', None)
154 temp
= attrs
.get('variable_param', None)
156 self
.count_parameter_list
= temp
.replace( ' ', '' ).split( ',' )
158 self
.count_parameter_list
= []
160 self
.p_type
= context
.context
.find_type(self
.p_type_string
)
161 if self
.p_type
== None:
162 raise RuntimeError("Unknown type '%s' in function '%s'." % (self
.p_type_string
, context
.name
))
165 # The count tag can be either a numeric string or the name of
166 # a variable. If it is the name of a variable, the int(c)
167 # statement will throw an exception, and the except block will
170 c
= attrs
.get('count', "0")
172 self
.p_count
= int(c
)
178 if attrs
.get('counter', "false") == "true":
183 if attrs
.get('output', "false") == "true":
189 # Pixel data has special parameters.
191 self
.width
= attrs
.get('img_width', None)
192 self
.height
= attrs
.get('img_height', None)
193 self
.depth
= attrs
.get('img_depth', None)
194 self
.extent
= attrs
.get('img_extent', None)
196 self
.img_xoff
= attrs
.get('img_xoff', None)
197 self
.img_yoff
= attrs
.get('img_yoff', None)
198 self
.img_zoff
= attrs
.get('img_zoff', None)
199 self
.img_woff
= attrs
.get('img_woff', None)
201 self
.img_format
= attrs
.get('img_format', None)
202 self
.img_type
= attrs
.get('img_type', None)
203 self
.img_target
= attrs
.get('img_target', None)
205 pad
= attrs
.get('img_pad_dimensions', "false")
207 self
.img_pad_dimensions
= 1
209 self
.img_pad_dimensions
= 0
212 null_flag
= attrs
.get('img_null_flag', "false")
213 if null_flag
== "true":
214 self
.img_null_flag
= 1
216 self
.img_null_flag
= 0
218 send_null
= attrs
.get('img_send_null', "false")
219 if send_null
== "true":
220 self
.img_send_null
= 1
222 self
.img_send_null
= 0
226 if self
.p_count
> 0 or self
.counter
or self
.count_parameter_list
:
232 # If there is a * anywhere in the parameter's type, then it
235 if re
.compile("[*]").search(self
.p_type_string
):
236 # We could do some other validation here. For
237 # example, an output parameter should not be const,
238 # but every non-output parameter should.
242 # If a parameter is not a pointer, then there cannot
243 # be an associated count (either fixed size or
244 # variable) and the parameter cannot be an output.
246 if has_count
or self
.is_output
:
247 raise RuntimeError("Non-pointer type has count or is output.")
250 glItem
.__init
__(self
, name
, p_name
, context
)
254 def is_variable_length_array(self
):
255 """Determine if a parameter is a variable length array.
257 A parameter is considered to be a variable length array if
258 its size depends on the value of another parameter that is
259 an enumerant. The params parameter to glTexEnviv is an
260 example of a variable length array parameter. Arrays whose
261 size depends on a count variable, such as the lists parameter
262 to glCallLists, are not variable length arrays in this
265 return self
.count_parameter_list
or self
.counter
or self
.width
269 return self
.is_pointer
272 def count_string(self
):
273 """Return a string representing the number of items
275 Returns a string representing the number of items in a
276 parameter. For scalar types this will always be "1". For
277 vector types, it will depend on whether or not it is a
278 fixed length vector (like the parameter of glVertex3fv),
279 a counted length (like the vector parameter of
280 glDeleteTextures), or a general variable length vector."""
283 if self
.count_parameter_list
:
285 elif self
.counter
!= None:
288 return str(self
.p_count
)
294 if self
.count_parameter_list
or self
.counter
or self
.width
or self
.is_output
:
296 elif self
.p_count
== 0:
297 return self
.p_type
.size
299 return self
.p_type
.size
* self
.p_count
301 def size_string(self
):
305 b_prod
= self
.p_type
.size
307 # Handle functions like glCompressedTexImage2D that
308 # have a counted 'void *' parameter.
310 if b_prod
== 0: b_prod
= 1
312 if not self
.count_parameter_list
and self
.counter
!= None:
313 a_prod
= self
.counter
314 elif self
.count_parameter_list
and self
.counter
== None:
316 elif self
.count_parameter_list
and self
.counter
!= None:
317 b_prod
= self
.counter
321 raise RuntimeError("Parameter '%s' to function '%s' has size 0." % (self
.name
, self
.context
.name
))
323 return "(%s * %s)" % (a_prod
, b_prod
)
328 class glParameterIterator
:
329 """Class to iterate over a list of glParameters.
331 Objects of this class are returned by the parameterIterator method of
332 the glFunction class. They are used to iterate over the list of
333 parameters to the function."""
335 def __init__(self
, data
):
343 if self
.index
== len( self
.data
):
350 class glFunction( glItem
):
351 def __init__(self
, context
, name
, attrs
):
352 self
.fn_alias
= attrs
.get('alias', None)
353 self
.fn_parameters
= []
355 self
.count_parameter_list
= []
356 self
.fn_return_type
= "void"
358 temp
= attrs
.get('offset', None)
359 if temp
== None or temp
== "?":
362 self
.fn_offset
= int(temp
)
364 fn_name
= attrs
.get('name', None)
365 if self
.fn_alias
!= None:
366 self
.real_name
= self
.fn_alias
368 self
.real_name
= fn_name
370 glItem
.__init
__(self
, name
, fn_name
, context
)
374 def parameterIterator(self
):
375 return glParameterIterator(self
.fn_parameters
)
378 def startElement(self
, name
, attrs
):
381 self
.context
.factory
.create(self
, name
, attrs
)
383 print "Error with parameter '%s' in function '%s'." \
384 % (attrs
.get('name','(unknown)'), self
.name
)
386 elif name
== "return":
387 self
.set_return_type(attrs
.get('type', None))
390 def append(self
, tag_name
, p
):
391 if tag_name
!= "param":
392 raise RuntimeError("Trying to append '%s' to parameter list of function '%s'." % (tag_name
, self
.name
))
397 self
.fn_parameters
.append(p
)
398 if p
.count_parameter_list
!= []:
399 self
.count_parameter_list
.extend( p
.count_parameter_list
)
402 def set_return_type(self
, t
):
403 self
.fn_return_type
= t
406 def get_parameter_string(self
):
409 for p
in glFunction
.parameterIterator(self
):
410 arg_string
= arg_string
+ comma
+ p
.p_type_string
+ " " + p
.name
420 """Factory to create objects derived from glItem."""
422 def create(self
, context
, name
, attrs
):
423 if name
== "function":
424 return glFunction(context
, name
, attrs
)
426 return glType(context
, name
, attrs
)
428 return glEnum(context
, name
, attrs
)
429 elif name
== "param":
430 return glParameter(context
, name
, attrs
)
435 class glFunctionIterator
:
436 """Class to iterate over a list of glFunctions
438 Objects of this classare returned by
439 FilterGLAPISpecBase::functionIterator. This default version
440 iterates over the functions in order of dispatch table offset. All
441 of the "true" functions are iterated first, followed by the alias
444 def __init__(self
, context
):
445 self
.context
= context
446 self
.keys
= context
.functions
.keys()
452 for self
.index
in range(0, len(self
.keys
)):
453 if self
.keys
[ self
.index
] >= 0: break
455 if self
.index
== len(self
.keys
):
459 self
.split
= self
.index
- 1
471 k
= self
.keys
[ self
.index
]
473 #if self.context.functions[k].fn_alias == None:
474 # if k != self.prevk + 1:
475 # print 'Missing offset %d' % (prevk)
476 # self.prevk = int(k)
478 self
.index
+= self
.direction
480 if self
.index
== len(self
.keys
):
481 self
.index
= self
.split
484 return self
.context
.functions
[k
]
487 class FilterGLAPISpecBase(saxutils
.XMLFilterBase
):
489 license
= "The license for this file is unspecified."
494 current_object
= None
496 current_category
= ""
499 saxutils
.XMLFilterBase
.__init
__(self
)
503 self
.factory
= glItemFactory()
504 self
.header_tag
= None
508 def find_type(self
,type_name
):
510 if re
.compile(t
).search(type_name
):
512 print "Unable to find base type matching \"%s\"." % (type_name
)
516 def find_function(self
,function_name
):
517 index
= self
.xref
[function_name
]
518 return self
.functions
[index
]
521 def functionIterator(self
):
522 return glFunctionIterator(self
)
525 def printFunctions(self
):
526 for f
in self
.functionIterator():
527 self
.printFunction(f
)
531 def printHeader(self
):
532 """Print the header associated with all files and call the printRealHeader method."""
534 print '/* DO NOT EDIT - This file generated automatically by %s script */' \
538 print ' * ' + self
.license
.replace('\n', '\n * ')
542 print '#if !defined( %s )' % (self
.header_tag
)
543 print '# define %s' % (self
.header_tag
)
545 self
.printRealHeader();
549 def printFooter(self
):
550 """Print the header associated with all files and call the printRealFooter method."""
552 self
.printFunctions()
553 self
.printRealFooter()
557 for u
in self
.undef_list
:
558 print "# undef %s" % (u
)
560 print '#endif /* !defined( %s ) */' % (self
.header_tag
)
563 def get_category_define(self
):
564 """Convert the category name to the #define that would be found in glext.h"""
566 if re
.compile("[1-9][0-9]*[.][0-9]+").match(self
.current_category
):
567 s
= self
.current_category
568 return "GL_VERSION_" + s
.replace(".", "_")
570 return self
.current_category
573 def append(self
, object_type
, obj
):
574 if object_type
== "function":
575 # If the function is not an alias and has a negative
576 # offset, then we do not need to track it. These are
577 # functions that don't have an assigned offset
579 if obj
.fn_offset
>= 0 or obj
.fn_alias
!= None:
580 if obj
.fn_offset
>= 0:
581 index
= obj
.fn_offset
583 index
= self
.next_alias
586 self
.functions
[index
] = obj
587 self
.xref
[obj
.name
] = index
588 elif object_type
== "type":
589 self
.types
[obj
.name
] = obj
594 def startElement(self
, name
, attrs
):
595 """Start a new element in the XML stream.
597 Starts a new element. There are three types of elements that
598 are specially handled by this function. When a "category"
599 element is encountered, the name of the category is saved.
600 If an element is encountered and no API object is
601 in-progress, a new object is created using the API factory.
602 Any future elements, until that API object is closed, are
603 passed to the current objects startElement method.
605 This paradigm was chosen becuase it allows subclasses of the
606 basic API types (i.e., glFunction, glEnum, etc.) to handle
607 additional XML data, GLX protocol information, that the base
608 classes do not know about."""
610 if self
.current_object
!= None:
611 self
.current_object
.startElement(name
, attrs
)
612 elif name
== "category":
613 self
.current_category
= attrs
.get('name', "")
614 elif name
== "include":
615 self
.next_include
= attrs
.get('name', "")
617 self
.current_object
= self
.factory
.create(self
, name
, attrs
)
621 def endElement(self
, name
):
622 if self
.current_object
!= None:
623 if self
.current_object
.endElement(name
):
624 self
.current_object
= None
625 elif name
== "include":
626 parser
= make_parser()
627 parser
.setFeature(feature_namespaces
, 0)
628 parser
.setContentHandler(self
)
630 f
= open(self
.next_include
)
637 self
.undef_list
.append("PURE")
638 print """# if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 96)
639 # define PURE __attribute__((pure))
644 def printFastcall(self
):
645 self
.undef_list
.append("FASTCALL")
646 print """# if defined(__i386__) && defined(__GNUC__)
647 # define FASTCALL __attribute__((fastcall))
652 def printVisibility(self
, S
, s
):
653 self
.undef_list
.append(S
)
654 print """# if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 3)
655 # define %s __attribute__((visibility("%s")))
658 # endif""" % (S
, s
, S
)
660 def printNoinline(self
):
661 self
.undef_list
.append("NOINLINE")
662 print """# if defined(__GNUC__)
663 # define NOINLINE __attribute__((noinline))
668 def printHaveAlias(self
):
669 self
.undef_list
.append("HAVE_ALIAS")
670 print """# if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95)
674 def printFunction(self
,offset
):
675 """Print a single function.
677 In the base class, this function is empty. All derived
678 classes should over-ride this function."""
682 def printRealHeader(self
):
683 """Print the "real" header for the created file.
685 In the base class, this function is empty. All derived
686 classes should over-ride this function."""
690 def printRealFooter(self
):
691 """Print the "real" footer for the created file.
693 In the base class, this function is empty. All derived
694 classes should over-ride this function."""