Go to most recent revision | Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
5564 | serge | 1 | #!/usr/bin/env python |
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 |
||
27 | |||
28 | from decimal import Decimal |
||
29 | import xml.etree.ElementTree as ET |
||
30 | import re, sys, string |
||
31 | import os.path |
||
32 | import typeexpr |
||
33 | import static_data |
||
34 | |||
35 | |||
36 | def parse_GL_API( file_name, factory = None ): |
||
37 | |||
38 | if not factory: |
||
39 | factory = gl_item_factory() |
||
40 | |||
41 | api = factory.create_api() |
||
42 | api.parse_file( file_name ) |
||
43 | |||
44 | # After the XML has been processed, we need to go back and assign |
||
45 | # dispatch offsets to the functions that request that their offsets |
||
46 | # be assigned by the scripts. Typically this means all functions |
||
47 | # that are not part of the ABI. |
||
48 | |||
49 | for func in api.functionIterateByCategory(): |
||
50 | if func.assign_offset: |
||
51 | func.offset = api.next_offset; |
||
52 | api.next_offset += 1 |
||
53 | |||
54 | return api |
||
55 | |||
56 | |||
57 | def is_attr_true( element, name, default = "false" ): |
||
58 | """Read a name value from an element's attributes. |
||
59 | |||
60 | The value read from the attribute list must be either 'true' or |
||
61 | 'false'. If the value is 'false', zero will be returned. If the |
||
62 | value is 'true', non-zero will be returned. An exception will be |
||
63 | raised for any other value.""" |
||
64 | |||
65 | value = element.get( name, default ) |
||
66 | if value == "true": |
||
67 | return 1 |
||
68 | elif value == "false": |
||
69 | return 0 |
||
70 | else: |
||
71 | raise RuntimeError('Invalid value "%s" for boolean "%s".' % (value, name)) |
||
72 | |||
73 | |||
74 | class gl_print_base(object): |
||
75 | """Base class of all API pretty-printers. |
||
76 | |||
77 | In the model-view-controller pattern, this is the view. Any derived |
||
78 | class will want to over-ride the printBody, printRealHader, and |
||
79 | printRealFooter methods. Some derived classes may want to over-ride |
||
80 | printHeader and printFooter, or even Print (though this is unlikely). |
||
81 | """ |
||
82 | |||
83 | def __init__(self): |
||
84 | # Name of the script that is generating the output file. |
||
85 | # Every derived class should set this to the name of its |
||
86 | # source file. |
||
87 | |||
88 | self.name = "a" |
||
89 | |||
90 | |||
91 | # License on the *generated* source file. This may differ |
||
92 | # from the license on the script that is generating the file. |
||
93 | # Every derived class should set this to some reasonable |
||
94 | # value. |
||
95 | # |
||
96 | # See license.py for an example of a reasonable value. |
||
97 | |||
98 | self.license = "The license for this file is unspecified." |
||
99 | |||
100 | |||
101 | # The header_tag is the name of the C preprocessor define |
||
102 | # used to prevent multiple inclusion. Typically only |
||
103 | # generated C header files need this to be set. Setting it |
||
104 | # causes code to be generated automatically in printHeader |
||
105 | # and printFooter. |
||
106 | |||
107 | self.header_tag = None |
||
108 | |||
109 | |||
110 | # List of file-private defines that must be undefined at the |
||
111 | # end of the file. This can be used in header files to define |
||
112 | # names for use in the file, then undefine them at the end of |
||
113 | # the header file. |
||
114 | |||
115 | self.undef_list = [] |
||
116 | return |
||
117 | |||
118 | |||
119 | def Print(self, api): |
||
120 | self.printHeader() |
||
121 | self.printBody(api) |
||
122 | self.printFooter() |
||
123 | return |
||
124 | |||
125 | |||
126 | def printHeader(self): |
||
127 | """Print the header associated with all files and call the printRealHeader method.""" |
||
128 | |||
129 | print '/* DO NOT EDIT - This file generated automatically by %s script */' \ |
||
130 | % (self.name) |
||
131 | print '' |
||
132 | print '/*' |
||
133 | print ' * ' + self.license.replace('\n', '\n * ') |
||
134 | print ' */' |
||
135 | print '' |
||
136 | if self.header_tag: |
||
137 | print '#if !defined( %s )' % (self.header_tag) |
||
138 | print '# define %s' % (self.header_tag) |
||
139 | print '' |
||
140 | self.printRealHeader(); |
||
141 | return |
||
142 | |||
143 | |||
144 | def printFooter(self): |
||
145 | """Print the header associated with all files and call the printRealFooter method.""" |
||
146 | |||
147 | self.printRealFooter() |
||
148 | |||
149 | if self.undef_list: |
||
150 | print '' |
||
151 | for u in self.undef_list: |
||
152 | print "# undef %s" % (u) |
||
153 | |||
154 | if self.header_tag: |
||
155 | print '' |
||
156 | print '#endif /* !defined( %s ) */' % (self.header_tag) |
||
157 | |||
158 | |||
159 | def printRealHeader(self): |
||
160 | """Print the "real" header for the created file. |
||
161 | |||
162 | In the base class, this function is empty. All derived |
||
163 | classes should over-ride this function.""" |
||
164 | return |
||
165 | |||
166 | |||
167 | def printRealFooter(self): |
||
168 | """Print the "real" footer for the created file. |
||
169 | |||
170 | In the base class, this function is empty. All derived |
||
171 | classes should over-ride this function.""" |
||
172 | return |
||
173 | |||
174 | |||
175 | def printPure(self): |
||
176 | """Conditionally define `PURE' function attribute. |
||
177 | |||
178 | Conditionally defines a preprocessor macro `PURE' that wraps |
||
179 | GCC's `pure' function attribute. The conditional code can be |
||
180 | easilly adapted to other compilers that support a similar |
||
181 | feature. |
||
182 | |||
183 | The name is also added to the file's undef_list. |
||
184 | """ |
||
185 | self.undef_list.append("PURE") |
||
186 | print """# if defined(__GNUC__) || (defined(__SUNPRO_C) && (__SUNPRO_C >= 0x590)) |
||
187 | # define PURE __attribute__((pure)) |
||
188 | # else |
||
189 | # define PURE |
||
190 | # endif""" |
||
191 | return |
||
192 | |||
193 | |||
194 | def printFastcall(self): |
||
195 | """Conditionally define `FASTCALL' function attribute. |
||
196 | |||
197 | Conditionally defines a preprocessor macro `FASTCALL' that |
||
198 | wraps GCC's `fastcall' function attribute. The conditional |
||
199 | code can be easilly adapted to other compilers that support a |
||
200 | similar feature. |
||
201 | |||
202 | The name is also added to the file's undef_list. |
||
203 | """ |
||
204 | |||
205 | self.undef_list.append("FASTCALL") |
||
206 | print """# if defined(__i386__) && defined(__GNUC__) && !defined(__CYGWIN__) && !defined(__MINGW32__) |
||
207 | # define FASTCALL __attribute__((fastcall)) |
||
208 | # else |
||
209 | # define FASTCALL |
||
210 | # endif""" |
||
211 | return |
||
212 | |||
213 | |||
214 | def printVisibility(self, S, s): |
||
215 | """Conditionally define visibility function attribute. |
||
216 | |||
217 | Conditionally defines a preprocessor macro name S that wraps |
||
218 | GCC's visibility function attribute. The visibility used is |
||
219 | the parameter s. The conditional code can be easilly adapted |
||
220 | to other compilers that support a similar feature. |
||
221 | |||
222 | The name is also added to the file's undef_list. |
||
223 | """ |
||
224 | |||
225 | self.undef_list.append(S) |
||
226 | print """# if (defined(__GNUC__) && !defined(__CYGWIN__) && !defined(__MINGW32__)) || (defined(__SUNPRO_C) && (__SUNPRO_C >= 0x590) && defined(__ELF__)) |
||
227 | # define %s __attribute__((visibility("%s"))) |
||
228 | # else |
||
229 | # define %s |
||
230 | # endif""" % (S, s, S) |
||
231 | return |
||
232 | |||
233 | |||
234 | def printNoinline(self): |
||
235 | """Conditionally define `NOINLINE' function attribute. |
||
236 | |||
237 | Conditionally defines a preprocessor macro `NOINLINE' that |
||
238 | wraps GCC's `noinline' function attribute. The conditional |
||
239 | code can be easilly adapted to other compilers that support a |
||
240 | similar feature. |
||
241 | |||
242 | The name is also added to the file's undef_list. |
||
243 | """ |
||
244 | |||
245 | self.undef_list.append("NOINLINE") |
||
246 | print """# if defined(__GNUC__) || (defined(__SUNPRO_C) && (__SUNPRO_C >= 0x590)) |
||
247 | # define NOINLINE __attribute__((noinline)) |
||
248 | # else |
||
249 | # define NOINLINE |
||
250 | # endif""" |
||
251 | return |
||
252 | |||
253 | |||
254 | def real_function_name(element): |
||
255 | name = element.get( "name" ) |
||
256 | alias = element.get( "alias" ) |
||
257 | |||
258 | if alias: |
||
259 | return alias |
||
260 | else: |
||
261 | return name |
||
262 | |||
263 | |||
264 | def real_category_name(c): |
||
265 | if re.compile("[1-9][0-9]*[.][0-9]+").match(c): |
||
266 | return "GL_VERSION_" + c.replace(".", "_") |
||
267 | else: |
||
268 | return c |
||
269 | |||
270 | |||
271 | def classify_category(name, number): |
||
272 | """Based on the category name and number, select a numerical class for it. |
||
273 | |||
274 | Categories are divided into four classes numbered 0 through 3. The |
||
275 | classes are: |
||
276 | |||
277 | 0. Core GL versions, sorted by version number. |
||
278 | 1. ARB extensions, sorted by extension number. |
||
279 | 2. Non-ARB extensions, sorted by extension number. |
||
280 | 3. Un-numbered extensions, sorted by extension name. |
||
281 | """ |
||
282 | |||
283 | try: |
||
284 | core_version = float(name) |
||
285 | except Exception,e: |
||
286 | core_version = 0.0 |
||
287 | |||
288 | if core_version > 0.0: |
||
289 | cat_type = 0 |
||
290 | key = name |
||
291 | elif name.startswith("GL_ARB_") or name.startswith("GLX_ARB_") or name.startswith("WGL_ARB_"): |
||
292 | cat_type = 1 |
||
293 | key = int(number) |
||
294 | else: |
||
295 | if number != None: |
||
296 | cat_type = 2 |
||
297 | key = int(number) |
||
298 | else: |
||
299 | cat_type = 3 |
||
300 | key = name |
||
301 | |||
302 | |||
303 | return [cat_type, key] |
||
304 | |||
305 | |||
306 | def create_parameter_string(parameters, include_names): |
||
307 | """Create a parameter string from a list of gl_parameters.""" |
||
308 | |||
309 | list = [] |
||
310 | for p in parameters: |
||
311 | if p.is_padding: |
||
312 | continue |
||
313 | |||
314 | if include_names: |
||
315 | list.append( p.string() ) |
||
316 | else: |
||
317 | list.append( p.type_string() ) |
||
318 | |||
319 | if len(list) == 0: list = ["void"] |
||
320 | |||
321 | return string.join(list, ", ") |
||
322 | |||
323 | |||
324 | class gl_item(object): |
||
325 | def __init__(self, element, context, category): |
||
326 | self.context = context |
||
327 | self.name = element.get( "name" ) |
||
328 | self.category = real_category_name( category ) |
||
329 | |||
330 | return |
||
331 | |||
332 | |||
333 | class gl_type( gl_item ): |
||
334 | def __init__(self, element, context, category): |
||
335 | gl_item.__init__(self, element, context, category) |
||
336 | self.size = int( element.get( "size" ), 0 ) |
||
337 | |||
338 | te = typeexpr.type_expression( None ) |
||
339 | tn = typeexpr.type_node() |
||
340 | tn.size = int( element.get( "size" ), 0 ) |
||
341 | tn.integer = not is_attr_true( element, "float" ) |
||
342 | tn.unsigned = is_attr_true( element, "unsigned" ) |
||
343 | tn.pointer = is_attr_true( element, "pointer" ) |
||
344 | tn.name = "GL" + self.name |
||
345 | te.set_base_type_node( tn ) |
||
346 | |||
347 | self.type_expr = te |
||
348 | return |
||
349 | |||
350 | |||
351 | def get_type_expression(self): |
||
352 | return self.type_expr |
||
353 | |||
354 | |||
355 | class gl_enum( gl_item ): |
||
356 | def __init__(self, element, context, category): |
||
357 | gl_item.__init__(self, element, context, category) |
||
358 | self.value = int( element.get( "value" ), 0 ) |
||
359 | |||
360 | temp = element.get( "count" ) |
||
361 | if not temp or temp == "?": |
||
362 | self.default_count = -1 |
||
363 | else: |
||
364 | try: |
||
365 | c = int(temp) |
||
366 | except Exception,e: |
||
367 | raise RuntimeError('Invalid count value "%s" for enum "%s" in function "%s" when an integer was expected.' % (temp, self.name, n)) |
||
368 | |||
369 | self.default_count = c |
||
370 | |||
371 | return |
||
372 | |||
373 | |||
374 | def priority(self): |
||
375 | """Calculate a 'priority' for this enum name. |
||
376 | |||
377 | When an enum is looked up by number, there may be many |
||
378 | possible names, but only one is the 'prefered' name. The |
||
379 | priority is used to select which name is the 'best'. |
||
380 | |||
381 | Highest precedence is given to core GL name. ARB extension |
||
382 | names have the next highest, followed by EXT extension names. |
||
383 | Vendor extension names are the lowest. |
||
384 | """ |
||
385 | |||
386 | if self.name.endswith( "_BIT" ): |
||
387 | bias = 1 |
||
388 | else: |
||
389 | bias = 0 |
||
390 | |||
391 | if self.category.startswith( "GL_VERSION_" ): |
||
392 | priority = 0 |
||
393 | elif self.category.startswith( "GL_ARB_" ): |
||
394 | priority = 2 |
||
395 | elif self.category.startswith( "GL_EXT_" ): |
||
396 | priority = 4 |
||
397 | else: |
||
398 | priority = 6 |
||
399 | |||
400 | return priority + bias |
||
401 | |||
402 | |||
403 | |||
404 | class gl_parameter(object): |
||
405 | def __init__(self, element, context): |
||
406 | self.name = element.get( "name" ) |
||
407 | |||
408 | ts = element.get( "type" ) |
||
409 | self.type_expr = typeexpr.type_expression( ts, context ) |
||
410 | |||
411 | temp = element.get( "variable_param" ) |
||
412 | if temp: |
||
413 | self.count_parameter_list = temp.split( ' ' ) |
||
414 | else: |
||
415 | self.count_parameter_list = [] |
||
416 | |||
417 | # The count tag can be either a numeric string or the name of |
||
418 | # a variable. If it is the name of a variable, the int(c) |
||
419 | # statement will throw an exception, and the except block will |
||
420 | # take over. |
||
421 | |||
422 | c = element.get( "count" ) |
||
423 | try: |
||
424 | count = int(c) |
||
425 | self.count = count |
||
426 | self.counter = None |
||
427 | except Exception,e: |
||
428 | count = 1 |
||
429 | self.count = 0 |
||
430 | self.counter = c |
||
431 | |||
432 | self.count_scale = int(element.get( "count_scale", "1" )) |
||
433 | |||
434 | elements = (count * self.count_scale) |
||
435 | if elements == 1: |
||
436 | elements = 0 |
||
437 | |||
438 | #if ts == "GLdouble": |
||
439 | # print '/* stack size -> %s = %u (before)*/' % (self.name, self.type_expr.get_stack_size()) |
||
440 | # print '/* # elements = %u */' % (elements) |
||
441 | self.type_expr.set_elements( elements ) |
||
442 | #if ts == "GLdouble": |
||
443 | # print '/* stack size -> %s = %u (after) */' % (self.name, self.type_expr.get_stack_size()) |
||
444 | |||
445 | self.is_client_only = is_attr_true( element, 'client_only' ) |
||
446 | self.is_counter = is_attr_true( element, 'counter' ) |
||
447 | self.is_output = is_attr_true( element, 'output' ) |
||
448 | |||
449 | |||
450 | # Pixel data has special parameters. |
||
451 | |||
452 | self.width = element.get('img_width') |
||
453 | self.height = element.get('img_height') |
||
454 | self.depth = element.get('img_depth') |
||
455 | self.extent = element.get('img_extent') |
||
456 | |||
457 | self.img_xoff = element.get('img_xoff') |
||
458 | self.img_yoff = element.get('img_yoff') |
||
459 | self.img_zoff = element.get('img_zoff') |
||
460 | self.img_woff = element.get('img_woff') |
||
461 | |||
462 | self.img_format = element.get('img_format') |
||
463 | self.img_type = element.get('img_type') |
||
464 | self.img_target = element.get('img_target') |
||
465 | |||
466 | self.img_pad_dimensions = is_attr_true( element, 'img_pad_dimensions' ) |
||
467 | self.img_null_flag = is_attr_true( element, 'img_null_flag' ) |
||
468 | self.img_send_null = is_attr_true( element, 'img_send_null' ) |
||
469 | |||
470 | self.is_padding = is_attr_true( element, 'padding' ) |
||
471 | return |
||
472 | |||
473 | |||
474 | def compatible(self, other): |
||
475 | return 1 |
||
476 | |||
477 | |||
478 | def is_array(self): |
||
479 | return self.is_pointer() |
||
480 | |||
481 | |||
482 | def is_pointer(self): |
||
483 | return self.type_expr.is_pointer() |
||
484 | |||
485 | |||
486 | def is_image(self): |
||
487 | if self.width: |
||
488 | return 1 |
||
489 | else: |
||
490 | return 0 |
||
491 | |||
492 | |||
493 | def is_variable_length(self): |
||
494 | return len(self.count_parameter_list) or self.counter |
||
495 | |||
496 | |||
497 | def is_64_bit(self): |
||
498 | count = self.type_expr.get_element_count() |
||
499 | if count: |
||
500 | if (self.size() / count) == 8: |
||
501 | return 1 |
||
502 | else: |
||
503 | if self.size() == 8: |
||
504 | return 1 |
||
505 | |||
506 | return 0 |
||
507 | |||
508 | |||
509 | def string(self): |
||
510 | return self.type_expr.original_string + " " + self.name |
||
511 | |||
512 | |||
513 | def type_string(self): |
||
514 | return self.type_expr.original_string |
||
515 | |||
516 | |||
517 | def get_base_type_string(self): |
||
518 | return self.type_expr.get_base_name() |
||
519 | |||
520 | |||
521 | def get_dimensions(self): |
||
522 | if not self.width: |
||
523 | return [ 0, "0", "0", "0", "0" ] |
||
524 | |||
525 | dim = 1 |
||
526 | w = self.width |
||
527 | h = "1" |
||
528 | d = "1" |
||
529 | e = "1" |
||
530 | |||
531 | if self.height: |
||
532 | dim = 2 |
||
533 | h = self.height |
||
534 | |||
535 | if self.depth: |
||
536 | dim = 3 |
||
537 | d = self.depth |
||
538 | |||
539 | if self.extent: |
||
540 | dim = 4 |
||
541 | e = self.extent |
||
542 | |||
543 | return [ dim, w, h, d, e ] |
||
544 | |||
545 | |||
546 | def get_stack_size(self): |
||
547 | return self.type_expr.get_stack_size() |
||
548 | |||
549 | |||
550 | def size(self): |
||
551 | if self.is_image(): |
||
552 | return 0 |
||
553 | else: |
||
554 | return self.type_expr.get_element_size() |
||
555 | |||
556 | |||
557 | def get_element_count(self): |
||
558 | c = self.type_expr.get_element_count() |
||
559 | if c == 0: |
||
560 | return 1 |
||
561 | |||
562 | return c |
||
563 | |||
564 | |||
565 | def size_string(self, use_parens = 1): |
||
566 | s = self.size() |
||
567 | if self.counter or self.count_parameter_list: |
||
568 | list = [ "compsize" ] |
||
569 | |||
570 | if self.counter and self.count_parameter_list: |
||
571 | list.append( self.counter ) |
||
572 | elif self.counter: |
||
573 | list = [ self.counter ] |
||
574 | |||
575 | if s > 1: |
||
576 | list.append( str(s) ) |
||
577 | |||
578 | if len(list) > 1 and use_parens : |
||
579 | return "(%s)" % (string.join(list, " * ")) |
||
580 | else: |
||
581 | return string.join(list, " * ") |
||
582 | |||
583 | elif self.is_image(): |
||
584 | return "compsize" |
||
585 | else: |
||
586 | return str(s) |
||
587 | |||
588 | |||
589 | def format_string(self): |
||
590 | if self.type_expr.original_string == "GLenum": |
||
591 | return "0x%x" |
||
592 | else: |
||
593 | return self.type_expr.format_string() |
||
594 | |||
595 | |||
596 | class gl_function( gl_item ): |
||
597 | def __init__(self, element, context): |
||
598 | self.context = context |
||
599 | self.name = None |
||
600 | |||
601 | self.entry_points = [] |
||
602 | self.return_type = "void" |
||
603 | self.parameters = [] |
||
604 | self.offset = -1 |
||
605 | self.initialized = 0 |
||
606 | self.images = [] |
||
607 | self.exec_flavor = 'mesa' |
||
608 | self.desktop = True |
||
609 | self.deprecated = None |
||
610 | |||
611 | # self.entry_point_api_map[name][api] is a decimal value |
||
612 | # indicating the earliest version of the given API in which |
||
613 | # each entry point exists. Every entry point is included in |
||
614 | # the first level of the map; the second level of the map only |
||
615 | # lists APIs which contain the entry point in at least one |
||
616 | # version. For example, |
||
617 | # self.entry_point_api_map['ClipPlanex'] == { 'es1': |
||
618 | # Decimal('1.1') }. |
||
619 | self.entry_point_api_map = {} |
||
620 | |||
621 | # self.api_map[api] is a decimal value indicating the earliest |
||
622 | # version of the given API in which ANY alias for the function |
||
623 | # exists. The map only lists APIs which contain the function |
||
624 | # in at least one version. For example, for the ClipPlanex |
||
625 | # function, self.entry_point_api_map == { 'es1': |
||
626 | # Decimal('1.1') }. |
||
627 | self.api_map = {} |
||
628 | |||
629 | self.assign_offset = False |
||
630 | |||
631 | self.static_entry_points = [] |
||
632 | |||
633 | # Track the parameter string (for the function prototype) |
||
634 | # for each entry-point. This is done because some functions |
||
635 | # change their prototype slightly when promoted from extension |
||
636 | # to ARB extension to core. glTexImage3DEXT and glTexImage3D |
||
637 | # are good examples of this. Scripts that need to generate |
||
638 | # code for these differing aliases need to real prototype |
||
639 | # for each entry-point. Otherwise, they may generate code |
||
640 | # that won't compile. |
||
641 | |||
642 | self.entry_point_parameters = {} |
||
643 | |||
644 | self.process_element( element ) |
||
645 | |||
646 | return |
||
647 | |||
648 | |||
649 | def process_element(self, element): |
||
650 | name = element.get( "name" ) |
||
651 | alias = element.get( "alias" ) |
||
652 | |||
653 | if name in static_data.functions: |
||
654 | self.static_entry_points.append(name) |
||
655 | |||
656 | self.entry_points.append( name ) |
||
657 | |||
658 | self.entry_point_api_map[name] = {} |
||
659 | for api in ('es1', 'es2'): |
||
660 | version_str = element.get(api, 'none') |
||
661 | assert version_str is not None |
||
662 | if version_str != 'none': |
||
663 | version_decimal = Decimal(version_str) |
||
664 | self.entry_point_api_map[name][api] = version_decimal |
||
665 | if api not in self.api_map or \ |
||
666 | version_decimal < self.api_map[api]: |
||
667 | self.api_map[api] = version_decimal |
||
668 | |||
669 | exec_flavor = element.get('exec') |
||
670 | if exec_flavor: |
||
671 | self.exec_flavor = exec_flavor |
||
672 | |||
673 | deprecated = element.get('deprecated', 'none') |
||
674 | if deprecated != 'none': |
||
675 | self.deprecated = Decimal(deprecated) |
||
676 | |||
677 | if not is_attr_true(element, 'desktop', 'true'): |
||
678 | self.desktop = False |
||
679 | |||
680 | if alias: |
||
681 | true_name = alias |
||
682 | else: |
||
683 | true_name = name |
||
684 | |||
685 | # Only try to set the offset when a non-alias entry-point |
||
686 | # is being processed. |
||
687 | |||
688 | if name in static_data.offsets: |
||
689 | self.offset = static_data.offsets[name] |
||
690 | else: |
||
691 | self.offset = -1 |
||
692 | self.assign_offset = self.exec_flavor != "skip" or name in static_data.unused_functions |
||
693 | |||
694 | if not self.name: |
||
695 | self.name = true_name |
||
696 | elif self.name != true_name: |
||
697 | raise RuntimeError("Function true name redefined. Was %s, now %s." % (self.name, true_name)) |
||
698 | |||
699 | |||
700 | # There are two possible cases. The first time an entry-point |
||
701 | # with data is seen, self.initialized will be 0. On that |
||
702 | # pass, we just fill in the data. The next time an |
||
703 | # entry-point with data is seen, self.initialized will be 1. |
||
704 | # On that pass we have to make that the new values match the |
||
705 | # valuse from the previous entry-point. |
||
706 | |||
707 | parameters = [] |
||
708 | return_type = "void" |
||
709 | for child in element.getchildren(): |
||
710 | if child.tag == "return": |
||
711 | return_type = child.get( "type", "void" ) |
||
712 | elif child.tag == "param": |
||
713 | param = self.context.factory.create_parameter(child, self.context) |
||
714 | parameters.append( param ) |
||
715 | |||
716 | |||
717 | if self.initialized: |
||
718 | if self.return_type != return_type: |
||
719 | raise RuntimeError( "Return type changed in %s. Was %s, now %s." % (name, self.return_type, return_type)) |
||
720 | |||
721 | if len(parameters) != len(self.parameters): |
||
722 | raise RuntimeError( "Parameter count mismatch in %s. Was %d, now %d." % (name, len(self.parameters), len(parameters))) |
||
723 | |||
724 | for j in range(0, len(parameters)): |
||
725 | p1 = parameters[j] |
||
726 | p2 = self.parameters[j] |
||
727 | if not p1.compatible( p2 ): |
||
728 | 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)) |
||
729 | |||
730 | |||
731 | if true_name == name or not self.initialized: |
||
732 | self.return_type = return_type |
||
733 | self.parameters = parameters |
||
734 | |||
735 | for param in self.parameters: |
||
736 | if param.is_image(): |
||
737 | self.images.append( param ) |
||
738 | |||
739 | if element.getchildren(): |
||
740 | self.initialized = 1 |
||
741 | self.entry_point_parameters[name] = parameters |
||
742 | else: |
||
743 | self.entry_point_parameters[name] = [] |
||
744 | |||
745 | return |
||
746 | |||
747 | def filter_entry_points(self, entry_point_list): |
||
748 | """Filter out entry points not in entry_point_list.""" |
||
749 | if not self.initialized: |
||
750 | raise RuntimeError('%s is not initialized yet' % self.name) |
||
751 | |||
752 | entry_points = [] |
||
753 | for ent in self.entry_points: |
||
754 | if ent not in entry_point_list: |
||
755 | if ent in self.static_entry_points: |
||
756 | self.static_entry_points.remove(ent) |
||
757 | self.entry_point_parameters.pop(ent) |
||
758 | else: |
||
759 | entry_points.append(ent) |
||
760 | |||
761 | if not entry_points: |
||
762 | raise RuntimeError('%s has no entry point after filtering' % self.name) |
||
763 | |||
764 | self.entry_points = entry_points |
||
765 | if self.name not in entry_points: |
||
766 | # use the first remaining entry point |
||
767 | self.name = entry_points[0] |
||
768 | self.parameters = self.entry_point_parameters[entry_points[0]] |
||
769 | |||
770 | def get_images(self): |
||
771 | """Return potentially empty list of input images.""" |
||
772 | return self.images |
||
773 | |||
774 | |||
775 | def parameterIterator(self, name = None): |
||
776 | if name is not None: |
||
777 | return self.entry_point_parameters[name].__iter__(); |
||
778 | else: |
||
779 | return self.parameters.__iter__(); |
||
780 | |||
781 | |||
782 | def get_parameter_string(self, entrypoint = None): |
||
783 | if entrypoint: |
||
784 | params = self.entry_point_parameters[ entrypoint ] |
||
785 | else: |
||
786 | params = self.parameters |
||
787 | |||
788 | return create_parameter_string( params, 1 ) |
||
789 | |||
790 | def get_called_parameter_string(self): |
||
791 | p_string = "" |
||
792 | comma = "" |
||
793 | |||
794 | for p in self.parameterIterator(): |
||
795 | if p.is_padding: |
||
796 | continue |
||
797 | p_string = p_string + comma + p.name |
||
798 | comma = ", " |
||
799 | |||
800 | return p_string |
||
801 | |||
802 | |||
803 | def is_abi(self): |
||
804 | return (self.offset >= 0 and not self.assign_offset) |
||
805 | |||
806 | def is_static_entry_point(self, name): |
||
807 | return name in self.static_entry_points |
||
808 | |||
809 | def dispatch_name(self): |
||
810 | if self.name in self.static_entry_points: |
||
811 | return self.name |
||
812 | else: |
||
813 | return "_dispatch_stub_%u" % (self.offset) |
||
814 | |||
815 | def static_name(self, name): |
||
816 | if name in self.static_entry_points: |
||
817 | return name |
||
818 | else: |
||
819 | return "_dispatch_stub_%u" % (self.offset) |
||
820 | |||
821 | def entry_points_for_api_version(self, api, version = None): |
||
822 | """Return a list of the entry point names for this function |
||
823 | which are supported in the given API (and optionally, version). |
||
824 | |||
825 | Use the decimal.Decimal type to precisely express non-integer |
||
826 | versions. |
||
827 | """ |
||
828 | result = [] |
||
829 | for entry_point, api_to_ver in self.entry_point_api_map.iteritems(): |
||
830 | if api not in api_to_ver: |
||
831 | continue |
||
832 | if version is not None and version < api_to_ver[api]: |
||
833 | continue |
||
834 | result.append(entry_point) |
||
835 | return result |
||
836 | |||
837 | |||
838 | class gl_item_factory(object): |
||
839 | """Factory to create objects derived from gl_item.""" |
||
840 | |||
841 | def create_function(self, element, context): |
||
842 | return gl_function(element, context) |
||
843 | |||
844 | def create_type(self, element, context, category): |
||
845 | return gl_type(element, context, category) |
||
846 | |||
847 | def create_enum(self, element, context, category): |
||
848 | return gl_enum(element, context, category) |
||
849 | |||
850 | def create_parameter(self, element, context): |
||
851 | return gl_parameter(element, context) |
||
852 | |||
853 | def create_api(self): |
||
854 | return gl_api(self) |
||
855 | |||
856 | |||
857 | class gl_api(object): |
||
858 | def __init__(self, factory): |
||
859 | self.functions_by_name = {} |
||
860 | self.enums_by_name = {} |
||
861 | self.types_by_name = {} |
||
862 | |||
863 | self.category_dict = {} |
||
864 | self.categories = [{}, {}, {}, {}] |
||
865 | |||
866 | self.factory = factory |
||
867 | |||
868 | self.next_offset = 0 |
||
869 | |||
870 | typeexpr.create_initial_types() |
||
871 | return |
||
872 | |||
873 | def filter_functions(self, entry_point_list): |
||
874 | """Filter out entry points not in entry_point_list.""" |
||
875 | functions_by_name = {} |
||
876 | for func in self.functions_by_name.itervalues(): |
||
877 | entry_points = [ent for ent in func.entry_points if ent in entry_point_list] |
||
878 | if entry_points: |
||
879 | func.filter_entry_points(entry_points) |
||
880 | functions_by_name[func.name] = func |
||
881 | |||
882 | self.functions_by_name = functions_by_name |
||
883 | |||
884 | def filter_functions_by_api(self, api, version = None): |
||
885 | """Filter out entry points not in the given API (or |
||
886 | optionally, not in the given version of the given API). |
||
887 | """ |
||
888 | functions_by_name = {} |
||
889 | for func in self.functions_by_name.itervalues(): |
||
890 | entry_points = func.entry_points_for_api_version(api, version) |
||
891 | if entry_points: |
||
892 | func.filter_entry_points(entry_points) |
||
893 | functions_by_name[func.name] = func |
||
894 | |||
895 | self.functions_by_name = functions_by_name |
||
896 | |||
897 | |||
898 | def parse_file(self, file_name): |
||
899 | doc = ET.parse( file_name ) |
||
900 | self.process_element(file_name, doc) |
||
901 | |||
902 | |||
903 | def process_element(self, file_name, doc): |
||
904 | element = doc.getroot() |
||
905 | if element.tag == "OpenGLAPI": |
||
906 | self.process_OpenGLAPI(file_name, element) |
||
907 | return |
||
908 | |||
909 | |||
910 | def process_OpenGLAPI(self, file_name, element): |
||
911 | for child in element.getchildren(): |
||
912 | if child.tag == "category": |
||
913 | self.process_category( child ) |
||
914 | elif child.tag == "OpenGLAPI": |
||
915 | self.process_OpenGLAPI( file_name, child ) |
||
916 | elif child.tag == '{http://www.w3.org/2001/XInclude}include': |
||
917 | href = child.get('href') |
||
918 | href = os.path.join(os.path.dirname(file_name), href) |
||
919 | self.parse_file(href) |
||
920 | |||
921 | return |
||
922 | |||
923 | |||
924 | def process_category(self, cat): |
||
925 | cat_name = cat.get( "name" ) |
||
926 | cat_number = cat.get( "number" ) |
||
927 | |||
928 | [cat_type, key] = classify_category(cat_name, cat_number) |
||
929 | self.categories[cat_type][key] = [cat_name, cat_number] |
||
930 | |||
931 | for child in cat.getchildren(): |
||
932 | if child.tag == "function": |
||
933 | func_name = real_function_name( child ) |
||
934 | |||
935 | temp_name = child.get( "name" ) |
||
936 | self.category_dict[ temp_name ] = [cat_name, cat_number] |
||
937 | |||
938 | if self.functions_by_name.has_key( func_name ): |
||
939 | func = self.functions_by_name[ func_name ] |
||
940 | func.process_element( child ) |
||
941 | else: |
||
942 | func = self.factory.create_function( child, self ) |
||
943 | self.functions_by_name[ func_name ] = func |
||
944 | |||
945 | if func.offset >= self.next_offset: |
||
946 | self.next_offset = func.offset + 1 |
||
947 | |||
948 | |||
949 | elif child.tag == "enum": |
||
950 | enum = self.factory.create_enum( child, self, cat_name ) |
||
951 | self.enums_by_name[ enum.name ] = enum |
||
952 | elif child.tag == "type": |
||
953 | t = self.factory.create_type( child, self, cat_name ) |
||
954 | self.types_by_name[ "GL" + t.name ] = t |
||
955 | |||
956 | return |
||
957 | |||
958 | |||
959 | def functionIterateByCategory(self, cat = None): |
||
960 | """Iterate over functions by category. |
||
961 | |||
962 | If cat is None, all known functions are iterated in category |
||
963 | order. See classify_category for details of the ordering. |
||
964 | Within a category, functions are sorted by name. If cat is |
||
965 | not None, then only functions in that category are iterated. |
||
966 | """ |
||
967 | lists = [{}, {}, {}, {}] |
||
968 | |||
969 | for func in self.functionIterateAll(): |
||
970 | [cat_name, cat_number] = self.category_dict[func.name] |
||
971 | |||
972 | if (cat == None) or (cat == cat_name): |
||
973 | [func_cat_type, key] = classify_category(cat_name, cat_number) |
||
974 | |||
975 | if not lists[func_cat_type].has_key(key): |
||
976 | lists[func_cat_type][key] = {} |
||
977 | |||
978 | lists[func_cat_type][key][func.name] = func |
||
979 | |||
980 | |||
981 | functions = [] |
||
982 | for func_cat_type in range(0,4): |
||
983 | keys = lists[func_cat_type].keys() |
||
984 | keys.sort() |
||
985 | |||
986 | for key in keys: |
||
987 | names = lists[func_cat_type][key].keys() |
||
988 | names.sort() |
||
989 | |||
990 | for name in names: |
||
991 | functions.append(lists[func_cat_type][key][name]) |
||
992 | |||
993 | return functions.__iter__() |
||
994 | |||
995 | |||
996 | def functionIterateByOffset(self): |
||
997 | max_offset = -1 |
||
998 | for func in self.functions_by_name.itervalues(): |
||
999 | if func.offset > max_offset: |
||
1000 | max_offset = func.offset |
||
1001 | |||
1002 | |||
1003 | temp = [None for i in range(0, max_offset + 1)] |
||
1004 | for func in self.functions_by_name.itervalues(): |
||
1005 | if func.offset != -1: |
||
1006 | temp[ func.offset ] = func |
||
1007 | |||
1008 | |||
1009 | list = [] |
||
1010 | for i in range(0, max_offset + 1): |
||
1011 | if temp[i]: |
||
1012 | list.append(temp[i]) |
||
1013 | |||
1014 | return list.__iter__(); |
||
1015 | |||
1016 | |||
1017 | def functionIterateAll(self): |
||
1018 | return self.functions_by_name.itervalues() |
||
1019 | |||
1020 | |||
1021 | def enumIterateByName(self): |
||
1022 | keys = self.enums_by_name.keys() |
||
1023 | keys.sort() |
||
1024 | |||
1025 | list = [] |
||
1026 | for enum in keys: |
||
1027 | list.append( self.enums_by_name[ enum ] ) |
||
1028 | |||
1029 | return list.__iter__() |
||
1030 | |||
1031 | |||
1032 | def categoryIterate(self): |
||
1033 | """Iterate over categories. |
||
1034 | |||
1035 | Iterate over all known categories in the order specified by |
||
1036 | classify_category. Each iterated value is a tuple of the |
||
1037 | name and number (which may be None) of the category. |
||
1038 | """ |
||
1039 | |||
1040 | list = [] |
||
1041 | for cat_type in range(0,4): |
||
1042 | keys = self.categories[cat_type].keys() |
||
1043 | keys.sort() |
||
1044 | |||
1045 | for key in keys: |
||
1046 | list.append(self.categories[cat_type][key]) |
||
1047 | |||
1048 | return list.__iter__() |
||
1049 | |||
1050 | |||
1051 | def get_category_for_name( self, name ): |
||
1052 | if self.category_dict.has_key(name): |
||
1053 | return self.category_dict[name] |
||
1054 | else: |
||
1055 | return [" |
||
1056 | |||
1057 | |||
1058 | def typeIterate(self): |
||
1059 | return self.types_by_name.itervalues() |
||
1060 | |||
1061 | |||
1062 | def find_type( self, type_name ): |
||
1063 | if type_name in self.types_by_name: |
||
1064 | return self.types_by_name[ type_name ].type_expr |
||
1065 | else: |
||
1066 | print "Unable to find base type matching \"%s\"." % (type_name) |
||
1067 | return None>> |