Turn off debug
[mesa.git] / src / mesa / glapi / gl_XML.py
1 #!/usr/bin/python2
2
3 # (C) Copyright IBM Corporation 2004, 2005
4 # All Rights Reserved.
5 #
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:
12 #
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
15 # Software.
16 #
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
23 # IN THE SOFTWARE.
24 #
25 # Authors:
26 # Ian Romanick <idr@us.ibm.com>
27
28 from xml.sax import saxutils
29 from xml.sax import make_parser
30 from xml.sax.handler import feature_namespaces
31
32 import re
33 import sys
34
35 def is_attr_true( attrs, name ):
36 """Read a name value from an element's attributes.
37
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."""
42
43 value = attrs.get((None, name), "false")
44 if value == "true":
45 return 1
46 elif value == "false":
47 return 0
48 else:
49 raise RuntimeError('Invalid value "%s" for boolean "%s".' % (value, name))
50
51
52 def parse_GL_API( handler, file_name ):
53 """Boiler-plate code to create an XML parser and use it.
54
55 Creates an XML parser and uses that parser with the application
56 supplied SAX callback, which should be derived from
57 FilterGLAPISpecBase.
58 """
59
60 parser = make_parser()
61 parser.setFeature(feature_namespaces, 1)
62 parser.setContentHandler( handler )
63
64 handler.printHeader()
65
66 if not file_name or file_name == "-":
67 parser.parse( sys.stdin )
68 else:
69 parser.parse( file_name )
70
71 handler.printFooter()
72 return
73
74
75 class glItem:
76 """Generic class on which all other API entity types are based."""
77
78 def __init__(self, tag_name, name, context):
79 self.name = name
80 self.category = context.get_category_define()
81 self.context = context
82 self.tag_name = tag_name
83
84 context.append(tag_name, self)
85 return
86
87 def startElementNS(self, name, qname, attrs):
88 """Generic startElement handler.
89
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
94 twice."""
95 return
96
97 def endElementNS(self, name, qname):
98 """Generic endElement handler.
99
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.
105
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.
109
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."""
113
114 if name == (None, self.tag_name):
115 return 1
116 else:
117 return 0
118
119 def get_category_define(self):
120 return self.category
121
122
123 class glEnum( glItem ):
124 """Subclass of glItem for representing GL enumerants.
125
126 This class is not complete, and is not really used yet."""
127
128 def __init__(self, context, name, attrs):
129 self.value = int(attrs.get((None, 'value'), "0x0000"), 0)
130
131 enum_name = "GL_" + attrs.get((None, 'name'), None)
132 glItem.__init__(self, name, enum_name, context)
133
134 temp = attrs.get((None, 'count'), None)
135 self.default_count = 0
136 if temp == "?":
137 self.default_count = -1
138 elif temp:
139 try:
140 c = int(temp)
141 except Exception,e:
142 raise RuntimeError('Invalid count value "%s" for enum "%s" in function "%s" when an integer was expected.' % (temp, self.name, n))
143
144 self.default_count = c
145 return
146
147
148 def process_attributes(self, attrs):
149 name = attrs.get((None, 'name'), None)
150
151 temp = attrs.get((None, 'count'), None)
152 if temp == None:
153 c = self.default_count
154 else:
155 try:
156 c = int(temp)
157 except Exception,e:
158 raise RuntimeError('Invalid count value "%s" for enum "%s" in function "%s" when an integer was expected.' % (temp, self.name, n))
159
160 mode_str = attrs.get((None, 'mode'), "set")
161 if mode_str == "set":
162 mode = 1
163 elif mode_str == "get":
164 mode = 0
165 else:
166 raise RuntimeError("Invalid mode '%s' for function '%s' in enum '%s'." % (mode_str, self.context.name, self.name))
167
168 return [name, c, mode]
169
170
171 class glType( glItem ):
172 """Subclass of glItem for representing GL types."""
173
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'), "")
177
178 type_name = "GL" + attrs.get((None, 'name'), None)
179 glItem.__init__(self, name, type_name, context)
180
181
182 class glParameter( glItem ):
183 """Parameter of a glFunction."""
184 p_type = None
185 p_type_string = ""
186 p_count = 0
187 counter = None
188 is_output = 0
189 is_counter = 0
190 is_pointer = 0
191
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)
195
196 temp = attrs.get((None, 'variable_param'), None)
197 if temp:
198 self.count_parameter_list = temp.split( ' ' )
199 else:
200 self.count_parameter_list = []
201
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))
205
206
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
210 # take over.
211
212 c = attrs.get((None, 'count'), "0")
213 try:
214 self.p_count = int(c)
215 self.counter = None
216 except Exception,e:
217 self.p_count = 0
218 self.counter = c
219
220 self.count_scale = int(attrs.get((None, 'count_scale'), "1"))
221
222 self.is_counter = is_attr_true( attrs, 'counter' )
223 self.is_output = is_attr_true( attrs, 'output' )
224
225
226 # Pixel data has special parameters.
227
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)
232
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)
237
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)
241
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' )
245
246 if self.p_count > 0 or self.counter or self.count_parameter_list:
247 has_count = 1
248 else:
249 has_count = 0
250
251
252 # If there is a * anywhere in the parameter's type, then it
253 # is a pointer.
254
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.
259
260 self.is_pointer = 1;
261 else:
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.
265
266 if has_count or self.is_output:
267 raise RuntimeError("Non-pointer type has count or is output.")
268 self.is_pointer = 0;
269
270 glItem.__init__(self, name, p_name, context)
271 return
272
273
274 def is_variable_length_array(self):
275 """Determine if a parameter is a variable length array.
276
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
283 sense."""
284
285 return self.count_parameter_list or self.counter or self.width
286
287
288 def is_array(self):
289 return self.is_pointer
290
291
292 def count_string(self):
293 """Return a string representing the number of items
294
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."""
301
302 if self.is_array():
303 if self.count_parameter_list:
304 return "compsize"
305 elif self.counter != None:
306 return self.counter
307 else:
308 return str(self.p_count)
309 else:
310 return "1"
311
312
313 def size(self):
314 if self.count_parameter_list or self.counter or self.width or self.is_output:
315 return 0
316 elif self.p_count == 0:
317 return self.p_type.size
318 else:
319 return self.p_type.size * self.p_count * self.count_scale
320
321 def size_string(self):
322 s = self.size()
323 if s == 0:
324 a_prod = "compsize"
325 b_prod = self.p_type.size
326
327 # Handle functions like glCompressedTexImage2D that
328 # have a counted 'void *' parameter.
329
330 if b_prod == 0: b_prod = 1
331
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)
335 else:
336 a_prod = self.counter
337 elif self.count_parameter_list and self.counter == None:
338 pass
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)
342 else:
343 b_prod = self.counter
344 elif self.width:
345 return "compsize"
346 else:
347 raise RuntimeError("Parameter '%s' to function '%s' has size 0." % (self.name, self.context.name))
348
349 return "(%s * %s)" % (a_prod, b_prod)
350 else:
351 return str(s)
352
353
354 class glParameterIterator:
355 """Class to iterate over a list of glParameters.
356
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."""
360
361 def __init__(self, data):
362 self.data = data
363 self.index = 0
364
365 def __iter__(self):
366 return self
367
368 def next(self):
369 if self.index == len( self.data ):
370 raise StopIteration
371 i = self.index
372 self.index += 1
373 return self.data[i]
374
375
376 class glFunction( glItem ):
377 def __init__(self, context, name, attrs):
378 self.fn_alias = attrs.get((None, 'alias'), None)
379 self.fn_parameters = []
380 self.image = None
381 self.count_parameter_list = []
382 self.fn_return_type = "void"
383
384 temp = attrs.get((None, 'offset'), None)
385 if temp == None or temp == "?":
386 self.fn_offset = -1
387 else:
388 self.fn_offset = int(temp)
389
390 fn_name = attrs.get((None, 'name'), None)
391 if self.fn_alias != None:
392 self.real_name = self.fn_alias
393 else:
394 self.real_name = fn_name
395
396 self.parameters_by_name = {}
397 self.variable_length_parameters = []
398
399 glItem.__init__(self, name, fn_name, context)
400 return
401
402
403 def parameterIterator(self):
404 return glParameterIterator(self.fn_parameters)
405
406
407 def startElementNS(self, name, qname, attrs):
408 [uri, true_name] = name
409 if true_name == "param":
410 try:
411 self.context.factory.create(self, true_name, attrs)
412 except RuntimeError:
413 print "Error with parameter '%s' in function '%s'." \
414 % (attrs.get((None, 'name'),'(unknown)'), self.name)
415 raise
416 elif true_name == "return":
417 self.set_return_type(attrs.get((None, 'type'), None))
418
419
420 def endElementNS(self, name, qname):
421 """Handle the end of a <function> element.
422
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.
426 """
427
428 [uri, true_name] = name
429 if true_name == "function":
430 for p in self.variable_length_parameters:
431 if p.counter:
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))
437
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))
441
442 return 1
443 else:
444 return 0
445
446
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))
450
451 if p.width:
452 self.image = p
453
454 self.fn_parameters.append(p)
455 if p.count_parameter_list != []:
456 self.count_parameter_list.extend( p.count_parameter_list )
457
458 if p.is_variable_length_array():
459 self.variable_length_parameters.append(p)
460
461 self.parameters_by_name[ p.name ] = p
462
463
464 def set_return_type(self, t):
465 self.fn_return_type = t
466
467
468 def get_parameter_string(self):
469 arg_string = ""
470 comma = ""
471 for p in glFunction.parameterIterator(self):
472 arg_string = arg_string + comma + p.p_type_string + " " + p.name
473 comma = ", "
474
475 if arg_string == "":
476 arg_string = "void"
477
478 return arg_string
479
480
481 class glItemFactory:
482 """Factory to create objects derived from glItem."""
483
484 def create(self, context, name, attrs):
485 if name == "function":
486 return glFunction(context, name, attrs)
487 elif name == "type":
488 return glType(context, name, attrs)
489 elif name == "enum":
490 return glEnum(context, name, attrs)
491 elif name == "param":
492 return glParameter(context, name, attrs)
493 else:
494 return None
495
496
497 class glFunctionIterator:
498 """Class to iterate over a list of glFunctions
499
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
504 functions."""
505
506 def __init__(self, context):
507 self.context = context
508 self.keys = context.functions.keys()
509 self.keys.sort()
510
511 self.prevk = -1
512 self.direction = 1
513
514 for self.index in range(0, len(self.keys)):
515 if self.keys[ self.index ] >= 0: break
516
517 if self.index == len(self.keys):
518 self.direction = -1
519 self.index -= 1
520
521 self.split = self.index - 1
522 return
523
524
525 def __iter__(self):
526 return self
527
528
529 def next(self):
530 if self.index < 0:
531 raise StopIteration
532
533 k = self.keys[ self.index ]
534
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)
539
540 self.index += self.direction
541
542 if self.index == len(self.keys):
543 self.index = self.split
544 self.direction = -1
545
546 return self.context.functions[k]
547
548
549 class FilterGLAPISpecBase(saxutils.XMLFilterBase):
550 name = "a"
551 license = "The license for this file is unspecified."
552 next_alias = -2
553 current_object = None
554
555 def __init__(self):
556 saxutils.XMLFilterBase.__init__(self)
557 self.functions = {}
558 self.types = {}
559 self.functions_by_name = {}
560 self.factory = glItemFactory()
561 self.header_tag = None
562 self.undef_list = []
563 self.current_category = ""
564
565
566 def find_type(self,type_name):
567 for t in self.types:
568 if re.compile(t).search(type_name):
569 return self.types[t]
570 print "Unable to find base type matching \"%s\"." % (type_name)
571 return None
572
573
574 def find_function(self,function_name):
575 return self.functions_by_name[function_name]
576
577
578 def functionIterator(self):
579 return glFunctionIterator(self)
580
581
582 def printFunctions(self):
583 for f in self.functionIterator():
584 self.printFunction(f)
585 return
586
587
588 def printHeader(self):
589 """Print the header associated with all files and call the printRealHeader method."""
590
591 print '/* DO NOT EDIT - This file generated automatically by %s script */' \
592 % (self.name)
593 print ''
594 print '/*'
595 print ' * ' + self.license.replace('\n', '\n * ')
596 print ' */'
597 print ''
598 if self.header_tag:
599 print '#if !defined( %s )' % (self.header_tag)
600 print '# define %s' % (self.header_tag)
601 print ''
602 self.printRealHeader();
603 return
604
605
606 def printFooter(self):
607 """Print the header associated with all files and call the printRealFooter method."""
608
609 self.printFunctions()
610 self.printRealFooter()
611 if self.header_tag:
612 if self.undef_list:
613 print ''
614 for u in self.undef_list:
615 print "# undef %s" % (u)
616 print ''
617 print '#endif /* !defined( %s ) */' % (self.header_tag)
618
619
620 def get_category_define(self):
621 """Convert the category name to the #define that would be found in glext.h"""
622
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(".", "_")
626 else:
627 return self.current_category
628
629
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
635
636 if not self.functions_by_name.has_key(obj.name):
637 self.functions_by_name[obj.name] = obj
638
639 if obj.fn_offset >= 0 or obj.fn_alias != None:
640 if obj.fn_offset >= 0:
641 index = obj.fn_offset
642 else:
643 index = self.next_alias
644 self.next_alias -= 1
645
646 self.functions[index] = obj
647 else:
648 # We should do some checking here to make
649 # sure the functions are an identical match.
650 pass
651
652 elif object_type == "type":
653 self.types[obj.name] = obj
654
655 return
656
657
658 def startElementNS(self, name, qname, attrs):
659 """Start a new element in the XML stream.
660
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.
668
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."""
673
674 [uri, true_name] = name
675 if uri is None:
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'), "")
682 else:
683 self.current_object = self.factory.create(self, true_name, attrs)
684 return
685
686
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)
695
696 f = open(self.next_include)
697 parser.parse(f)
698
699 return
700
701
702 def printPure(self):
703 self.undef_list.append("PURE")
704 print """# if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 96)
705 # define PURE __attribute__((pure))
706 # else
707 # define PURE
708 # endif"""
709
710 def printFastcall(self):
711 self.undef_list.append("FASTCALL")
712 print """# if defined(__i386__) && defined(__GNUC__)
713 # define FASTCALL __attribute__((fastcall))
714 # else
715 # define FASTCALL
716 # endif"""
717
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")))
722 # else
723 # define %s
724 # endif""" % (S, s, S)
725
726 def printNoinline(self):
727 self.undef_list.append("NOINLINE")
728 print """# if defined(__GNUC__)
729 # define NOINLINE __attribute__((noinline))
730 # else
731 # define NOINLINE
732 # endif"""
733
734 def printHaveAlias(self):
735 self.undef_list.append("HAVE_ALIAS")
736 print """# if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95)
737 # define HAVE_ALIAS
738 # endif"""
739
740 def printFunction(self,offset):
741 """Print a single function.
742
743 In the base class, this function is empty. All derived
744 classes should over-ride this function."""
745 return
746
747
748 def printRealHeader(self):
749 """Print the "real" header for the created file.
750
751 In the base class, this function is empty. All derived
752 classes should over-ride this function."""
753 return
754
755
756 def printRealFooter(self):
757 """Print the "real" footer for the created file.
758
759 In the base class, this function is empty. All derived
760 classes should over-ride this function."""
761 return