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