xref: /aosp_15_r20/external/mesa3d/src/gfxstream/codegen/scripts/cgenerator.py (revision 6104692788411f58d303aa86923a9ff6ecaded22)
1#!/usr/bin/python3 -i
2#
3# Copyright 2013-2023 The Khronos Group Inc.
4# Copyright 2023-2024 Google Inc.
5#
6# SPDX-License-Identifier: Apache-2.0
7
8import os
9import re
10
11from generator import (GeneratorOptions,
12                       MissingGeneratorOptionsError, MissingRegistryError,
13                       OutputGenerator, noneStr, regSortFeatures, write)
14
15class CGeneratorOptions(GeneratorOptions):
16    """CGeneratorOptions - subclass of GeneratorOptions.
17
18    Adds options used by COutputGenerator objects during C language header
19    generation."""
20
21    def __init__(self,
22                 prefixText='',
23                 apientry='',
24                 apientryp='',
25                 alignFuncParam=0,
26                 **kwargs
27                 ):
28        """Constructor.
29        Additional parameters beyond parent class:
30
31        - prefixText - list of strings to prefix generated header with
32        (usually a copyright statement + calling convention macros)
33        - apientry - string to use for the calling convention macro,
34        in typedefs, such as APIENTRY
35        - apientryp - string to use for the calling convention macro
36        in function pointer typedefs, such as APIENTRYP
37        - alignFuncParam - if nonzero and parameters are being put on a
38        separate line, align parameter names at the specified column"""
39
40        GeneratorOptions.__init__(self, **kwargs)
41
42        self.prefixText = prefixText
43        """list of strings to prefix generated header with (usually a copyright statement + calling convention macros)."""
44
45        self.apientry = apientry
46        """string to use for the calling convention macro, in typedefs, such as APIENTRY."""
47
48        self.apientryp = apientryp
49        """string to use for the calling convention macro in function pointer typedefs, such as APIENTRYP."""
50
51        self.alignFuncParam = alignFuncParam
52        """if nonzero and parameters are being put on a separate line, align parameter names at the specified column"""
53
54class COutputGenerator(OutputGenerator):
55    """Generates C-language API interfaces."""
56
57    # This is an ordered list of sections in the header file.
58    TYPE_SECTIONS = ['include', 'define', 'basetype', 'handle', 'enum',
59                     'group', 'bitmask', 'funcpointer', 'struct']
60    ALL_SECTIONS = TYPE_SECTIONS + ['commandPointer', 'command']
61
62    def __init__(self, *args, **kwargs):
63        super().__init__(*args, **kwargs)
64        # Internal state - accumulators for different inner block text
65        self.sections = {section: [] for section in self.ALL_SECTIONS}
66        self.feature_not_empty = False
67
68    def beginFile(self, genOpts):
69        OutputGenerator.beginFile(self, genOpts)
70        if self.genOpts is None:
71            raise MissingGeneratorOptionsError()
72
73        # User-supplied prefix text, if any (list of strings)
74        if genOpts.prefixText:
75            for s in genOpts.prefixText:
76                write(s, file=self.outFile)
77
78        # C++ extern wrapper - after prefix lines so they can add includes.
79        self.newline()
80        write('#ifdef __cplusplus', file=self.outFile)
81        write('extern "C" {', file=self.outFile)
82        write('#endif', file=self.outFile)
83        self.newline()
84
85    def endFile(self):
86        # C-specific
87        # Finish C++ wrapper and multiple inclusion protection
88        if self.genOpts is None:
89            raise MissingGeneratorOptionsError()
90        self.newline()
91        write('#ifdef __cplusplus', file=self.outFile)
92        write('}', file=self.outFile)
93        write('#endif', file=self.outFile)
94        # Finish processing in superclass
95        OutputGenerator.endFile(self)
96
97    def beginFeature(self, interface, emit):
98        # Start processing in superclass
99        OutputGenerator.beginFeature(self, interface, emit)
100        # C-specific
101        # Accumulate includes, defines, types, enums, function pointer typedefs,
102        # end function prototypes separately for this feature. They are only
103        # printed in endFeature().
104        self.sections = {section: [] for section in self.ALL_SECTIONS}
105        self.feature_not_empty = False
106
107    def endFeature(self):
108        "Actually write the interface to the output file."
109        # C-specific
110        if self.emit:
111            if self.feature_not_empty:
112                if self.genOpts is None:
113                    raise MissingGeneratorOptionsError()
114                is_core = self.featureName and self.featureName.startswith('VK_VERSION_')
115                self.newline()
116
117                # Generate warning of possible use in IDEs
118                write(f'// {self.featureName} is a preprocessor guard. Do not pass it to API calls.', file=self.outFile)
119                write('#define', self.featureName, '1', file=self.outFile)
120                for section in self.TYPE_SECTIONS:
121                    contents = self.sections[section]
122                    if contents:
123                        write('\n'.join(contents), file=self.outFile)
124
125                if self.sections['commandPointer']:
126                    write('\n'.join(self.sections['commandPointer']), file=self.outFile)
127                    self.newline()
128
129                if self.sections['command']:
130                    write('\n'.join(self.sections['command']), end='', file=self.outFile)
131
132        # Finish processing in superclass
133        OutputGenerator.endFeature(self)
134
135    def appendSection(self, section, text):
136        "Append a definition to the specified section"
137
138        if section is None:
139            exit(1)
140
141        self.sections[section].append(text)
142        self.feature_not_empty = True
143
144    def genType(self, typeinfo, name, alias):
145        "Generate type."
146        OutputGenerator.genType(self, typeinfo, name, alias)
147        typeElem = typeinfo.elem
148
149        # Vulkan:
150        # Determine the category of the type, and the type section to add
151        # its definition to.
152        # 'funcpointer' is added to the 'struct' section as a workaround for
153        # internal issue #877, since structures and function pointer types
154        # can have cross-dependencies.
155        category = typeElem.get('category')
156        if category == 'funcpointer':
157            section = 'struct'
158        else:
159            section = category
160
161        if category in ('struct', 'union'):
162            # If the type is a struct type, generate it using the
163            # special-purpose generator.
164            self.genStruct(typeinfo, name, alias)
165        else:
166            if self.genOpts is None:
167                raise MissingGeneratorOptionsError()
168            # Replace <apientry /> tags with an APIENTRY-style string
169            # (from self.genOpts). Copy other text through unchanged.
170            # If the resulting text is an empty string, do not emit it.
171            body = noneStr(typeElem.text)
172            for elem in typeElem:
173                if elem.tag == 'apientry':
174                    body += self.genOpts.apientry + noneStr(elem.tail)
175                else:
176                    body += noneStr(elem.text) + noneStr(elem.tail)
177            if body:
178                # Add extra newline after multi-line entries.
179                if '\n' in body[0:-1]:
180                    body += '\n'
181                self.appendSection(section, body)
182
183    def genProtectString(self, protect_str):
184        """Generate protection string.
185
186        Protection strings are the strings defining the OS/Platform/Graphics
187        requirements for a given API command.  When generating the
188        language header files, we need to make sure the items specific to a
189        graphics API or OS platform are properly wrapped in #ifs."""
190        protect_if_str = ''
191        protect_end_str = ''
192        if not protect_str:
193            return (protect_if_str, protect_end_str)
194
195        if ',' in protect_str:
196            protect_list = protect_str.split(',')
197            protect_defs = ('defined(%s)' % d for d in protect_list)
198            protect_def_str = ' && '.join(protect_defs)
199            protect_if_str = '#if %s\n' % protect_def_str
200            protect_end_str = '#endif // %s\n' % protect_def_str
201        else:
202            protect_if_str = '#ifdef %s\n' % protect_str
203            protect_end_str = '#endif // %s\n' % protect_str
204
205        return (protect_if_str, protect_end_str)
206
207    def genStruct(self, typeinfo, typeName, alias):
208        """Generate struct (e.g. C "struct" type).
209
210        This is a special case of the <type> tag where the contents are
211        interpreted as a set of <member> tags instead of freeform C
212        C type declarations. The <member> tags are just like <param>
213        tags - they are a declaration of a struct or union member.
214        Only simple member declarations are supported (no nested
215        structs etc.)
216
217        If alias is not None, then this struct aliases another; just
218        generate a typedef of that alias."""
219        OutputGenerator.genStruct(self, typeinfo, typeName, alias)
220
221        if self.genOpts is None:
222            raise MissingGeneratorOptionsError()
223
224        typeElem = typeinfo.elem
225
226        if alias:
227            body = 'typedef ' + alias + ' ' + typeName + ';\n'
228        else:
229            body = ''
230            (protect_begin, protect_end) = self.genProtectString(typeElem.get('protect'))
231            if protect_begin:
232                body += protect_begin
233
234            body += 'typedef ' + typeElem.get('category')
235
236            body += ' ' + typeName + ' {\n'
237
238            targetLen = self.getMaxCParamTypeLength(typeinfo)
239            for member in typeElem.findall('.//member'):
240                body += self.makeCParamDecl(member, targetLen + 4)
241                body += ';\n'
242            body += '} ' + typeName + ';\n'
243            if protect_end:
244                body += protect_end
245
246        self.appendSection('struct', body)
247
248    def genGroup(self, groupinfo, groupName, alias=None):
249        """Generate groups (e.g. C "enum" type).
250
251        These are concatenated together with other types.
252
253        If alias is not None, it is the name of another group type
254        which aliases this type; just generate that alias."""
255        OutputGenerator.genGroup(self, groupinfo, groupName, alias)
256        groupElem = groupinfo.elem
257
258        # After either enumerated type or alias paths, add the declaration
259        # to the appropriate section for the group being defined.
260        if groupElem.get('type') == 'bitmask':
261            section = 'bitmask'
262        else:
263            section = 'group'
264
265        if alias:
266            # If the group name is aliased, just emit a typedef declaration
267            # for the alias.
268            body = 'typedef ' + alias + ' ' + groupName + ';\n'
269            self.appendSection(section, body)
270
271    def genEnum(self, enuminfo, name, alias):
272        """Generate the C declaration for a constant (a single <enum> value).
273
274        <enum> tags may specify their values in several ways, but are usually
275        just integers."""
276
277        OutputGenerator.genEnum(self, enuminfo, name, alias)
278
279        body = self.buildConstantCDecl(enuminfo, name, alias)
280        self.appendSection('enum', body)
281
282    def genCmd(self, cmdinfo, name, alias):
283        "Command generation"
284        OutputGenerator.genCmd(self, cmdinfo, name, alias)
285
286        if self.genOpts is None:
287            raise MissingGeneratorOptionsError()
288
289        prefix = ''
290        decls = self.makeCDecls(cmdinfo.elem)
291        self.appendSection('command', prefix + decls[0] + '\n')
292        self.appendSection('commandPointer', decls[1])
293