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
34 def is_attr_true( attrs
, name
):
35 """Read a name value from an element's attributes.
37 The value read from the attribute list must be either 'true' or
38 'false'. If the value is 'false', zero will be returned. If the
39 value is 'true', non-zero will be returned. An exception will be
40 raised for any other value."""
42 value
= attrs
.get(name
, "false")
45 elif value
== "false":
48 raise RuntimeError('Invalid value "%s" for boolean "%s".' % (value
, name
))
52 """Generic class on which all other API entity types are based."""
54 def __init__(self
, tag_name
, name
, context
):
56 self
.category
= context
.get_category_define()
57 self
.context
= context
58 self
.tag_name
= tag_name
60 context
.append(tag_name
, self
)
63 def startElement(self
, name
, attrs
):
64 """Generic startElement handler.
66 The startElement handler is called for all elements except
67 the one that starts the object. For a foo element, the
68 XML "<foo><bar/></foo>" would cause the startElement handler
69 to be called once, but the endElement handler would be called
73 def endElement(self
, name
):
74 """Generic endElement handler.
76 Generic endElement handler. Returns 1 if the tag containing
77 the object is complete. Otherwise 0 is returned. All
78 derived class endElement handlers should call this method. If
79 the name of the ending tag is the same as the tag that
80 started this object, the object is assumed to be complete.
82 This fails if a tag can contain another tag with the same
83 name. The XML "<foo><foo/><bar/></foo>" would fail. The
84 object would end before the bar tag was processed.
86 The endElement handler is called for every end element
87 associated with an object, even the element that started the
88 object. See the description of startElement an example."""
90 if name
== self
.tag_name
:
95 def get_category_define(self
):
99 class glEnum( glItem
):
100 """Subclass of glItem for representing GL enumerants.
102 This class is not complete, and is not really used yet."""
104 def __init__(self
, context
, name
, attrs
):
105 self
.value
= int(attrs
.get('value', "0x0000"), 0)
107 enum_name
= "GL_" + attrs
.get('name', None)
108 glItem
.__init
__(self
, name
, enum_name
, context
)
110 temp
= attrs
.get('count', None)
111 self
.default_count
= 0
113 self
.default_count
= -1
118 raise RuntimeError('Invalid count value "%s" for enum "%s" in function "%s" when an integer was expected.' % (temp
, self
.name
, n
))
120 self
.default_count
= c
124 def process_attributes(self
, attrs
):
125 name
= attrs
.get('name', None)
127 temp
= attrs
.get('count', None)
129 c
= self
.default_count
134 raise RuntimeError('Invalid count value "%s" for enum "%s" in function "%s" when an integer was expected.' % (temp
, self
.name
, n
))
136 mode_str
= attrs
.get('mode', "set")
137 if mode_str
== "set":
139 elif mode_str
== "get":
142 raise RuntimeError("Invalid mode '%s' for function '%s' in enum '%s'." % (mode_str
, self
.context
.name
, self
.name
))
144 return [name
, c
, mode
]
147 class glType( glItem
):
148 """Subclass of glItem for representing GL types."""
150 def __init__(self
, context
, name
, attrs
):
151 self
.size
= int(attrs
.get('size', "0"))
152 self
.glx_name
= attrs
.get('glx_name', "")
154 type_name
= "GL" + attrs
.get('name', None)
155 glItem
.__init
__(self
, name
, type_name
, context
)
158 class glParameter( glItem
):
159 """Parameter of a glFunction."""
168 def __init__(self
, context
, name
, attrs
):
169 p_name
= attrs
.get('name', None)
170 self
.p_type_string
= attrs
.get('type', None)
172 temp
= attrs
.get('variable_param', None)
174 self
.count_parameter_list
= temp
.replace( ' ', '' ).split( ',' )
176 self
.count_parameter_list
= []
178 self
.p_type
= context
.context
.find_type(self
.p_type_string
)
179 if self
.p_type
== None:
180 raise RuntimeError("Unknown type '%s' in function '%s'." % (self
.p_type_string
, context
.name
))
183 # The count tag can be either a numeric string or the name of
184 # a variable. If it is the name of a variable, the int(c)
185 # statement will throw an exception, and the except block will
188 c
= attrs
.get('count', "0")
190 self
.p_count
= int(c
)
196 self
.count_scale
= int(attrs
.get('count_scale', "1"))
198 self
.is_counter
= is_attr_true( attrs
, 'counter' )
199 self
.is_output
= is_attr_true( attrs
, 'output' )
202 # Pixel data has special parameters.
204 self
.width
= attrs
.get('img_width', None)
205 self
.height
= attrs
.get('img_height', None)
206 self
.depth
= attrs
.get('img_depth', None)
207 self
.extent
= attrs
.get('img_extent', None)
209 self
.img_xoff
= attrs
.get('img_xoff', None)
210 self
.img_yoff
= attrs
.get('img_yoff', None)
211 self
.img_zoff
= attrs
.get('img_zoff', None)
212 self
.img_woff
= attrs
.get('img_woff', None)
214 self
.img_format
= attrs
.get('img_format', None)
215 self
.img_type
= attrs
.get('img_type', None)
216 self
.img_target
= attrs
.get('img_target', None)
218 self
.img_pad_dimensions
= is_attr_true( attrs
, 'img_pad_dimensions' )
219 self
.img_null_flag
= is_attr_true( attrs
, 'img_null_flag' )
220 self
.img_send_null
= is_attr_true( attrs
, 'img_send_null' )
222 if self
.p_count
> 0 or self
.counter
or self
.count_parameter_list
:
228 # If there is a * anywhere in the parameter's type, then it
231 if re
.compile("[*]").search(self
.p_type_string
):
232 # We could do some other validation here. For
233 # example, an output parameter should not be const,
234 # but every non-output parameter should.
238 # If a parameter is not a pointer, then there cannot
239 # be an associated count (either fixed size or
240 # variable) and the parameter cannot be an output.
242 if has_count
or self
.is_output
:
243 raise RuntimeError("Non-pointer type has count or is output.")
246 glItem
.__init
__(self
, name
, p_name
, context
)
250 def is_variable_length_array(self
):
251 """Determine if a parameter is a variable length array.
253 A parameter is considered to be a variable length array if
254 its size depends on the value of another parameter that is
255 an enumerant. The params parameter to glTexEnviv is an
256 example of a variable length array parameter. Arrays whose
257 size depends on a count variable, such as the lists parameter
258 to glCallLists, are not variable length arrays in this
261 return self
.count_parameter_list
or self
.counter
or self
.width
265 return self
.is_pointer
268 def count_string(self
):
269 """Return a string representing the number of items
271 Returns a string representing the number of items in a
272 parameter. For scalar types this will always be "1". For
273 vector types, it will depend on whether or not it is a
274 fixed length vector (like the parameter of glVertex3fv),
275 a counted length (like the vector parameter of
276 glDeleteTextures), or a general variable length vector."""
279 if self
.count_parameter_list
:
281 elif self
.counter
!= None:
284 return str(self
.p_count
)
290 if self
.count_parameter_list
or self
.counter
or self
.width
or self
.is_output
:
292 elif self
.p_count
== 0:
293 return self
.p_type
.size
295 return self
.p_type
.size
* self
.p_count
* self
.count_scale
297 def size_string(self
):
301 b_prod
= self
.p_type
.size
303 # Handle functions like glCompressedTexImage2D that
304 # have a counted 'void *' parameter.
306 if b_prod
== 0: b_prod
= 1
308 if not self
.count_parameter_list
and self
.counter
!= None:
309 if self
.count_scale
> 1:
310 a_prod
= '(%s * %u)' % (self
.counter
, self
.count_scale
)
312 a_prod
= self
.counter
313 elif self
.count_parameter_list
and self
.counter
== None:
315 elif self
.count_parameter_list
and self
.counter
!= None:
316 if self
.count_scale
> 1:
317 b_prod
= '(%s * %u)' % (self
.counter
, self
.count_scale
)
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
):
353 def __init__(self
, context
, name
, attrs
):
354 self
.fn_alias
= attrs
.get('alias', None)
355 self
.fn_parameters
= []
357 self
.count_parameter_list
= []
358 self
.fn_return_type
= "void"
360 temp
= attrs
.get('offset', None)
361 if temp
== None or temp
== "?":
364 self
.fn_offset
= int(temp
)
366 fn_name
= attrs
.get('name', None)
367 if self
.fn_alias
!= None:
368 self
.real_name
= self
.fn_alias
370 self
.real_name
= fn_name
372 self
.parameters_by_name
= {}
373 self
.variable_length_parameters
= []
375 glItem
.__init
__(self
, name
, fn_name
, context
)
379 def parameterIterator(self
):
380 return glParameterIterator(self
.fn_parameters
)
383 def startElement(self
, name
, attrs
):
386 self
.context
.factory
.create(self
, name
, attrs
)
388 print "Error with parameter '%s' in function '%s'." \
389 % (attrs
.get('name','(unknown)'), self
.name
)
391 elif name
== "return":
392 self
.set_return_type(attrs
.get('type', None))
395 def endElement(self
, name
):
396 """Handle the end of a <function> element.
398 At the end of a <function> element, there is some semantic
399 checking that can be done. This prevents some possible
400 exceptions from being thrown elsewhere in the code.
403 if name
== "function":
404 for p
in self
.variable_length_parameters
:
406 counter
= self
.parameters_by_name
[ p
.counter
]
407 if not self
.parameters_by_name
.has_key( p
.counter
):
408 raise RuntimeError("Parameter '%s' of function '%s' has counter '%s', but function has no such parameter." % (p
.name
, self
.name
, p
.counter
))
409 elif not self
.parameters_by_name
[ p
.counter
].is_counter
:
410 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
))
412 for n
in p
.count_parameter_list
:
413 if not self
.parameters_by_name
.has_key( n
):
414 raise RuntimeError("Parameter '%s' of function '%s' has size parameter '%s', but function has no such parameter." % (p
.name
, self
.name
, n
))
421 def append(self
, tag_name
, p
):
422 if tag_name
!= "param":
423 raise RuntimeError("Trying to append '%s' to parameter list of function '%s'." % (tag_name
, self
.name
))
428 self
.fn_parameters
.append(p
)
429 if p
.count_parameter_list
!= []:
430 self
.count_parameter_list
.extend( p
.count_parameter_list
)
432 if p
.is_variable_length_array():
433 self
.variable_length_parameters
.append(p
)
435 self
.parameters_by_name
[ p
.name
] = p
438 def set_return_type(self
, t
):
439 self
.fn_return_type
= t
442 def get_parameter_string(self
):
445 for p
in glFunction
.parameterIterator(self
):
446 arg_string
= arg_string
+ comma
+ p
.p_type_string
+ " " + p
.name
456 """Factory to create objects derived from glItem."""
458 def create(self
, context
, name
, attrs
):
459 if name
== "function":
460 return glFunction(context
, name
, attrs
)
462 return glType(context
, name
, attrs
)
464 return glEnum(context
, name
, attrs
)
465 elif name
== "param":
466 return glParameter(context
, name
, attrs
)
471 class glFunctionIterator
:
472 """Class to iterate over a list of glFunctions
474 Objects of this classare returned by
475 FilterGLAPISpecBase::functionIterator. This default version
476 iterates over the functions in order of dispatch table offset. All
477 of the "true" functions are iterated first, followed by the alias
480 def __init__(self
, context
):
481 self
.context
= context
482 self
.keys
= context
.functions
.keys()
488 for self
.index
in range(0, len(self
.keys
)):
489 if self
.keys
[ self
.index
] >= 0: break
491 if self
.index
== len(self
.keys
):
495 self
.split
= self
.index
- 1
507 k
= self
.keys
[ self
.index
]
509 #if self.context.functions[k].fn_alias == None:
510 # if k != self.prevk + 1:
511 # print 'Missing offset %d' % (prevk)
512 # self.prevk = int(k)
514 self
.index
+= self
.direction
516 if self
.index
== len(self
.keys
):
517 self
.index
= self
.split
520 return self
.context
.functions
[k
]
523 class FilterGLAPISpecBase(saxutils
.XMLFilterBase
):
525 license
= "The license for this file is unspecified."
527 current_object
= None
530 saxutils
.XMLFilterBase
.__init
__(self
)
533 self
.functions_by_name
= {}
534 self
.factory
= glItemFactory()
535 self
.header_tag
= None
537 self
.current_category
= ""
540 def find_type(self
,type_name
):
542 if re
.compile(t
).search(type_name
):
544 print "Unable to find base type matching \"%s\"." % (type_name
)
548 def find_function(self
,function_name
):
549 return self
.functions_by_name
[function_name
]
552 def functionIterator(self
):
553 return glFunctionIterator(self
)
556 def printFunctions(self
):
557 for f
in self
.functionIterator():
558 self
.printFunction(f
)
562 def printHeader(self
):
563 """Print the header associated with all files and call the printRealHeader method."""
565 print '/* DO NOT EDIT - This file generated automatically by %s script */' \
569 print ' * ' + self
.license
.replace('\n', '\n * ')
573 print '#if !defined( %s )' % (self
.header_tag
)
574 print '# define %s' % (self
.header_tag
)
576 self
.printRealHeader();
580 def printFooter(self
):
581 """Print the header associated with all files and call the printRealFooter method."""
583 self
.printFunctions()
584 self
.printRealFooter()
588 for u
in self
.undef_list
:
589 print "# undef %s" % (u
)
591 print '#endif /* !defined( %s ) */' % (self
.header_tag
)
594 def get_category_define(self
):
595 """Convert the category name to the #define that would be found in glext.h"""
597 if re
.compile("[1-9][0-9]*[.][0-9]+").match(self
.current_category
):
598 s
= self
.current_category
599 return "GL_VERSION_" + s
.replace(".", "_")
601 return self
.current_category
604 def append(self
, object_type
, obj
):
605 if object_type
== "function":
606 # If the function is not an alias and has a negative
607 # offset, then we do not need to track it. These are
608 # functions that don't have an assigned offset
610 if obj
.fn_offset
>= 0 or obj
.fn_alias
!= None:
611 if obj
.fn_offset
>= 0:
612 index
= obj
.fn_offset
614 index
= self
.next_alias
617 self
.functions
[index
] = obj
619 self
.functions_by_name
[obj
.name
] = obj
621 elif object_type
== "type":
622 self
.types
[obj
.name
] = obj
627 def startElement(self
, name
, attrs
):
628 """Start a new element in the XML stream.
630 Starts a new element. There are three types of elements that
631 are specially handled by this function. When a "category"
632 element is encountered, the name of the category is saved.
633 If an element is encountered and no API object is
634 in-progress, a new object is created using the API factory.
635 Any future elements, until that API object is closed, are
636 passed to the current objects startElement method.
638 This paradigm was chosen becuase it allows subclasses of the
639 basic API types (i.e., glFunction, glEnum, etc.) to handle
640 additional XML data, GLX protocol information, that the base
641 classes do not know about."""
643 if self
.current_object
!= None:
644 self
.current_object
.startElement(name
, attrs
)
645 elif name
== "category":
646 self
.current_category
= attrs
.get('name', "")
647 elif name
== "include":
648 self
.next_include
= attrs
.get('name', "")
650 self
.current_object
= self
.factory
.create(self
, name
, attrs
)
654 def endElement(self
, name
):
655 if self
.current_object
!= None:
656 if self
.current_object
.endElement(name
):
657 self
.current_object
= None
658 elif name
== "include":
659 parser
= make_parser()
660 parser
.setFeature(feature_namespaces
, 0)
661 parser
.setContentHandler(self
)
663 f
= open(self
.next_include
)
670 self
.undef_list
.append("PURE")
671 print """# if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 96)
672 # define PURE __attribute__((pure))
677 def printFastcall(self
):
678 self
.undef_list
.append("FASTCALL")
679 print """# if defined(__i386__) && defined(__GNUC__)
680 # define FASTCALL __attribute__((fastcall))
685 def printVisibility(self
, S
, s
):
686 self
.undef_list
.append(S
)
687 print """# if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 3)
688 # define %s __attribute__((visibility("%s")))
691 # endif""" % (S
, s
, S
)
693 def printNoinline(self
):
694 self
.undef_list
.append("NOINLINE")
695 print """# if defined(__GNUC__)
696 # define NOINLINE __attribute__((noinline))
701 def printHaveAlias(self
):
702 self
.undef_list
.append("HAVE_ALIAS")
703 print """# if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95)
707 def printFunction(self
,offset
):
708 """Print a single function.
710 In the base class, this function is empty. All derived
711 classes should over-ride this function."""
715 def printRealHeader(self
):
716 """Print the "real" header for the created file.
718 In the base class, this function is empty. All derived
719 classes should over-ride this function."""
723 def printRealFooter(self
):
724 """Print the "real" footer for the created file.
726 In the base class, this function is empty. All derived
727 classes should over-ride this function."""