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>
29
import re, sys, string
33
def parse_GL_API( file_name, factory = None ):
34
doc = libxml2.readFile( file_name, None, libxml2.XML_PARSE_XINCLUDE + libxml2.XML_PARSE_NOBLANKS + libxml2.XML_PARSE_DTDVALID + libxml2.XML_PARSE_DTDATTR + libxml2.XML_PARSE_DTDLOAD + libxml2.XML_PARSE_NOENT )
35
ret = doc.xincludeProcess()
38
factory = gl_item_factory()
40
api = factory.create_item( "api", None, None )
41
api.process_element( doc )
43
# After the XML has been processed, we need to go back and assign
44
# dispatch offsets to the functions that request that their offsets
45
# be assigned by the scripts. Typically this means all functions
46
# that are not part of the ABI.
48
for func in api.functionIterateByCategory():
49
if func.assign_offset:
50
func.offset = api.next_offset;
58
def is_attr_true( element, name ):
59
"""Read a name value from an element's attributes.
61
The value read from the attribute list must be either 'true' or
62
'false'. If the value is 'false', zero will be returned. If the
63
value is 'true', non-zero will be returned. An exception will be
64
raised for any other value."""
66
value = element.nsProp( name, None )
69
elif value == "false":
72
raise RuntimeError('Invalid value "%s" for boolean "%s".' % (value, name))
76
"""Base class of all API pretty-printers.
78
In the model-view-controller pattern, this is the view. Any derived
79
class will want to over-ride the printBody, printRealHader, and
80
printRealFooter methods. Some derived classes may want to over-ride
81
printHeader and printFooter, or even Print (though this is unlikely).
85
# Name of the script that is generating the output file.
86
# Every derived class should set this to the name of its
92
# License on the *generated* source file. This may differ
93
# from the license on the script that is generating the file.
94
# Every derived class should set this to some reasonable
97
# See license.py for an example of a reasonable value.
99
self.license = "The license for this file is unspecified."
102
# The header_tag is the name of the C preprocessor define
103
# used to prevent multiple inclusion. Typically only
104
# generated C header files need this to be set. Setting it
105
# causes code to be generated automatically in printHeader
108
self.header_tag = None
111
# List of file-private defines that must be undefined at the
112
# end of the file. This can be used in header files to define
113
# names for use in the file, then undefine them at the end of
120
def Print(self, api):
127
def printHeader(self):
128
"""Print the header associated with all files and call the printRealHeader method."""
130
print '/* DO NOT EDIT - This file generated automatically by %s script */' \
134
print ' * ' + self.license.replace('\n', '\n * ')
138
print '#if !defined( %s )' % (self.header_tag)
139
print '# define %s' % (self.header_tag)
141
self.printRealHeader();
145
def printFooter(self):
146
"""Print the header associated with all files and call the printRealFooter method."""
148
self.printRealFooter()
152
for u in self.undef_list:
153
print "# undef %s" % (u)
157
print '#endif /* !defined( %s ) */' % (self.header_tag)
160
def printRealHeader(self):
161
"""Print the "real" header for the created file.
163
In the base class, this function is empty. All derived
164
classes should over-ride this function."""
168
def printRealFooter(self):
169
"""Print the "real" footer for the created file.
171
In the base class, this function is empty. All derived
172
classes should over-ride this function."""
177
"""Conditionally define `PURE' function attribute.
179
Conditionally defines a preprocessor macro `PURE' that wraps
180
GCC's `pure' function attribute. The conditional code can be
181
easilly adapted to other compilers that support a similar
184
The name is also added to the file's undef_list.
186
self.undef_list.append("PURE")
187
print """# if defined(__GNUC__) || (defined(__SUNPRO_C) && (__SUNPRO_C >= 0x590))
188
# define PURE __attribute__((pure))
195
def printFastcall(self):
196
"""Conditionally define `FASTCALL' function attribute.
198
Conditionally defines a preprocessor macro `FASTCALL' that
199
wraps GCC's `fastcall' function attribute. The conditional
200
code can be easilly adapted to other compilers that support a
203
The name is also added to the file's undef_list.
206
self.undef_list.append("FASTCALL")
207
print """# if defined(__i386__) && defined(__GNUC__) && !defined(__CYGWIN__) && !defined(__MINGW32__)
208
# define FASTCALL __attribute__((fastcall))
215
def printVisibility(self, S, s):
216
"""Conditionally define visibility function attribute.
218
Conditionally defines a preprocessor macro name S that wraps
219
GCC's visibility function attribute. The visibility used is
220
the parameter s. The conditional code can be easilly adapted
221
to other compilers that support a similar feature.
223
The name is also added to the file's undef_list.
226
self.undef_list.append(S)
227
print """# if defined(__GNUC__) || (defined(__SUNPRO_C) && (__SUNPRO_C >= 0x590)) && defined(__ELF__)
228
# define %s __attribute__((visibility("%s")))
231
# endif""" % (S, s, S)
235
def printNoinline(self):
236
"""Conditionally define `NOINLINE' function attribute.
238
Conditionally defines a preprocessor macro `NOINLINE' that
239
wraps GCC's `noinline' function attribute. The conditional
240
code can be easilly adapted to other compilers that support a
243
The name is also added to the file's undef_list.
246
self.undef_list.append("NOINLINE")
247
print """# if defined(__GNUC__) || (defined(__SUNPRO_C) && (__SUNPRO_C >= 0x590))
248
# define NOINLINE __attribute__((noinline))
255
def real_function_name(element):
256
name = element.nsProp( "name", None )
257
alias = element.nsProp( "alias", None )
265
def real_category_name(c):
266
if re.compile("[1-9][0-9]*[.][0-9]+").match(c):
267
return "GL_VERSION_" + c.replace(".", "_")
272
def classify_category(name, number):
273
"""Based on the category name and number, select a numerical class for it.
275
Categories are divided into four classes numbered 0 through 3. The
278
0. Core GL versions, sorted by version number.
279
1. ARB extensions, sorted by extension number.
280
2. Non-ARB extensions, sorted by extension number.
281
3. Un-numbered extensions, sorted by extension name.
285
core_version = float(name)
289
if core_version > 0.0:
292
elif name.startswith("GL_ARB_") or name.startswith("GLX_ARB_") or name.startswith("WGL_ARB_"):
304
return [cat_type, key]
307
def create_parameter_string(parameters, include_names):
308
"""Create a parameter string from a list of gl_parameters."""
316
list.append( p.string() )
318
list.append( p.type_string() )
320
if len(list) == 0: list = ["void"]
322
return string.join(list, ", ")
326
def __init__(self, element, context):
327
self.context = context
328
self.name = element.nsProp( "name", None )
329
self.category = real_category_name( element.parent.nsProp( "name", None ) )
333
class gl_type( gl_item ):
334
def __init__(self, element, context):
335
gl_item.__init__(self, element, context)
336
self.size = int( element.nsProp( "size", None ), 0 )
338
te = typeexpr.type_expression( None )
339
tn = typeexpr.type_node()
340
tn.size = int( element.nsProp( "size", None ), 0 )
341
tn.integer = not is_attr_true( element, "float" )
342
tn.unsigned = is_attr_true( element, "unsigned" )
343
tn.name = "GL" + self.name
344
te.set_base_type_node( tn )
350
def get_type_expression(self):
351
return self.type_expr
354
class gl_enum( gl_item ):
355
def __init__(self, element, context):
356
gl_item.__init__(self, element, context)
357
self.value = int( element.nsProp( "value", None ), 0 )
359
temp = element.nsProp( "count", None )
360
if not temp or temp == "?":
361
self.default_count = -1
366
raise RuntimeError('Invalid count value "%s" for enum "%s" in function "%s" when an integer was expected.' % (temp, self.name, n))
368
self.default_count = c
374
"""Calculate a 'priority' for this enum name.
376
When an enum is looked up by number, there may be many
377
possible names, but only one is the 'prefered' name. The
378
priority is used to select which name is the 'best'.
380
Highest precedence is given to core GL name. ARB extension
381
names have the next highest, followed by EXT extension names.
382
Vendor extension names are the lowest.
385
if self.name.endswith( "_BIT" ):
390
if self.category.startswith( "GL_VERSION_" ):
392
elif self.category.startswith( "GL_ARB_" ):
394
elif self.category.startswith( "GL_EXT_" ):
399
return priority + bias
404
def __init__(self, element, context):
405
self.name = element.nsProp( "name", None )
407
ts = element.nsProp( "type", None )
408
self.type_expr = typeexpr.type_expression( ts, context )
410
temp = element.nsProp( "variable_param", None )
412
self.count_parameter_list = temp.split( ' ' )
414
self.count_parameter_list = []
416
# The count tag can be either a numeric string or the name of
417
# a variable. If it is the name of a variable, the int(c)
418
# statement will throw an exception, and the except block will
421
c = element.nsProp( "count", None )
431
self.count_scale = int(element.nsProp( "count_scale", None ))
433
elements = (count * self.count_scale)
437
#if ts == "GLdouble":
438
# print '/* stack size -> %s = %u (before)*/' % (self.name, self.type_expr.get_stack_size())
439
# print '/* # elements = %u */' % (elements)
440
self.type_expr.set_elements( elements )
441
#if ts == "GLdouble":
442
# print '/* stack size -> %s = %u (after) */' % (self.name, self.type_expr.get_stack_size())
444
self.is_client_only = is_attr_true( element, 'client_only' )
445
self.is_counter = is_attr_true( element, 'counter' )
446
self.is_output = is_attr_true( element, 'output' )
449
# Pixel data has special parameters.
451
self.width = element.nsProp('img_width', None)
452
self.height = element.nsProp('img_height', None)
453
self.depth = element.nsProp('img_depth', None)
454
self.extent = element.nsProp('img_extent', None)
456
self.img_xoff = element.nsProp('img_xoff', None)
457
self.img_yoff = element.nsProp('img_yoff', None)
458
self.img_zoff = element.nsProp('img_zoff', None)
459
self.img_woff = element.nsProp('img_woff', None)
461
self.img_format = element.nsProp('img_format', None)
462
self.img_type = element.nsProp('img_type', None)
463
self.img_target = element.nsProp('img_target', None)
465
self.img_pad_dimensions = is_attr_true( element, 'img_pad_dimensions' )
466
self.img_null_flag = is_attr_true( element, 'img_null_flag' )
467
self.img_send_null = is_attr_true( element, 'img_send_null' )
469
self.is_padding = is_attr_true( element, 'padding' )
473
def compatible(self, other):
478
return self.is_pointer()
481
def is_pointer(self):
482
return self.type_expr.is_pointer()
492
def is_variable_length(self):
493
return len(self.count_parameter_list) or self.counter
497
count = self.type_expr.get_element_count()
499
if (self.size() / count) == 8:
509
return self.type_expr.original_string + " " + self.name
512
def type_string(self):
513
return self.type_expr.original_string
516
def get_base_type_string(self):
517
return self.type_expr.get_base_name()
520
def get_dimensions(self):
522
return [ 0, "0", "0", "0", "0" ]
542
return [ dim, w, h, d, e ]
545
def get_stack_size(self):
546
return self.type_expr.get_stack_size()
553
return self.type_expr.get_element_size()
556
def get_element_count(self):
557
c = self.type_expr.get_element_count()
564
def size_string(self, use_parens = 1):
566
if self.counter or self.count_parameter_list:
567
list = [ "compsize" ]
569
if self.counter and self.count_parameter_list:
570
list.append( self.counter )
572
list = [ self.counter ]
575
list.append( str(s) )
577
if len(list) > 1 and use_parens :
578
return "(%s)" % (string.join(list, " * "))
580
return string.join(list, " * ")
582
elif self.is_image():
588
def format_string(self):
589
if self.type_expr.original_string == "GLenum":
592
return self.type_expr.format_string()
596
class gl_function( gl_item ):
597
def __init__(self, element, context):
598
self.context = context
601
self.entry_points = []
602
self.return_type = "void"
608
self.assign_offset = 0
610
self.static_entry_points = []
612
# Track the parameter string (for the function prototype)
613
# for each entry-point. This is done because some functions
614
# change their prototype slightly when promoted from extension
615
# to ARB extension to core. glTexImage3DEXT and glTexImage3D
616
# are good examples of this. Scripts that need to generate
617
# code for these differing aliases need to real prototype
618
# for each entry-point. Otherwise, they may generate code
619
# that won't compile.
621
self.parameter_strings = {}
623
self.process_element( element )
628
def process_element(self, element):
629
name = element.nsProp( "name", None )
630
alias = element.nsProp( "alias", None )
632
if is_attr_true(element, "static_dispatch"):
633
self.static_entry_points.append(name)
635
self.entry_points.append( name )
641
# Only try to set the offset when a non-alias
642
# entry-point is being processes.
644
offset = element.nsProp( "offset", None )
651
if offset == "assign":
652
self.assign_offset = 1
656
self.name = true_name
657
elif self.name != true_name:
658
raise RuntimeError("Function true name redefined. Was %s, now %s." % (self.name, true_name))
661
# There are two possible cases. The first time an entry-point
662
# with data is seen, self.initialized will be 0. On that
663
# pass, we just fill in the data. The next time an
664
# entry-point with data is seen, self.initialized will be 1.
665
# On that pass we have to make that the new values match the
666
# valuse from the previous entry-point.
670
child = element.children
672
if child.type == "element":
673
if child.name == "return":
674
return_type = child.nsProp( "type", None )
675
elif child.name == "param":
676
param = self.context.factory.create_item( "parameter", child, self.context)
677
parameters.append( param )
683
if self.return_type != return_type:
684
raise RuntimeError( "Return type changed in %s. Was %s, now %s." % (name, self.return_type, return_type))
686
if len(parameters) != len(self.parameters):
687
raise RuntimeError( "Parameter count mismatch in %s. Was %d, now %d." % (name, len(self.parameters), len(parameters)))
689
for j in range(0, len(parameters)):
691
p2 = self.parameters[j]
692
if not p1.compatible( p2 ):
693
raise RuntimeError( 'Parameter type mismatch in %s. "%s" was "%s", now "%s".' % (name, p2.name, p2.type_expr.original_string, p1.type_expr.original_string))
696
if true_name == name or not self.initialized:
697
self.return_type = return_type
698
self.parameters = parameters
700
for param in self.parameters:
702
self.images.append( param )
706
self.parameter_strings[name] = create_parameter_string(parameters, 1)
708
self.parameter_strings[name] = None
713
def get_images(self):
714
"""Return potentially empty list of input images."""
718
def parameterIterator(self):
719
return self.parameters.__iter__();
722
def get_parameter_string(self, entrypoint = None):
724
s = self.parameter_strings[ entrypoint ]
728
return create_parameter_string( self.parameters, 1 )
730
def get_called_parameter_string(self):
734
for p in self.parameterIterator():
735
p_string = p_string + comma + p.name
742
return (self.offset >= 0 and not self.assign_offset)
744
def is_static_entry_point(self, name):
745
return name in self.static_entry_points
747
def dispatch_name(self):
748
if self.name in self.static_entry_points:
751
return "_dispatch_stub_%u" % (self.offset)
753
def static_name(self, name):
754
if name in self.static_entry_points:
757
return "_dispatch_stub_%u" % (self.offset)
760
class gl_item_factory:
761
"""Factory to create objects derived from gl_item."""
763
def create_item(self, item_name, element, context):
764
if item_name == "function":
765
return gl_function(element, context)
766
if item_name == "type":
767
return gl_type(element, context)
768
elif item_name == "enum":
769
return gl_enum(element, context)
770
elif item_name == "parameter":
771
return gl_parameter(element, context)
772
elif item_name == "api":
779
def __init__(self, factory):
780
self.functions_by_name = {}
781
self.enums_by_name = {}
782
self.types_by_name = {}
784
self.category_dict = {}
785
self.categories = [{}, {}, {}, {}]
787
self.factory = factory
791
typeexpr.create_initial_types()
795
def process_element(self, doc):
796
element = doc.children
797
while element.type != "element" or element.name != "OpenGLAPI":
798
element = element.next
801
self.process_OpenGLAPI(element)
805
def process_OpenGLAPI(self, element):
806
child = element.children
808
if child.type == "element":
809
if child.name == "category":
810
self.process_category( child )
811
elif child.name == "OpenGLAPI":
812
self.process_OpenGLAPI( child )
819
def process_category(self, cat):
820
cat_name = cat.nsProp( "name", None )
821
cat_number = cat.nsProp( "number", None )
823
[cat_type, key] = classify_category(cat_name, cat_number)
824
self.categories[cat_type][key] = [cat_name, cat_number]
828
if child.type == "element":
829
if child.name == "function":
830
func_name = real_function_name( child )
832
temp_name = child.nsProp( "name", None )
833
self.category_dict[ temp_name ] = [cat_name, cat_number]
835
if self.functions_by_name.has_key( func_name ):
836
func = self.functions_by_name[ func_name ]
837
func.process_element( child )
839
func = self.factory.create_item( "function", child, self )
840
self.functions_by_name[ func_name ] = func
842
if func.offset >= self.next_offset:
843
self.next_offset = func.offset + 1
846
elif child.name == "enum":
847
enum = self.factory.create_item( "enum", child, self )
848
self.enums_by_name[ enum.name ] = enum
849
elif child.name == "type":
850
t = self.factory.create_item( "type", child, self )
851
self.types_by_name[ "GL" + t.name ] = t
859
def functionIterateByCategory(self, cat = None):
860
"""Iterate over functions by category.
862
If cat is None, all known functions are iterated in category
863
order. See classify_category for details of the ordering.
864
Within a category, functions are sorted by name. If cat is
865
not None, then only functions in that category are iterated.
867
lists = [{}, {}, {}, {}]
869
for func in self.functionIterateAll():
870
[cat_name, cat_number] = self.category_dict[func.name]
872
if (cat == None) or (cat == cat_name):
873
[func_cat_type, key] = classify_category(cat_name, cat_number)
875
if not lists[func_cat_type].has_key(key):
876
lists[func_cat_type][key] = {}
878
lists[func_cat_type][key][func.name] = func
882
for func_cat_type in range(0,4):
883
keys = lists[func_cat_type].keys()
887
names = lists[func_cat_type][key].keys()
891
functions.append(lists[func_cat_type][key][name])
893
return functions.__iter__()
896
def functionIterateByOffset(self):
898
for func in self.functions_by_name.itervalues():
899
if func.offset > max_offset:
900
max_offset = func.offset
903
temp = [None for i in range(0, max_offset + 1)]
904
for func in self.functions_by_name.itervalues():
905
if func.offset != -1:
906
temp[ func.offset ] = func
910
for i in range(0, max_offset + 1):
914
return list.__iter__();
917
def functionIterateAll(self):
918
return self.functions_by_name.itervalues()
921
def enumIterateByName(self):
922
keys = self.enums_by_name.keys()
927
list.append( self.enums_by_name[ enum ] )
929
return list.__iter__()
932
def categoryIterate(self):
933
"""Iterate over categories.
935
Iterate over all known categories in the order specified by
936
classify_category. Each iterated value is a tuple of the
937
name and number (which may be None) of the category.
941
for cat_type in range(0,4):
942
keys = self.categories[cat_type].keys()
946
list.append(self.categories[cat_type][key])
948
return list.__iter__()
951
def get_category_for_name( self, name ):
952
if self.category_dict.has_key(name):
953
return self.category_dict[name]
955
return ["<unknown category>", None]
958
def typeIterate(self):
959
return self.types_by_name.itervalues()
962
def find_type( self, type_name ):
963
if type_name in self.types_by_name:
964
return self.types_by_name[ type_name ].type_expr
966
print "Unable to find base type matching \"%s\"." % (type_name)