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)
94 self
.default_count
= 0
96 self
.default_count
= -1
101 raise RuntimeError('Invalid count value "%s" for enum "%s" in function "%s" when an integer was expected.' % (temp
, self
.name
, n
))
103 self
.default_count
= c
107 def process_attributes(self
, attrs
):
108 name
= attrs
.get('name', None)
110 temp
= attrs
.get('count', None)
112 c
= self
.default_count
117 raise RuntimeError('Invalid count value "%s" for enum "%s" in function "%s" when an integer was expected.' % (temp
, self
.name
, n
))
119 mode_str
= attrs
.get('mode', "set")
120 if mode_str
== "set":
122 elif mode_str
== "get":
125 raise RuntimeError("Invalid mode '%s' for function '%s' in enum '%s'." % (mode_str
, self
.context
.name
, self
.name
))
127 return [name
, c
, mode
]
130 class glType( glItem
):
131 """Subclass of glItem for representing GL types."""
133 def __init__(self
, context
, name
, attrs
):
134 self
.size
= int(attrs
.get('size', "0"))
135 self
.glx_name
= attrs
.get('glx_name', "")
137 type_name
= "GL" + attrs
.get('name', None)
138 glItem
.__init
__(self
, name
, type_name
, context
)
141 class glParameter( glItem
):
142 """Parameter of a glFunction."""
151 def __init__(self
, context
, name
, attrs
):
152 p_name
= attrs
.get('name', None)
153 self
.p_type_string
= attrs
.get('type', None)
155 temp
= attrs
.get('variable_param', None)
157 self
.count_parameter_list
= temp
.replace( ' ', '' ).split( ',' )
159 self
.count_parameter_list
= []
161 self
.p_type
= context
.context
.find_type(self
.p_type_string
)
162 if self
.p_type
== None:
163 raise RuntimeError("Unknown type '%s' in function '%s'." % (self
.p_type_string
, context
.name
))
166 # The count tag can be either a numeric string or the name of
167 # a variable. If it is the name of a variable, the int(c)
168 # statement will throw an exception, and the except block will
171 c
= attrs
.get('count', "0")
173 self
.p_count
= int(c
)
179 self
.count_scale
= int(attrs
.get('count_scale', "1"))
181 if attrs
.get('counter', "false") == "true":
186 if attrs
.get('output', "false") == "true":
192 # Pixel data has special parameters.
194 self
.width
= attrs
.get('img_width', None)
195 self
.height
= attrs
.get('img_height', None)
196 self
.depth
= attrs
.get('img_depth', None)
197 self
.extent
= attrs
.get('img_extent', None)
199 self
.img_xoff
= attrs
.get('img_xoff', None)
200 self
.img_yoff
= attrs
.get('img_yoff', None)
201 self
.img_zoff
= attrs
.get('img_zoff', None)
202 self
.img_woff
= attrs
.get('img_woff', None)
204 self
.img_format
= attrs
.get('img_format', None)
205 self
.img_type
= attrs
.get('img_type', None)
206 self
.img_target
= attrs
.get('img_target', None)
208 pad
= attrs
.get('img_pad_dimensions', "false")
210 self
.img_pad_dimensions
= 1
212 self
.img_pad_dimensions
= 0
215 null_flag
= attrs
.get('img_null_flag', "false")
216 if null_flag
== "true":
217 self
.img_null_flag
= 1
219 self
.img_null_flag
= 0
221 send_null
= attrs
.get('img_send_null', "false")
222 if send_null
== "true":
223 self
.img_send_null
= 1
225 self
.img_send_null
= 0
229 if self
.p_count
> 0 or self
.counter
or self
.count_parameter_list
:
235 # If there is a * anywhere in the parameter's type, then it
238 if re
.compile("[*]").search(self
.p_type_string
):
239 # We could do some other validation here. For
240 # example, an output parameter should not be const,
241 # but every non-output parameter should.
245 # If a parameter is not a pointer, then there cannot
246 # be an associated count (either fixed size or
247 # variable) and the parameter cannot be an output.
249 if has_count
or self
.is_output
:
250 raise RuntimeError("Non-pointer type has count or is output.")
253 glItem
.__init
__(self
, name
, p_name
, context
)
257 def is_variable_length_array(self
):
258 """Determine if a parameter is a variable length array.
260 A parameter is considered to be a variable length array if
261 its size depends on the value of another parameter that is
262 an enumerant. The params parameter to glTexEnviv is an
263 example of a variable length array parameter. Arrays whose
264 size depends on a count variable, such as the lists parameter
265 to glCallLists, are not variable length arrays in this
268 return self
.count_parameter_list
or self
.counter
or self
.width
272 return self
.is_pointer
275 def count_string(self
):
276 """Return a string representing the number of items
278 Returns a string representing the number of items in a
279 parameter. For scalar types this will always be "1". For
280 vector types, it will depend on whether or not it is a
281 fixed length vector (like the parameter of glVertex3fv),
282 a counted length (like the vector parameter of
283 glDeleteTextures), or a general variable length vector."""
286 if self
.count_parameter_list
:
288 elif self
.counter
!= None:
291 return str(self
.p_count
)
297 if self
.count_parameter_list
or self
.counter
or self
.width
or self
.is_output
:
299 elif self
.p_count
== 0:
300 return self
.p_type
.size
302 return self
.p_type
.size
* self
.p_count
* self
.count_scale
304 def size_string(self
):
308 b_prod
= self
.p_type
.size
310 # Handle functions like glCompressedTexImage2D that
311 # have a counted 'void *' parameter.
313 if b_prod
== 0: b_prod
= 1
315 if not self
.count_parameter_list
and self
.counter
!= None:
316 if self
.count_scale
> 1:
317 a_prod
= '(%s * %u)' % (self
.counter
, self
.count_scale
)
319 a_prod
= self
.counter
320 elif self
.count_parameter_list
and self
.counter
== None:
322 elif self
.count_parameter_list
and self
.counter
!= None:
323 if self
.count_scale
> 1:
324 b_prod
= '(%s * %u)' % (self
.counter
, self
.count_scale
)
326 b_prod
= self
.counter
330 raise RuntimeError("Parameter '%s' to function '%s' has size 0." % (self
.name
, self
.context
.name
))
332 return "(%s * %s)" % (a_prod
, b_prod
)
337 class glParameterIterator
:
338 """Class to iterate over a list of glParameters.
340 Objects of this class are returned by the parameterIterator method of
341 the glFunction class. They are used to iterate over the list of
342 parameters to the function."""
344 def __init__(self
, data
):
352 if self
.index
== len( self
.data
):
359 class glFunction( glItem
):
360 def __init__(self
, context
, name
, attrs
):
361 self
.fn_alias
= attrs
.get('alias', None)
362 self
.fn_parameters
= []
364 self
.count_parameter_list
= []
365 self
.fn_return_type
= "void"
367 temp
= attrs
.get('offset', None)
368 if temp
== None or temp
== "?":
371 self
.fn_offset
= int(temp
)
373 fn_name
= attrs
.get('name', None)
374 if self
.fn_alias
!= None:
375 self
.real_name
= self
.fn_alias
377 self
.real_name
= fn_name
379 self
.parameters_by_name
= {}
380 self
.variable_length_parameters
= []
382 glItem
.__init
__(self
, name
, fn_name
, context
)
386 def parameterIterator(self
):
387 return glParameterIterator(self
.fn_parameters
)
390 def startElement(self
, name
, attrs
):
393 self
.context
.factory
.create(self
, name
, attrs
)
395 print "Error with parameter '%s' in function '%s'." \
396 % (attrs
.get('name','(unknown)'), self
.name
)
398 elif name
== "return":
399 self
.set_return_type(attrs
.get('type', None))
402 def endElement(self
, name
):
403 """Handle the end of a <function> element.
405 At the end of a <function> element, there is some semantic
406 checking that can be done. This prevents some possible
407 exceptions from being thrown elsewhere in the code.
410 if name
== "function":
411 for p
in self
.variable_length_parameters
:
413 counter
= self
.parameters_by_name
[ p
.counter
]
414 if not self
.parameters_by_name
.has_key( p
.counter
):
415 raise RuntimeError("Parameter '%s' of function '%s' has counter '%s', but function has no such parameter." % (p
.name
, self
.name
, p
.counter
))
416 elif not self
.parameters_by_name
[ p
.counter
].is_counter
:
417 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
))
419 for n
in p
.count_parameter_list
:
420 if not self
.parameters_by_name
.has_key( n
):
421 raise RuntimeError("Parameter '%s' of function '%s' has size parameter '%s', but function has no such parameter." % (p
.name
, self
.name
, n
))
428 def append(self
, tag_name
, p
):
429 if tag_name
!= "param":
430 raise RuntimeError("Trying to append '%s' to parameter list of function '%s'." % (tag_name
, self
.name
))
435 self
.fn_parameters
.append(p
)
436 if p
.count_parameter_list
!= []:
437 self
.count_parameter_list
.extend( p
.count_parameter_list
)
439 if p
.is_variable_length_array():
440 self
.variable_length_parameters
.append(p
)
442 self
.parameters_by_name
[ p
.name
] = p
445 def set_return_type(self
, t
):
446 self
.fn_return_type
= t
449 def get_parameter_string(self
):
452 for p
in glFunction
.parameterIterator(self
):
453 arg_string
= arg_string
+ comma
+ p
.p_type_string
+ " " + p
.name
463 """Factory to create objects derived from glItem."""
465 def create(self
, context
, name
, attrs
):
466 if name
== "function":
467 return glFunction(context
, name
, attrs
)
469 return glType(context
, name
, attrs
)
471 return glEnum(context
, name
, attrs
)
472 elif name
== "param":
473 return glParameter(context
, name
, attrs
)
478 class glFunctionIterator
:
479 """Class to iterate over a list of glFunctions
481 Objects of this classare returned by
482 FilterGLAPISpecBase::functionIterator. This default version
483 iterates over the functions in order of dispatch table offset. All
484 of the "true" functions are iterated first, followed by the alias
487 def __init__(self
, context
):
488 self
.context
= context
489 self
.keys
= context
.functions
.keys()
495 for self
.index
in range(0, len(self
.keys
)):
496 if self
.keys
[ self
.index
] >= 0: break
498 if self
.index
== len(self
.keys
):
502 self
.split
= self
.index
- 1
514 k
= self
.keys
[ self
.index
]
516 #if self.context.functions[k].fn_alias == None:
517 # if k != self.prevk + 1:
518 # print 'Missing offset %d' % (prevk)
519 # self.prevk = int(k)
521 self
.index
+= self
.direction
523 if self
.index
== len(self
.keys
):
524 self
.index
= self
.split
527 return self
.context
.functions
[k
]
530 class FilterGLAPISpecBase(saxutils
.XMLFilterBase
):
532 license
= "The license for this file is unspecified."
534 current_object
= None
537 saxutils
.XMLFilterBase
.__init
__(self
)
540 self
.functions_by_name
= {}
541 self
.factory
= glItemFactory()
542 self
.header_tag
= None
544 self
.current_category
= ""
547 def find_type(self
,type_name
):
549 if re
.compile(t
).search(type_name
):
551 print "Unable to find base type matching \"%s\"." % (type_name
)
555 def find_function(self
,function_name
):
556 return self
.functions_by_name
[function_name
]
559 def functionIterator(self
):
560 return glFunctionIterator(self
)
563 def printFunctions(self
):
564 for f
in self
.functionIterator():
565 self
.printFunction(f
)
569 def printHeader(self
):
570 """Print the header associated with all files and call the printRealHeader method."""
572 print '/* DO NOT EDIT - This file generated automatically by %s script */' \
576 print ' * ' + self
.license
.replace('\n', '\n * ')
580 print '#if !defined( %s )' % (self
.header_tag
)
581 print '# define %s' % (self
.header_tag
)
583 self
.printRealHeader();
587 def printFooter(self
):
588 """Print the header associated with all files and call the printRealFooter method."""
590 self
.printFunctions()
591 self
.printRealFooter()
595 for u
in self
.undef_list
:
596 print "# undef %s" % (u
)
598 print '#endif /* !defined( %s ) */' % (self
.header_tag
)
601 def get_category_define(self
):
602 """Convert the category name to the #define that would be found in glext.h"""
604 if re
.compile("[1-9][0-9]*[.][0-9]+").match(self
.current_category
):
605 s
= self
.current_category
606 return "GL_VERSION_" + s
.replace(".", "_")
608 return self
.current_category
611 def append(self
, object_type
, obj
):
612 if object_type
== "function":
613 # If the function is not an alias and has a negative
614 # offset, then we do not need to track it. These are
615 # functions that don't have an assigned offset
617 if obj
.fn_offset
>= 0 or obj
.fn_alias
!= None:
618 if obj
.fn_offset
>= 0:
619 index
= obj
.fn_offset
621 index
= self
.next_alias
624 self
.functions
[index
] = obj
626 self
.functions_by_name
[obj
.name
] = obj
628 elif object_type
== "type":
629 self
.types
[obj
.name
] = obj
634 def startElement(self
, name
, attrs
):
635 """Start a new element in the XML stream.
637 Starts a new element. There are three types of elements that
638 are specially handled by this function. When a "category"
639 element is encountered, the name of the category is saved.
640 If an element is encountered and no API object is
641 in-progress, a new object is created using the API factory.
642 Any future elements, until that API object is closed, are
643 passed to the current objects startElement method.
645 This paradigm was chosen becuase it allows subclasses of the
646 basic API types (i.e., glFunction, glEnum, etc.) to handle
647 additional XML data, GLX protocol information, that the base
648 classes do not know about."""
650 if self
.current_object
!= None:
651 self
.current_object
.startElement(name
, attrs
)
652 elif name
== "category":
653 self
.current_category
= attrs
.get('name', "")
654 elif name
== "include":
655 self
.next_include
= attrs
.get('name', "")
657 self
.current_object
= self
.factory
.create(self
, name
, attrs
)
661 def endElement(self
, name
):
662 if self
.current_object
!= None:
663 if self
.current_object
.endElement(name
):
664 self
.current_object
= None
665 elif name
== "include":
666 parser
= make_parser()
667 parser
.setFeature(feature_namespaces
, 0)
668 parser
.setContentHandler(self
)
670 f
= open(self
.next_include
)
677 self
.undef_list
.append("PURE")
678 print """# if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 96)
679 # define PURE __attribute__((pure))
684 def printFastcall(self
):
685 self
.undef_list
.append("FASTCALL")
686 print """# if defined(__i386__) && defined(__GNUC__)
687 # define FASTCALL __attribute__((fastcall))
692 def printVisibility(self
, S
, s
):
693 self
.undef_list
.append(S
)
694 print """# if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 3)
695 # define %s __attribute__((visibility("%s")))
698 # endif""" % (S
, s
, S
)
700 def printNoinline(self
):
701 self
.undef_list
.append("NOINLINE")
702 print """# if defined(__GNUC__)
703 # define NOINLINE __attribute__((noinline))
708 def printHaveAlias(self
):
709 self
.undef_list
.append("HAVE_ALIAS")
710 print """# if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95)
714 def printFunction(self
,offset
):
715 """Print a single function.
717 In the base class, this function is empty. All derived
718 classes should over-ride this function."""
722 def printRealHeader(self
):
723 """Print the "real" header for the created file.
725 In the base class, this function is empty. All derived
726 classes should over-ride this function."""
730 def printRealFooter(self
):
731 """Print the "real" footer for the created file.
733 In the base class, this function is empty. All derived
734 classes should over-ride this function."""