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."""
145 p_count_parameters
= None
151 def __init__(self
, context
, name
, attrs
):
152 p_name
= attrs
.get('name', None)
153 self
.p_type_string
= attrs
.get('type', None)
154 self
.p_count_parameters
= attrs
.get('variable_param', None)
156 if self
.p_count_parameters
:
157 temp
= self
.p_count_parameters
.replace( ' ', '' )
158 self
.count_parameter_list
= temp
.split( ',' )
160 self
.count_parameter_list
= []
162 self
.p_type
= context
.context
.find_type(self
.p_type_string
)
163 if self
.p_type
== None:
164 raise RuntimeError("Unknown type '%s' in function '%s'." % (self
.p_type_string
, context
.name
))
167 # The count tag can be either a numeric string or the name of
168 # a variable. If it is the name of a variable, the int(c)
169 # statement will throw an exception, and the except block will
172 c
= attrs
.get('count', "0")
174 self
.p_count
= int(c
)
180 if attrs
.get('counter', "false") == "true":
185 if attrs
.get('output', "false") == "true":
191 # Pixel data has special parameters.
193 self
.width
= attrs
.get('img_width', None)
194 self
.height
= attrs
.get('img_height', None)
195 self
.depth
= attrs
.get('img_depth', None)
196 self
.extent
= attrs
.get('img_extent', None)
198 self
.img_xoff
= attrs
.get('img_xoff', None)
199 self
.img_yoff
= attrs
.get('img_yoff', None)
200 self
.img_zoff
= attrs
.get('img_zoff', None)
201 self
.img_woff
= attrs
.get('img_woff', None)
203 self
.img_format
= attrs
.get('img_format', None)
204 self
.img_type
= attrs
.get('img_type', None)
205 self
.img_target
= attrs
.get('img_target', None)
207 pad
= attrs
.get('img_pad_dimensions', "false")
209 self
.img_pad_dimensions
= 1
211 self
.img_pad_dimensions
= 0
214 null_flag
= attrs
.get('img_null_flag', "false")
215 if null_flag
== "true":
216 self
.img_null_flag
= 1
218 self
.img_null_flag
= 0
220 send_null
= attrs
.get('img_send_null', "false")
221 if send_null
== "true":
222 self
.img_send_null
= 1
224 self
.img_send_null
= 0
228 if self
.p_count
> 0 or self
.counter
or self
.p_count_parameters
:
234 # If there is a * anywhere in the parameter's type, then it
237 if re
.compile("[*]").search(self
.p_type_string
):
238 # We could do some other validation here. For
239 # example, an output parameter should not be const,
240 # but every non-output parameter should.
244 # If a parameter is not a pointer, then there cannot
245 # be an associated count (either fixed size or
246 # variable) and the parameter cannot be an output.
248 if has_count
or self
.is_output
:
249 raise RuntimeError("Non-pointer type has count or is output.")
252 glItem
.__init
__(self
, name
, p_name
, context
)
256 def is_variable_length_array(self
):
257 """Determine if a parameter is a variable length array.
259 A parameter is considered to be a variable length array if
260 its size depends on the value of another parameter that is
261 an enumerant. The params parameter to glTexEnviv is an
262 example of a variable length array parameter. Arrays whose
263 size depends on a count variable, such as the lists parameter
264 to glCallLists, are not variable length arrays in this
267 return self
.p_count_parameters
or self
.counter
or self
.width
271 return self
.is_pointer
274 def count_string(self
):
275 """Return a string representing the number of items
277 Returns a string representing the number of items in a
278 parameter. For scalar types this will always be "1". For
279 vector types, it will depend on whether or not it is a
280 fixed length vector (like the parameter of glVertex3fv),
281 a counted length (like the vector parameter of
282 glDeleteTextures), or a general variable length vector."""
285 if self
.p_count_parameters
!= None:
287 elif self
.counter
!= None:
290 return str(self
.p_count
)
296 if self
.p_count_parameters
or self
.counter
or self
.width
or self
.is_output
:
298 elif self
.p_count
== 0:
299 return self
.p_type
.size
301 return self
.p_type
.size
* self
.p_count
303 def size_string(self
):
307 b_prod
= self
.p_type
.size
309 # Handle functions like glCompressedTexImage2D that
310 # have a counted 'void *' parameter.
312 if b_prod
== 0: b_prod
= 1
314 if self
.p_count_parameters
== None and self
.counter
!= None:
315 a_prod
= self
.counter
316 elif self
.p_count_parameters
!= None and self
.counter
== None:
318 elif self
.p_count_parameters
!= None and self
.counter
!= None:
319 b_prod
= self
.counter
323 raise RuntimeError("Parameter '%s' to function '%s' has size 0." % (self
.name
, self
.context
.name
))
325 return "(%s * %s)" % (a_prod
, b_prod
)
330 class glParameterIterator
:
331 """Class to iterate over a list of glParameters.
333 Objects of this class are returned by the parameterIterator method of
334 the glFunction class. They are used to iterate over the list of
335 parameters to the function."""
337 def __init__(self
, data
):
345 if self
.index
== len( self
.data
):
352 class glFunction( glItem
):
356 fn_return_type
= "void"
359 def __init__(self
, context
, name
, attrs
):
360 self
.fn_alias
= attrs
.get('alias', None)
361 self
.fn_parameters
= []
363 self
.count_parameter_list
= []
365 temp
= attrs
.get('offset', None)
366 if temp
== None or temp
== "?":
369 self
.fn_offset
= int(temp
)
371 fn_name
= attrs
.get('name', None)
372 if self
.fn_alias
!= None:
373 self
.real_name
= self
.fn_alias
375 self
.real_name
= fn_name
377 glItem
.__init
__(self
, name
, fn_name
, context
)
381 def parameterIterator(self
):
382 return glParameterIterator(self
.fn_parameters
)
385 def startElement(self
, name
, attrs
):
388 self
.context
.factory
.create(self
, name
, attrs
)
390 print "Error with parameter '%s' in function '%s'." \
391 % (attrs
.get('name','(unknown)'), self
.name
)
393 elif name
== "return":
394 self
.set_return_type(attrs
.get('type', None))
397 def append(self
, tag_name
, p
):
398 if tag_name
!= "param":
399 raise RuntimeError("Trying to append '%s' to parameter list of function '%s'." % (tag_name
, self
.name
))
404 self
.fn_parameters
.append(p
)
405 if p
.count_parameter_list
!= []:
406 self
.count_parameter_list
.extend( p
.count_parameter_list
)
409 def set_return_type(self
, t
):
410 self
.fn_return_type
= t
413 def get_parameter_string(self
):
416 for p
in glFunction
.parameterIterator(self
):
417 arg_string
= arg_string
+ comma
+ p
.p_type_string
+ " " + p
.name
427 """Factory to create objects derived from glItem."""
429 def create(self
, context
, name
, attrs
):
430 if name
== "function":
431 return glFunction(context
, name
, attrs
)
433 return glType(context
, name
, attrs
)
435 return glEnum(context
, name
, attrs
)
436 elif name
== "param":
437 return glParameter(context
, name
, attrs
)
442 class glFunctionIterator
:
443 """Class to iterate over a list of glFunctions
445 Objects of this classare returned by
446 FilterGLAPISpecBase::functionIterator. This default version
447 iterates over the functions in order of dispatch table offset. All
448 of the "true" functions are iterated first, followed by the alias
451 def __init__(self
, context
):
452 self
.context
= context
453 self
.keys
= context
.functions
.keys()
459 for self
.index
in range(0, len(self
.keys
)):
460 if self
.keys
[ self
.index
] >= 0: break
462 if self
.index
== len(self
.keys
):
466 self
.split
= self
.index
- 1
478 k
= self
.keys
[ self
.index
]
480 #if self.context.functions[k].fn_alias == None:
481 # if k != self.prevk + 1:
482 # print 'Missing offset %d' % (prevk)
483 # self.prevk = int(k)
485 self
.index
+= self
.direction
487 if self
.index
== len(self
.keys
):
488 self
.index
= self
.split
491 return self
.context
.functions
[k
]
494 class FilterGLAPISpecBase(saxutils
.XMLFilterBase
):
496 license
= "The license for this file is unspecified."
501 current_object
= None
503 current_category
= ""
506 saxutils
.XMLFilterBase
.__init
__(self
)
510 self
.factory
= glItemFactory()
511 self
.header_tag
= None
515 def find_type(self
,type_name
):
517 if re
.compile(t
).search(type_name
):
519 print "Unable to find base type matching \"%s\"." % (type_name
)
523 def find_function(self
,function_name
):
524 index
= self
.xref
[function_name
]
525 return self
.functions
[index
]
528 def functionIterator(self
):
529 return glFunctionIterator(self
)
532 def printFunctions(self
):
533 for f
in self
.functionIterator():
534 self
.printFunction(f
)
538 def printHeader(self
):
539 """Print the header associated with all files and call the printRealHeader method."""
541 print '/* DO NOT EDIT - This file generated automatically by %s script */' \
545 print ' * ' + self
.license
.replace('\n', '\n * ')
549 print '#if !defined( %s )' % (self
.header_tag
)
550 print '# define %s' % (self
.header_tag
)
552 self
.printRealHeader();
556 def printFooter(self
):
557 """Print the header associated with all files and call the printRealFooter method."""
559 self
.printFunctions()
560 self
.printRealFooter()
564 for u
in self
.undef_list
:
565 print "# undef %s" % (u
)
567 print '#endif /* !defined( %s ) */' % (self
.header_tag
)
570 def get_category_define(self
):
571 """Convert the category name to the #define that would be found in glext.h"""
573 if re
.compile("[1-9][0-9]*[.][0-9]+").match(self
.current_category
):
574 s
= self
.current_category
575 return "GL_VERSION_" + s
.replace(".", "_")
577 return self
.current_category
580 def append(self
, object_type
, obj
):
581 if object_type
== "function":
582 # If the function is not an alias and has a negative
583 # offset, then we do not need to track it. These are
584 # functions that don't have an assigned offset
586 if obj
.fn_offset
>= 0 or obj
.fn_alias
!= None:
587 if obj
.fn_offset
>= 0:
588 index
= obj
.fn_offset
590 index
= self
.next_alias
593 self
.functions
[index
] = obj
594 self
.xref
[obj
.name
] = index
595 elif object_type
== "type":
596 self
.types
[obj
.name
] = obj
601 def startElement(self
, name
, attrs
):
602 """Start a new element in the XML stream.
604 Starts a new element. There are three types of elements that
605 are specially handled by this function. When a "category"
606 element is encountered, the name of the category is saved.
607 If an element is encountered and no API object is
608 in-progress, a new object is created using the API factory.
609 Any future elements, until that API object is closed, are
610 passed to the current objects startElement method.
612 This paradigm was chosen becuase it allows subclasses of the
613 basic API types (i.e., glFunction, glEnum, etc.) to handle
614 additional XML data, GLX protocol information, that the base
615 classes do not know about."""
617 if self
.current_object
!= None:
618 self
.current_object
.startElement(name
, attrs
)
619 elif name
== "category":
620 self
.current_category
= attrs
.get('name', "")
622 self
.current_object
= self
.factory
.create(self
, name
, attrs
)
626 def endElement(self
, name
):
627 if self
.current_object
!= None:
628 if self
.current_object
.endElement(name
):
629 self
.current_object
= None
634 self
.undef_list
.append("PURE")
635 print """# if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 96)
636 # define PURE __attribute__((pure))
641 def printFastcall(self
):
642 self
.undef_list
.append("FASTCALL")
643 print """# if defined(__i386__) && defined(__GNUC__)
644 # define FASTCALL __attribute__((fastcall))
649 def printVisibility(self
, S
, s
):
650 self
.undef_list
.append(S
)
651 print """# if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 3)
652 # define %s __attribute__((visibility("%s")))
655 # endif""" % (S
, s
, S
)
657 def printNoinline(self
):
658 self
.undef_list
.append("NOINLINE")
659 print """# if defined(__GNUC__)
660 # define NOINLINE __attribute__((noinline))
665 def printHaveAlias(self
):
666 self
.undef_list
.append("HAVE_ALIAS")
667 print """# if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95)
671 def printFunction(self
,offset
):
672 """Print a single function.
674 In the base class, this function is empty. All derived
675 classes should over-ride this function."""
679 def printRealHeader(self
):
680 """Print the "real" header for the created file.
682 In the base class, this function is empty. All derived
683 classes should over-ride this function."""
687 def printRealFooter(self
):
688 """Print the "real" footer for the created file.
690 In the base class, this function is empty. All derived
691 classes should over-ride this function."""