[Piglit] [PATCH 2/4] Generate piglit-dispatch from gl.xml.

Fabian Bieler fabianbieler at fastmail.fm
Sat Feb 22 01:02:07 PST 2014


The .spec files which were previously used are no longer maintained.
Khronos advises to migrate to the XML Registry.

Note: This breaks the arb_multisample-pushpop test as it expects
GL_ALL_ATTRIB_BITS not to include GL_MULTISAMPLE_BIT as the extension spec
mandates. gl.xml defines GL_ALL_ATTRIB_BITS to include GL_MULTISAMPLE_BIT
as GL 1.3 and later specs say.

Signed-off-by: Fabian Bieler <fabianbieler at fastmail.fm>
---
 CMakeLists.txt               |   1 -
 cmake/piglit_dispatch.cmake  |   2 +-
 cmake/piglit_glapi.cmake     |  45 ----
 glapi/parse_glspec.py        | 555 -------------------------------------------
 tests/util/gen_dispatch.py   | 234 +++++++++++-------
 tests/util/piglit-dispatch.h |   1 +
 6 files changed, 155 insertions(+), 683 deletions(-)
 delete mode 100644 cmake/piglit_glapi.cmake
 delete mode 100644 glapi/parse_glspec.py

diff --git a/CMakeLists.txt b/CMakeLists.txt
index bef9c35..68a19b8 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -376,7 +376,6 @@ configure_file(
 )
 
 include(cmake/piglit_util.cmake)
-include(cmake/piglit_glapi.cmake)
 include(cmake/piglit_dispatch.cmake)
 
 include_directories(src)
diff --git a/cmake/piglit_dispatch.cmake b/cmake/piglit_dispatch.cmake
index 0b0a2eb..78e9782 100644
--- a/cmake/piglit_dispatch.cmake
+++ b/cmake/piglit_dispatch.cmake
@@ -30,7 +30,7 @@ set(piglit_dispatch_gen_outputs
 
 set(piglit_dispatch_gen_inputs
 	${CMAKE_SOURCE_DIR}/tests/util/gen_dispatch.py
-	${CMAKE_BINARY_DIR}/glapi/glapi.json
+	${CMAKE_SOURCE_DIR}/glapi/gl.xml
 	)
 
 add_custom_command(
diff --git a/cmake/piglit_glapi.cmake b/cmake/piglit_glapi.cmake
deleted file mode 100644
index 59d4d42..0000000
--- a/cmake/piglit_glapi.cmake
+++ /dev/null
@@ -1,45 +0,0 @@
-# Copyright 2012 Intel Corporation
-#
-# Permission is hereby granted, free of charge, to any person obtaining a
-# copy of this software and associated documentation files (the "Software"),
-# to deal in the Software without restriction, including without limitation
-# the rights to use, copy, modify, merge, publish, distribute, sublicense,
-# and/or sell copies of the Software, and to permit persons to whom the
-# Software is furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice (including the next
-# paragraph) shall be included in all copies or substantial portions of the
-# Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
-# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
-# IN THE SOFTWARE.
-
-# Note: we're outputting the generated file to a subdirectory of
-# ${CMAKE_SOURCE_DIR} so that we can check it back in to source
-# control.
-set(piglit_glapi_src_dir ${CMAKE_SOURCE_DIR}/glapi)
-
-file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/glapi)
-
-set(piglit_glapi_output ${CMAKE_BINARY_DIR}/glapi/glapi.json)
-
-set(piglit_glapi_inputs
-	${piglit_glapi_src_dir}/parse_glspec.py
-	${piglit_glapi_src_dir}/gl.tm
-	${piglit_glapi_src_dir}/gl.spec
-	${piglit_glapi_src_dir}/enum.spec
-	${piglit_glapi_src_dir}/enumext.spec
-	${piglit_glapi_src_dir}/GLES3/gl3.h
-	${piglit_glapi_src_dir}/GLES2/gl2ext.h
-	)
-
-add_custom_command(
-	OUTPUT ${piglit_glapi_output}
-	DEPENDS ${piglit_glapi_inputs}
-	COMMAND ${python} ${piglit_glapi_inputs} ${piglit_glapi_output}
-	)
diff --git a/glapi/parse_glspec.py b/glapi/parse_glspec.py
deleted file mode 100644
index bab4996..0000000
--- a/glapi/parse_glspec.py
+++ /dev/null
@@ -1,555 +0,0 @@
-# Copyright 2012 Intel Corporation
-#
-# Permission is hereby granted, free of charge, to any person obtaining a
-# copy of this software and associated documentation files (the "Software"),
-# to deal in the Software without restriction, including without limitation
-# the rights to use, copy, modify, merge, publish, distribute, sublicense,
-# and/or sell copies of the Software, and to permit persons to whom the
-# Software is furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice (including the next
-# paragraph) shall be included in all copies or substantial portions of the
-# Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
-# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
-# IN THE SOFTWARE.
-
-# This script generates a JSON description of the GL API based on
-# source files published by www.opengl.org.
-#
-# The source file "gl.tm" is a CSV file mapping from abstract type
-# names (in column 0) to C types (in column 3).  All other columns are
-# ignored.
-#
-#
-# The source file "gl.spec" consists of a record for each API
-# function, which looks like this:
-#
-# # Function name (excluding the "gl" prefix), followed by the names
-# # of all function parameters:
-# GetVertexAttribdvARB(index, pname, params)
-#
-#         # Property/value pairs follow.  Order is irrelevant.
-#
-#         # "return" specifies the return type (as a reference to
-#         # column 0 of gl.tm).
-#         return          void
-#
-#         # "param" specifies the type of a single function parameter
-#         # (as a reference to column 0 of gl.tm).  In addition, it
-#         # specifies whether the parameter is passed as an input
-#         # value, as an input or output array, or an input or output
-#         # reference.  Input arrays and references get translated
-#         # into const pointers, and Output arrays and references get
-#         # translated into non-const pointers.  Note that for arrays,
-#         # the size of the array appears in brackets after the word
-#         # "array".  This value is ignored.
-#         param           index           UInt32 in value
-#         param           pname           VertexAttribPropertyARB in value
-#         param           params          Float64 out array [4]
-#
-#         # "category" specifies which extension (or GL version) this
-#         # function was introduced in.  For extensions, the category
-#         # name is the extension name (without the "GL_" prefix).
-#         # For GL versions, the category name looks like
-#         # e.g. "VERSION_1_0" or "VERSION_1_0_DEPRECATED" (for
-#         # deprecated features).
-#         category        ARB_vertex_program
-#
-#         # "alias" specifies the name of a function that is
-#         # behaviorally indistinguishable from this function (if
-#         # any).
-#         alias           GetVertexAttribdv
-#
-#         # Other property/value pairs are ignored.
-#
-# # Lines in any other format are ignored.
-#
-#
-# The source file "enumext.spec" consists of lines of the form <enum
-# name> = <value>, e.g.:
-#
-#         FRONT                                      = 0x0404
-#
-# The enum name is the name without the "GL_" prefix.
-#
-# It is also permissible for the value to be a reference to an enum
-# that appeared earlier, e.g.:
-#
-#         DRAW_FRAMEBUFFER_BINDING                   = GL_FRAMEBUFFER_BINDING
-#
-# Note that when this happens the value *does* include the "GL_"
-# prefix.
-#
-#
-# The JSON output format is as follows:
-# {
-#   "categories": {
-#     <category name>: {
-#       "kind": <"GL" or "GLES" for a GL version, "extension" for an extension>
-#       "gl_10x_version": <For a GL version, version number times 10>,
-#       "extension_name" <For an extension, name of the extension>
-#     }, ...
-#   },
-#   "enums": {
-#     <enum name, without "GL_" prefix>: {
-#       "value_int": <value integer>
-#       "value_str": <value string>
-#     }, ...
-#   },
-#   "functions": {
-#     <function name, without "gl" prefix>: {
-#       "categories": <list of categories in which this function appears>,
-#       "param_names": <list of param names>,
-#       "param_types": <list of param types>,
-#       "return_type": <type, or "void" if no return>
-#     }, ...
-#   },
-#   "function_alias_sets": {
-#     <list of synonymous function names>, ...
-#   },
-# }
-
-
-import collections
-import csv
-import re
-import sys
-try:
-    import simplejson as json
-except ImportError:
-    import json
-
-
-GLSPEC_HEADER_REGEXP = re.compile(r'^(\w+)\((.*)\)$')
-GLSPEC_ATTRIBUTE_REGEXP = re.compile(r'^\s+(\w+)\s+(.*)$')
-GL_VERSION_REGEXP = re.compile('^VERSION_([0-9])_([0-9])(_DEPRECATED)?$')
-GLES_VERSION_REGEXP = re.compile('^GL_ES_VERSION_([0-9])_([0-9])(_DEPRECATED)?$')
-ENUM_REGEXP = re.compile(r'^\s+(\w+)\s+=\s+(\w+)$')
-EXTENSION_SUFFIX_REGEXP = re.compile(r'(ARB|EXT|KHR|OES|NV|AMD|IMG|QCOM|INTEL)$')
-
-
-# Convert a type into a canonical form that is consistent about its
-# use of spaces.
-#
-# Example input: 'const       void**'
-# Example output: 'const void * *'
-def normalize_type(typ):
-    tokens = [token for token in typ.replace('*', ' * ').split(' ')
-              if token != '']
-    return ' '.join(tokens)
-
-
-# Interpret an enumerated value, which may be in hex or decimal, and
-# may include a type suffix such as "ull".
-#
-# Example input: '0xFFFFFFFFul'
-# Example output: 4294967295
-def decode_enum_value(value_str):
-    for suffix in ('u', 'ul', 'ull'):
-        if value_str.endswith(suffix):
-            value_str = value_str[:-len(suffix)]
-            break
-    return int(value_str, 0)
-
-
-# Convert an object to a form that can be serialized to JSON.  Python
-# "set" objects are converted to lists.
-def jsonize(obj):
-    if type(obj) in (set, frozenset):
-        return sorted(obj)
-    else:
-        raise Exception('jsonize failed for {0}'.format(type(obj)))
-
-
-# Iterate through the lines of a file, discarding end-of-line comments
-# delimited by "#".  Blank lines are discarded, as well as any
-# whitespace at the end of a line.
-def filter_comments(f):
-    for line in f:
-        if '#' in line:
-            line = line[:line.find('#')]
-        line = line.rstrip()
-        if line != '':
-            yield line.rstrip()
-
-
-# Convert a category name from the form used in the gl.spec file to
-# the form we want to output in JSON.  E.g.:
-#
-# - "VERSION_2_1" is converted into { 'kind': 'GL', 'gl_10x_version': 21 }
-#
-# - "ARB_foo" is converted into:
-#   { 'kind': 'extension', 'extension_name': 'GL_ARB_foo' }
-#
-# - "GL_ES_VERSION_2_0" is converted into:
-#     { 'kind': 'GLES', 'gl_10x_version': 20 }
-#   (this category is a piglit extension for local-gl.spec)
-def translate_category(category_name):
-    m = GL_VERSION_REGEXP.match(category_name)
-    if m:
-        ones = int(m.group(1))
-        tenths = int(m.group(2))
-        return ('{0}.{1}'.format(ones, tenths),
-                {'kind': 'GL', 'gl_10x_version': 10 * ones + tenths})
-
-    m = GLES_VERSION_REGEXP.match(category_name)
-    if m:
-        ones = int(m.group(1))
-        tenths = int(m.group(2))
-        return ('GLES{0}.{1}'.format(ones, tenths),
-                {'kind': 'GLES', 'gl_10x_version': 10 * ones + tenths})
-
-    extension_name = 'GL_' + category_name
-    return (extension_name,
-            {'kind': 'extension', 'extension_name': extension_name})
-
-
-# Data structure keeping track of which function names are known, and
-# which names are synonymous with which other names.
-class SynonymMap(object):
-    def __init__(self):
-        # __name_to_synonyms maps from a function name to the set of
-        # all names that are synonymous with it (including itself).
-        self.__name_to_synonyms = {}
-
-    # Add a single function name which is not (yet) known to be
-    # synonymous with any other name.  No effect if the function name
-    # is already known.
-    def add_singleton(self, name):
-        if name not in self.__name_to_synonyms:
-            self.__name_to_synonyms[name] = frozenset([name])
-        return self.__name_to_synonyms[name]
-
-    # Add a pair of function names, and note that they are synonymous.
-    # Synonymity is transitive, so if either of the two function names
-    # previously had known synonyms, all synonyms are combined into a
-    # single set.
-    def add_alias(self, name, alias):
-        name_ss = self.add_singleton(name)
-        alias_ss = self.add_singleton(alias)
-        combined_set = name_ss | alias_ss
-        for n in combined_set:
-            self.__name_to_synonyms[n] = combined_set
-
-    # Get a set of sets of synonymous functions.
-    def get_synonym_sets(self):
-        return frozenset(self.__name_to_synonyms.values())
-
-
-# In-memory representation of the GL API.
-class Api(object):
-    def __init__(self):
-        # Api.type_translation is a dict mapping abstract type names
-        # to C types.  It is based on the data in the gl.tm file.  For
-        # example, the dict entry for String is:
-        #
-        # 'String': 'const GLubyte *'
-        self.type_translation = {}
-
-        # Api.enums is a dict mapping enum names (without the 'GL_'
-        # prefix) to a dict containing (a) the enum value expressed as
-        # an integer, and (b) the enum value expressed as a C literal.
-        # It is based on the data in the gl.spec file.  For example,
-        # the dict entry for GL_CLIENT_ALL_ATTRIB_BITS is:
-        #
-        # 'CLIENT_ALL_ATTRIB_BITS': { 'value_int': 4294967295,
-        #                             'value_str': "0xFFFFFFFF" }
-        self.enums = {}
-
-        # Api.functions is a dict mapping function names (without the
-        # 'gl' prefix) to a dict containing (a) the name of the
-        # category the function is in, (b) the function call parameter
-        # names, (c) the function call parameter types, and (d) the
-        # function return type.  It is based on the data in the
-        # gl.spec file, cross-referenced against the type translations
-        # from the gl.tm file.  For example, the dict entry for
-        # glAreTexturesResident is:
-        #
-        # 'AreTexturesResident': {
-        #    'category': '1.1',
-        #    'param_names': ['n', 'textures', 'residences'],
-        #    'param_types': ['GLsizei', 'const GLuint *', 'GLboolean *'],
-        #    'return_type': ['GLboolean'] }
-        self.functions = {}
-
-        # Api.synonyms is a SynonymMap object which records which
-        # function names are aliases of each other.  It is based on
-        # the "alias" declarations from the gl.spec file.
-        self.synonyms = SynonymMap()
-
-        # Api.categories is a dict mapping category names to a dict
-        # describing the category.  For categories representing a GL
-        # version, the dict entry looks like this:
-        #
-        # '2.1': { 'kind': 'GL', 'gl_10x_version': 21 }
-        #
-        # For categories representing a GLES version, the dict entry looks
-        # like this:
-        #
-        # 'GLES2.0': { 'kind': 'GLES', 'gl_10x_version': 20 }
-        #
-        # For categories representing an extension, the dict entry
-        # looks like this:
-        #
-        # 'GL_ARB_sync': { 'kind': 'extension',
-        #                  'extension_name': 'GL_ARB_sync' }
-        self.categories = {}
-
-    # Convert each line in the gl.tm file into a key/value pair in
-    # self.type_translation, mapping an abstract type name to a C
-    # type.
-    def read_gl_tm(self, f):
-        for line in csv.reader(filter_comments(f)):
-            name = line[0].strip()
-            typ = line[3].strip()
-            if typ == '*':
-                # gl.tm uses "*" to represent void (for void used as a
-                # return value).
-                typ = 'void'
-            self.type_translation[name] = normalize_type(typ)
-
-    # Group the lines in the gl.spec file into triples (function_name,
-    # param_names, attributes).  For example, the following gl.spec
-    # input:
-    #
-    # Foo(bar, baz):
-    #     x  value1
-    #     y  value2 other_info
-    #     y  value3 more_info
-    #
-    # Produces this output triple:
-    #
-    # ('Foo', ['bar', 'baz'],
-    #  {'x': ['value1'], 'y': ['value2 other_info', 'value3 more_info']})
-    @staticmethod
-    def group_gl_spec_functions(f):
-        function_name = None
-        param_names = None
-        attributes = None
-        for line in filter_comments(f):
-            m = GLSPEC_HEADER_REGEXP.match(line)
-            if m:
-                if function_name:
-                    yield function_name, param_names, attributes
-                function_name = m.group(1)
-                if m.group(2) == '':
-                    param_names = []
-                else:
-                    param_names = [n.strip() for n in m.group(2).split(',')]
-                attributes = collections.defaultdict(list)
-                continue
-            m = GLSPEC_ATTRIBUTE_REGEXP.match(line)
-            if m:
-                attribute_type, attribute_data = m.groups()
-                attributes[attribute_type].append(attribute_data)
-                continue
-            continue
-        if function_name:
-            yield function_name, param_names, attributes
-
-    def add_function(self, name, return_type, param_names, param_types,
-                     category_string):
-        category, additional_data = translate_category(category_string)
-        if category not in self.categories:
-            self.categories[category] = additional_data
-
-        if name not in self.functions:
-            self.functions[name] = {
-                'return_type': return_type,
-                'param_names': param_names,
-                'param_types': param_types,
-                'categories': [category]
-            }
-        else:
-            if category not in self.functions[name]['categories']:
-                self.functions[name]['categories'].append(category)
-
-        self.synonyms.add_singleton(name)
-
-    # Process the data in gl.spec, and populate self.functions,
-    # self.synonyms, and self.categories based on it.
-    def read_gl_spec(self, f):
-        for name, param_names, attributes in self.group_gl_spec_functions(f):
-            if name in self.functions:
-                raise Exception(
-                    'Function {0!r} appears more than once'.format(name))
-            param_name_to_index = dict(
-                (param_name, index)
-                for index, param_name in enumerate(param_names))
-            param_types = [None] * len(param_names)
-            if len(attributes['param']) != len(param_names):
-                raise Exception(
-                    'Function {0!r} has a different number of parameters and '
-                    'param declarations'.format(name))
-            for param_datum in attributes['param']:
-                param_datum_tokens = param_datum.split()
-                param_name = param_datum_tokens[0]
-                param_index = param_name_to_index[param_name]
-                param_base_type = self.type_translation[param_datum_tokens[1]]
-                param_dir = param_datum_tokens[2]
-                param_multiplicity = param_datum_tokens[3]
-                if param_types[param_index] is not None:
-                    raise Exception(
-                        'Function {0!r} contains more than one param '
-                        'declaration for parameter {1!r}'.format(
-                            name, param_name))
-                if param_multiplicity == 'value':
-                    assert param_dir == 'in'
-                    param_type = param_base_type
-                elif param_multiplicity in ('array', 'reference'):
-                    if param_dir == 'in':
-                        # Note: technically this is not correct if
-                        # param_base_type is a pointer type (e.g. to
-                        # make an "in array" of "void *" we should
-                        # produce "void * const *", not "const void *
-                        # *").  However, the scripts used by the GL
-                        # consortium to produce glext.h from gl.spec
-                        # produce "const void * *", and fortunately
-                        # the only ill effect of this is that clients
-                        # have to do a little more typecasting than
-                        # they should.  So to avoid confusing people,
-                        # we're going to make the same mistake, so
-                        # that the resulting function signatures match
-                        # those in glext.h.
-                        param_type = normalize_type(
-                            'const {0} *'.format(param_base_type))
-                    elif param_dir == 'out':
-                        param_type = normalize_type(
-                            '{0} *'.format(param_base_type))
-                    else:
-                        raise Exception(
-                            'Function {0!r} parameter {1!r} uses unrecognized '
-                            'direction {2!r}'.format(name, param_name,
-                                                     param_dir))
-                else:
-                    raise Exception(
-                        'Function {0!r} parameter {1!r} uses unrecognized '
-                        'multiplicity {2!r}'.format(name, param_name,
-                                                    param_multiplicity))
-                param_types[param_index] = param_type
-            if len(attributes['return']) != 1:
-                raise Exception(
-                    'Function {0!r} contains {1} return attributes'.format(
-                        name, len(attributes['return'])))
-            return_type = self.type_translation[attributes['return'][0]]
-            if len(attributes['category']) != 1:
-                raise Exception(
-                    'Function {0!r} contains {1} category attributes'.format(
-                        name, len(attributes['category'])))
-            category = attributes['category'][0]
-            self.add_function(name, return_type, param_names, param_types,
-                              category)
-            for alias in attributes['alias']:
-                self.synonyms.add_alias(name, alias)
-
-    def read_gles_header(self, f):
-        category = 'GL_ES_VERSION_2_0'
-        for line in f:
-            # The GLES gl3.h has typedefs, tokens, and prototypes,
-            # each listed after a comment indicating whether they're
-            # part of 2.0 core or 3.0 core.
-            #
-            # The gl2ext.h is split into groups of functions prefixed
-            # by the extension name in a comment.
-            if re.match(r'/\* OpenGL ES 2.0 \*/', line):
-                category = 'GL_ES_VERSION_2_0'
-            elif re.match(r'/\* OpenGL ES 3.0 \*/', line):
-                category = 'GL_ES_VERSION_3_0'
-            else:
-                m = re.match(r'/\* (GL_.*) \*/', line)
-                if m:
-                    # replace only the first occurence of 'GL_'
-                    category = m.group(1).replace('GL_', '', 1)
-
-            m = re.match(r'GL_APICALL', line)
-            if m:
-                # We do the regexp in two parts to make sure that we
-                # actually do catch all the GL_APICALLs.
-                m = re.match(r'^GL_APICALL\s*(.*)\s*GL_APIENTRY'
-                             '\s*gl(\w*)\s\((.*)\).*$', line)
-                return_type, name, args = m.groups()
-
-                return_type = return_type.strip()
-                args = args.split(', ')
-
-                if args == ['void']:
-                    args = []
-                param_names = []
-                param_types = []
-                for arg in args:
-                    splitloc = max(arg.rfind(' '), arg.rfind('*'))
-                    param_types.append(arg[:splitloc + 1])
-                    param_names.append(arg[splitloc + 1:])
-
-                self.add_function(name, return_type, param_names, param_types,
-                                  category)
-
-                # Since we don't have alias information for
-                # extensions, assume that pretty much anything
-                # with the same base name as a core function is
-                # aliased with it.
-                #
-                # glTexImage3DOES is an exception because it
-                # doesn't have the border argument.
-                if name != 'TexImage3DOES':
-                    corename = EXTENSION_SUFFIX_REGEXP.sub('', name)
-                    if corename in self.functions:
-                        self.synonyms.add_alias(corename, name)
-
-    # Convert each line in the enumext.spec file into a key/value pair
-    # in self.enums, mapping an enum name to a dict.  For example, the
-    # following enumext.spec input:
-    #
-    # CLIENT_ALL_ATTRIB_BITS = 0xFFFFFFFF # ClientAttribMask
-    #
-    # Produces the dict entry:
-    #
-    # 'CLIENT_ALL_ATTRIB_BITS': { 'value_int': 4294967295,
-    #                             'value_str': "0xFFFFFFFF" }
-    def read_enumext_spec(self, f):
-        for line in filter_comments(f):
-            m = ENUM_REGEXP.match(line)
-            if m:
-                name, value = m.groups()
-                if value.startswith('GL_'):
-                    value_rhs = value[3:]
-                    value_int = self.enums[value_rhs]['value_int']
-                else:
-                    value_int = decode_enum_value(value)
-                self.enums[name] = {'value_str': value,
-                                    'value_int': value_int}
-
-    # Convert the stored API into JSON.  To make diffing easier, all
-    # dictionaries are sorted by key, and all sets are sorted by set
-    # element.
-    def to_json(self):
-        return json.dumps({'categories': self.categories,
-                           'enums': self.enums,
-                           'functions': self.functions,
-                           'function_alias_sets':
-                           self.synonyms.get_synonym_sets()},
-                          indent=2, sort_keys=True, default=jsonize)
-
-
-if __name__ == '__main__':
-    api = Api()
-    with open(sys.argv[1]) as f:
-        api.read_gl_tm(f)
-    with open(sys.argv[2]) as f:
-        api.read_gl_spec(f)
-    with open(sys.argv[3]) as f:
-        api.read_enumext_spec(f)
-    with open(sys.argv[4]) as f:
-        api.read_enumext_spec(f)
-    with open(sys.argv[5]) as f:
-        api.read_gles_header(f)
-    with open(sys.argv[6]) as f:
-        api.read_gles_header(f)
-    with open(sys.argv[7], 'w') as f:
-        f.write(api.to_json())
diff --git a/tests/util/gen_dispatch.py b/tests/util/gen_dispatch.py
index c97af94..d983513 100644
--- a/tests/util/gen_dispatch.py
+++ b/tests/util/gen_dispatch.py
@@ -20,41 +20,10 @@
 # IN THE SOFTWARE.
 
 # This script generates a C file (and corresponding header) allowing
-# Piglit to dispatch calls to OpenGL based on a JSON description of
-# the GL API (and extensions).
-#
-# Invoke this script with 3 command line arguments: the JSON input
-# filename, the C output filename, and the header outpit filename.
-#
-#
-# The input looks like this:
-#
-# {
-#   "categories": {
-#     <category name>: {
-#       "kind": <"GL" or "GLES" for a GL spec API, "extension" for an extension>,
-#       "gl_10x_version": <For a GL version, version number times 10>,
-#       "extension_name" <For an extension, name of the extension>
-#     }, ...
-#   },
-#   "enums": {
-#     <enum name, without "GL_" prefix>: {
-#       "value_int": <value integer>
-#       "value_str": <value string>
-#     }, ...
-#   },
-#   "functions": {
-#     <function name, without "gl" prefix>: {
-#       "categories": <list of categories in which this function appears>,
-#       "param_names": <list of param names>,
-#       "param_types": <list of param types>,
-#       "return_type": <type, or "void" if no return>
-#     }, ...
-#   },
-#   "function_alias_sets": {
-#     <list of synonymous function names>, ...
-#   },
-# }
+# Piglit to dispatch calls to OpenGL based on the OpenGL API Registry gl.xml.
+#
+# Invoke this script with 3 command line arguments: the path to gl.xml,
+# the C output filename, and the header output filename.
 #
 #
 # The generated header consists of the following:
@@ -145,10 +114,7 @@
 import collections
 import os.path
 import sys
-try:
-    import simplejson as json
-except:
-    import json
+from xml.etree import ElementTree
 
 
 # Generate a top-of-file comment cautioning that the file is
@@ -197,7 +163,6 @@ def fixup_param_name(name):
     else:
         return name
 
-
 # Internal representation of a category.
 #
 # - For a category representing a GL version, Category.kind is 'GL'
@@ -208,12 +173,15 @@ def fixup_param_name(name):
 #   'extension' and Category.extension_name is the extension name
 #   (including the 'GL_' prefix).
 class Category(object):
-    def __init__(self, json_data):
-        self.kind = json_data['kind']
-        if 'gl_10x_version' in json_data:
-            self.gl_10x_version = json_data['gl_10x_version']
-        if 'extension_name' in json_data:
-            self.extension_name = json_data['extension_name']
+    def __init__(self, xml_element):
+        if xml_element.tag == 'feature':
+            api = xml_element.attrib['api']
+            number = xml_element.attrib['number']
+            self.kind = 'GL' if api == 'gl' else 'GLES'
+            self.gl_10x_version = int(number.replace('.', ''))
+        else:
+            self.kind = 'extension'
+            self.extension_name = xml_element.attrib['name']
 
     # Generate a human-readable representation of the category (for
     # use in generated comments).
@@ -245,21 +213,41 @@ class Category(object):
 # - Function.return_type is the return type of the function, or 'void'
 #   if the function has no return.
 #
-# - Function.category is a Category object describing the extension or
-#   GL version the function is defined in.
+# - Function.categories is a list of Category objects describing the extension
+#   or GL version the function is defined in.
 class Function(object):
-    def __init__(self, name, json_data):
-        self.name = name
+    def __init__(self, xml_function):
+        proto = xml_function.find('proto')
+        self.gl_name = proto.find('name').text
         self.param_names = [
-            fixup_param_name(x) for x in json_data['param_names']]
-        self.param_types = json_data['param_types']
-        self.return_type = json_data['return_type']
-        self.categories = json_data['categories']
+            fixup_param_name(x.find('name').text)
+            for x in xml_function.findall('param')]
+        self.param_types = [
+            self.get_type(x)
+            for x in xml_function.findall('param')]
+        self.return_type = self.get_type(proto)
+
+        # Categories are added later.
+        self.categories = []
+
+    # Helper to extract type information from gl.xml data.
+    @staticmethod
+    def get_type(xml_elem):
+        type_name = ""
+        if xml_elem.text: type_name += xml_elem.text
+        for child in xml_elem:
+            if child.tag == 'ptype':
+                if child.text: type_name += child.text
+            if child.tail: type_name += child.tail
+            if child.tag == 'name':
+                break
+        return type_name.strip()
+
 
-    # Name of the function, with the 'gl' prefix.
+    # Name of the function, without the 'gl' prefix.
     @property
-    def gl_name(self):
-        return 'gl' + self.name
+    def name(self):
+        return self.gl_name.partition('gl')[2]
 
     # Name of the function signature typedef corresponding to this
     # function.  E.g. for the glGetString function, this is
@@ -297,9 +285,10 @@ class Function(object):
 # - Enum.value_str is the value of the enum, as a string suitable for
 #   emitting as C code.
 class Enum(object):
-    def __init__(self, json_data):
-        self.value_int = json_data['value_int']
-        self.value_str = json_data['value_str']
+    def __init__(self, xml_enum):
+        value = xml_enum.attrib['value']
+        self.value_int = int(value, base=0)
+        self.value_str = value
 
 
 # Data structure keeping track of a set of synonymous functions.  Such
@@ -379,17 +368,102 @@ class DispatchSet(object):
 # - Api.categories is a dict mapping category name to a Category
 #   object.
 class Api(object):
-    def __init__(self, json_data):
+    def __init__(self, xml_root):
+        # Parse enums.
         self.enums = dict(
-            (key, Enum(value))
-            for key, value in json_data['enums'].items())
+            (xml_enum.attrib['name'], Enum(xml_enum))
+            for xml_enums in xml_root.findall('enums')
+            for xml_enum in xml_enums.findall('enum'))
+
+        # Parse function prototypes ("commands" in gl.xml lingo).
+        xml_functions = xml_root.find('commands').findall('command')
         self.functions = dict(
-            (key, Function(key, value))
-            for key, value in json_data['functions'].items())
-        self.function_alias_sets = json_data['function_alias_sets']
-        self.categories = dict(
-            (key, Category(value))
-            for key, value in json_data['categories'].items())
+            (self.function_name(xml_function), Function(xml_function))
+            for xml_function in xml_functions)
+
+        self.fix_aliases(xml_functions)
+
+        # Parse function alias data.
+        self.function_alias_sets = [[self.function_name(xml_function)]
+            for xml_function in xml_functions
+            if not self.function_is_alias(xml_function)]
+        aliases = [(self.function_name(xml_function),
+                    self.function_alias(xml_function))
+            for xml_function in xml_functions
+            if self.function_is_alias(xml_function)]
+        self.merge_aliases(self.function_alias_sets, aliases)
+
+        self.categories = dict()
+        # Parse core api categories ("features" in gl.xml lingo).
+        for xml_feature in xml_root.findall('feature'):
+            api = xml_feature.attrib['api']
+            number = xml_feature.attrib['number']
+            name = number if (api == 'gl') else 'GLES' + number
+            self.categories[name] = Category(xml_feature)
+            self.add_category_to_functions(name, xml_feature)
+        # Parse GL extension categories.
+        for xml_extension in xml_root.find('extensions').findall('extension'):
+            name = xml_extension.attrib['name']
+            self.categories[name] = Category(xml_extension)
+            self.add_category_to_functions(name, xml_extension)
+
+    @staticmethod
+    def function_name(xml_function):
+        return xml_function.find('proto').find('name').text
+
+    @staticmethod
+    def function_alias(xml_function):
+        return xml_function.find('alias').attrib['name']
+
+    @staticmethod
+    def function_is_alias(xml_function):
+        return xml_function.find('alias') is not None
+
+    # glDebugMessageInsertARB and glDebugMessageControlARB are marked as
+    # aliases of the corresponding 4.3 core functions in gl.xml. This is not
+    # correct as the core functions accept additional enums for their type
+    # and severity parameters. Remove the aliases.
+    @classmethod
+    def fix_aliases(cls, xml_functions):
+        for xml_function in xml_functions:
+            if cls.function_name(xml_function) == 'glDebugMessageInsertARB':
+                xml_function.remove(xml_function.find('alias'))
+            if cls.function_name(xml_function) == 'glDebugMessageControlARB':
+                xml_function.remove(xml_function.find('alias'))
+
+    # Helper function to build list of aliases lists.
+    #
+    # merged -- list of n-tuples of function names that are aliases of each
+    #           other.
+    # to_merge -- list of 2-tuples to merge into merged.
+    #
+    # recursively try to merge aliases from to_merge into merged until to_merge
+    # is empty.
+    @staticmethod
+    def merge_aliases(merged, to_merge):
+        if not to_merge:
+            return
+        still_to_merge = []
+        for alias in to_merge:
+            i = next((i
+                for i, f in enumerate(merged)
+                if alias[1] in f), None)
+            if i != None:
+                merged[i].append(alias[0])
+            else:
+                still_to_merge.append(to_merge)
+
+        Api.merge_aliases(merged, still_to_merge)
+
+    # Add given category to all functions it requires.
+    #
+    # name -- category name e.g.: "1.5", "GLES2.0" or "GL_ARB_multitexture"
+    # xml_element -- xml element of category
+    def add_category_to_functions(self, name, xml_element):
+        for function in [cmd.attrib['name']
+                         for req in xml_element.findall('require')
+                         for cmd in req.findall('command')]:
+            self.functions[function].categories.append(name)
 
     # Generate a list of (name, value) pairs representing all enums in
     # the API.  The resulting list is sorted by enum value.
@@ -436,8 +510,8 @@ class Api(object):
 # Read the given input file and return an Api object containing the
 # data in it.
 def read_api(filename):
-    with open(filename, 'r') as f:
-        return Api(json.load(f))
+    tree = ElementTree.parse(filename)
+    return Api(tree.getroot())
 
 
 # Generate the resolve function for a given DispatchSet.
@@ -486,6 +560,11 @@ def generate_resolve_function(ds):
             # glTexImage3DEXT, cast the result to PFNGLTEXIMAGE3DPROC
             # to avoid a warning.
             typedef_name = 'PFNGLTEXIMAGE3DPROC'
+        elif f.name == 'ShaderSourceARB':
+            # As above: glShaderSourceARB defines the first argument as
+            # GLhandleARB, glShaderSource uses a GLuint.  Both are
+            # typedefs of unsigned int.
+            typedef_name = 'PFNGLSHADERSOURCEPROC'
         else:
             typedef_name = f.typedef_name
 
@@ -495,9 +574,8 @@ def generate_resolve_function(ds):
         condition_code_pairs.append((condition, code))
 
     # XXX: glDraw{Arrays,Elements}InstancedARB are exposed by
-    # ARB_instanced_arrays in addition to ARB_draw_instanced, but neither
-    # gl.spec nor gl.json can accomodate an extension with two categories, so
-    # insert these cases here.
+    # ARB_instanced_arrays in addition to ARB_draw_instanced, but gl.xml
+    # ignores the former, so insert these cases here.
         if f.gl_name in ('glDrawArraysInstancedARB',
                          'glDrawElementsInstancedARB'):
             condition = 'check_extension("GL_ARB_instanced_arrays")'
@@ -649,20 +727,14 @@ def generate_code(api):
 
     # Emit enum #defines
     for name, value in api.compute_unique_enums():
-        h_contents.append('#define GL_{0} {1}\n'.format(name, value))
+        h_contents.append('#define {0} {1}\n'.format(name, value))
 
     # Emit extension #defines
-    #
-    # While enum.ext lists some old extension names (defined to 1), it
-    # doesn't contain the full set that appears in glext.h.
     h_contents.append('\n')
     for ext in api.extensions:
         h_contents.append('#define {0} 1\n'.format(ext))
 
     # Emit GL version #defines
-    #
-    # While enum.ext lists GL versions up to 3.2, it didn't continue
-    # adding them for later GL versions.
     h_contents.append('\n')
     for ver in api.gl_versions:
         h_contents.append('#define GL_VERSION_{0}_{1} 1\n'.format(
diff --git a/tests/util/piglit-dispatch.h b/tests/util/piglit-dispatch.h
index 669aecc..cb2dbad 100644
--- a/tests/util/piglit-dispatch.h
+++ b/tests/util/piglit-dispatch.h
@@ -96,6 +96,7 @@ typedef unsigned short GLushort;
 typedef unsigned long GLulong;
 typedef float GLfloat;
 typedef float GLclampf;
+typedef int GLclampx;
 typedef double GLdouble;
 typedef double GLclampd;
 typedef void GLvoid;
-- 
1.8.3.2




More information about the Piglit mailing list