1#!/usr/bin/python3 -i 2# 3# Copyright (c) 2013-2020 The Khronos Group Inc. 4# 5# SPDX-License-Identifier: Apache-2.0 6 7import os 8import re 9from generator import (GeneratorOptions, OutputGenerator, noneStr, 10 regSortFeatures, write) 11 12 13class CGeneratorOptions(GeneratorOptions): 14 """CGeneratorOptions - subclass of GeneratorOptions. 15 16 Adds options used by COutputGenerator objects during C language header 17 generation.""" 18 19 def __init__(self, 20 prefixText="", 21 genFuncPointers=True, 22 protectFile=True, 23 protectFeature=True, 24 protectProto=None, 25 protectProtoStr=None, 26 apicall='', 27 apientry='', 28 apientryp='', 29 indentFuncProto=True, 30 indentFuncPointer=False, 31 alignFuncParam=0, 32 genEnumBeginEndRange=False, 33 genAliasMacro=False, 34 aliasMacro='', 35 **kwargs 36 ): 37 """Constructor. 38 Additional parameters beyond parent class: 39 40 - prefixText - list of strings to prefix generated header with 41 (usually a copyright statement + calling convention macros). 42 - protectFile - True if multiple inclusion protection should be 43 generated (based on the filename) around the entire header. 44 - protectFeature - True if #ifndef..#endif protection should be 45 generated around a feature interface in the header file. 46 - genFuncPointers - True if function pointer typedefs should be 47 generated 48 - protectProto - If conditional protection should be generated 49 around prototype declarations, set to either '#ifdef' 50 to require opt-in (#ifdef protectProtoStr) or '#ifndef' 51 to require opt-out (#ifndef protectProtoStr). Otherwise 52 set to None. 53 - protectProtoStr - #ifdef/#ifndef symbol to use around prototype 54 declarations, if protectProto is set 55 - apicall - string to use for the function declaration prefix, 56 such as APICALL on Windows. 57 - apientry - string to use for the calling convention macro, 58 in typedefs, such as APIENTRY. 59 - apientryp - string to use for the calling convention macro 60 in function pointer typedefs, such as APIENTRYP. 61 - indentFuncProto - True if prototype declarations should put each 62 parameter on a separate line 63 - indentFuncPointer - True if typedefed function pointers should put each 64 parameter on a separate line 65 - alignFuncParam - if nonzero and parameters are being put on a 66 separate line, align parameter names at the specified column 67 - genEnumBeginEndRange - True if BEGIN_RANGE / END_RANGE macros should 68 be generated for enumerated types 69 - genAliasMacro - True if the OpenXR alias macro should be generated 70 for aliased types (unclear what other circumstances this is useful) 71 - aliasMacro - alias macro to inject when genAliasMacro is True""" 72 GeneratorOptions.__init__(self, **kwargs) 73 74 self.prefixText = prefixText 75 """list of strings to prefix generated header with (usually a copyright statement + calling convention macros).""" 76 77 self.genFuncPointers = genFuncPointers 78 """True if function pointer typedefs should be generated""" 79 80 self.protectFile = protectFile 81 """True if multiple inclusion protection should be generated (based on the filename) around the entire header.""" 82 83 self.protectFeature = protectFeature 84 """True if #ifndef..#endif protection should be generated around a feature interface in the header file.""" 85 86 self.protectProto = protectProto 87 """If conditional protection should be generated around prototype declarations, set to either '#ifdef' to require opt-in (#ifdef protectProtoStr) or '#ifndef' to require opt-out (#ifndef protectProtoStr). Otherwise set to None.""" 88 89 self.protectProtoStr = protectProtoStr 90 """#ifdef/#ifndef symbol to use around prototype declarations, if protectProto is set""" 91 92 self.apicall = apicall 93 """string to use for the function declaration prefix, such as APICALL on Windows.""" 94 95 self.apientry = apientry 96 """string to use for the calling convention macro, in typedefs, such as APIENTRY.""" 97 98 self.apientryp = apientryp 99 """string to use for the calling convention macro in function pointer typedefs, such as APIENTRYP.""" 100 101 self.indentFuncProto = indentFuncProto 102 """True if prototype declarations should put each parameter on a separate line""" 103 104 self.indentFuncPointer = indentFuncPointer 105 """True if typedefed function pointers should put each parameter on a separate line""" 106 107 self.alignFuncParam = alignFuncParam 108 """if nonzero and parameters are being put on a separate line, align parameter names at the specified column""" 109 110 self.genEnumBeginEndRange = genEnumBeginEndRange 111 """True if BEGIN_RANGE / END_RANGE macros should be generated for enumerated types""" 112 113 self.genAliasMacro = genAliasMacro 114 """True if the OpenXR alias macro should be generated for aliased types (unclear what other circumstances this is useful)""" 115 116 self.aliasMacro = aliasMacro 117 """alias macro to inject when genAliasMacro is True""" 118 119 self.codeGenerator = True 120 """True if this generator makes compilable code""" 121 122 123class COutputGenerator(OutputGenerator): 124 """Generates C-language API interfaces.""" 125 126 # This is an ordered list of sections in the header file. 127 TYPE_SECTIONS = ['include', 'define', 'basetype', 'handle', 'enum', 128 'group', 'bitmask', 'funcpointer', 'struct'] 129 ALL_SECTIONS = TYPE_SECTIONS + ['commandPointer', 'command'] 130 131 def __init__(self, *args, **kwargs): 132 super().__init__(*args, **kwargs) 133 # Internal state - accumulators for different inner block text 134 self.sections = {section: [] for section in self.ALL_SECTIONS} 135 self.feature_not_empty = False 136 self.may_alias = None 137 138 def beginFile(self, genOpts): 139 OutputGenerator.beginFile(self, genOpts) 140 # C-specific 141 # 142 # Multiple inclusion protection & C++ wrappers. 143 if genOpts.protectFile and self.genOpts.filename: 144 headerSym = re.sub(r'\.h', '_h_', 145 os.path.basename(self.genOpts.filename)).upper() 146 write('#ifndef', headerSym, file=self.outFile) 147 write('#define', headerSym, '1', file=self.outFile) 148 self.newline() 149 150 # User-supplied prefix text, if any (list of strings) 151 if genOpts.prefixText: 152 for s in genOpts.prefixText: 153 write(s, file=self.outFile) 154 155 # C++ extern wrapper - after prefix lines so they can add includes. 156 self.newline() 157 write('#ifdef __cplusplus', file=self.outFile) 158 write('extern "C" {', file=self.outFile) 159 write('#endif', file=self.outFile) 160 self.newline() 161 162 def endFile(self): 163 # C-specific 164 # Finish C++ wrapper and multiple inclusion protection 165 self.newline() 166 write('#ifdef __cplusplus', file=self.outFile) 167 write('}', file=self.outFile) 168 write('#endif', file=self.outFile) 169 if self.genOpts.protectFile and self.genOpts.filename: 170 self.newline() 171 write('#endif', file=self.outFile) 172 # Finish processing in superclass 173 OutputGenerator.endFile(self) 174 175 def beginFeature(self, interface, emit): 176 # Start processing in superclass 177 OutputGenerator.beginFeature(self, interface, emit) 178 # C-specific 179 # Accumulate includes, defines, types, enums, function pointer typedefs, 180 # end function prototypes separately for this feature. They're only 181 # printed in endFeature(). 182 self.sections = {section: [] for section in self.ALL_SECTIONS} 183 self.feature_not_empty = False 184 185 def endFeature(self): 186 "Actually write the interface to the output file." 187 # C-specific 188 if self.emit: 189 if self.feature_not_empty: 190 if self.genOpts.conventions.writeFeature(self.featureExtraProtect, self.genOpts.filename): 191 self.newline() 192 if self.genOpts.protectFeature: 193 write('#ifndef', self.featureName, file=self.outFile) 194 # If type declarations are needed by other features based on 195 # this one, it may be necessary to suppress the ExtraProtect, 196 # or move it below the 'for section...' loop. 197 if self.featureExtraProtect is not None: 198 write('#ifdef', self.featureExtraProtect, file=self.outFile) 199 self.newline() 200 write('#define', self.featureName, '1', file=self.outFile) 201 for section in self.TYPE_SECTIONS: 202 contents = self.sections[section] 203 if contents: 204 write('\n'.join(contents), file=self.outFile) 205 if self.genOpts.genFuncPointers and self.sections['commandPointer']: 206 write('\n'.join(self.sections['commandPointer']), file=self.outFile) 207 self.newline() 208 if self.sections['command']: 209 if self.genOpts.protectProto: 210 write(self.genOpts.protectProto, 211 self.genOpts.protectProtoStr, file=self.outFile) 212 write('\n'.join(self.sections['command']), end='', file=self.outFile) 213 if self.genOpts.protectProto: 214 write('#endif', file=self.outFile) 215 else: 216 self.newline() 217 if self.featureExtraProtect is not None: 218 write('#endif /*', self.featureExtraProtect, '*/', file=self.outFile) 219 if self.genOpts.protectFeature: 220 write('#endif /*', self.featureName, '*/', file=self.outFile) 221 # Finish processing in superclass 222 OutputGenerator.endFeature(self) 223 224 def appendSection(self, section, text): 225 "Append a definition to the specified section" 226 # self.sections[section].append('SECTION: ' + section + '\n') 227 self.sections[section].append(text) 228 self.feature_not_empty = True 229 230 def genType(self, typeinfo, name, alias): 231 "Generate type." 232 OutputGenerator.genType(self, typeinfo, name, alias) 233 typeElem = typeinfo.elem 234 235 # Vulkan: 236 # Determine the category of the type, and the type section to add 237 # its definition to. 238 # 'funcpointer' is added to the 'struct' section as a workaround for 239 # internal issue #877, since structures and function pointer types 240 # can have cross-dependencies. 241 category = typeElem.get('category') 242 if category == 'funcpointer': 243 section = 'struct' 244 else: 245 section = category 246 247 if category in ('struct', 'union'): 248 # If the type is a struct type, generate it using the 249 # special-purpose generator. 250 self.genStruct(typeinfo, name, alias) 251 else: 252 # OpenXR: this section was not under 'else:' previously, just fell through 253 if alias: 254 # If the type is an alias, just emit a typedef declaration 255 body = 'typedef ' + alias + ' ' + name + ';\n' 256 else: 257 # Replace <apientry /> tags with an APIENTRY-style string 258 # (from self.genOpts). Copy other text through unchanged. 259 # If the resulting text is an empty string, don't emit it. 260 body = noneStr(typeElem.text) 261 for elem in typeElem: 262 if elem.tag == 'apientry': 263 body += self.genOpts.apientry + noneStr(elem.tail) 264 else: 265 body += noneStr(elem.text) + noneStr(elem.tail) 266 if body: 267 # Add extra newline after multi-line entries. 268 if '\n' in body[0:-1]: 269 body += '\n' 270 self.appendSection(section, body) 271 272 def genProtectString(self, protect_str): 273 """Generate protection string. 274 275 Protection strings are the strings defining the OS/Platform/Graphics 276 requirements for a given OpenXR command. When generating the 277 language header files, we need to make sure the items specific to a 278 graphics API or OS platform are properly wrapped in #ifs.""" 279 protect_if_str = '' 280 protect_end_str = '' 281 if not protect_str: 282 return (protect_if_str, protect_end_str) 283 284 if ',' in protect_str: 285 protect_list = protect_str.split(",") 286 protect_defs = ('defined(%s)' % d for d in protect_list) 287 protect_def_str = ' && '.join(protect_defs) 288 protect_if_str = '#if %s\n' % protect_def_str 289 protect_end_str = '#endif // %s\n' % protect_def_str 290 else: 291 protect_if_str = '#ifdef %s\n' % protect_str 292 protect_end_str = '#endif // %s\n' % protect_str 293 294 return (protect_if_str, protect_end_str) 295 296 def typeMayAlias(self, typeName): 297 if not self.may_alias: 298 # First time we've asked if a type may alias. 299 # So, let's populate the set of all names of types that may. 300 301 # Everyone with an explicit mayalias="true" 302 self.may_alias = set(typeName 303 for typeName, data in self.registry.typedict.items() 304 if data.elem.get('mayalias') == 'true') 305 306 # Every type mentioned in some other type's parentstruct attribute. 307 parent_structs = (otherType.elem.get('parentstruct') 308 for otherType in self.registry.typedict.values()) 309 self.may_alias.update(set(x for x in parent_structs 310 if x is not None)) 311 return typeName in self.may_alias 312 313 def genStruct(self, typeinfo, typeName, alias): 314 """Generate struct (e.g. C "struct" type). 315 316 This is a special case of the <type> tag where the contents are 317 interpreted as a set of <member> tags instead of freeform C 318 C type declarations. The <member> tags are just like <param> 319 tags - they are a declaration of a struct or union member. 320 Only simple member declarations are supported (no nested 321 structs etc.) 322 323 If alias is not None, then this struct aliases another; just 324 generate a typedef of that alias.""" 325 OutputGenerator.genStruct(self, typeinfo, typeName, alias) 326 327 typeElem = typeinfo.elem 328 329 if alias: 330 body = 'typedef ' + alias + ' ' + typeName + ';\n' 331 else: 332 body = '' 333 (protect_begin, protect_end) = self.genProtectString(typeElem.get('protect')) 334 if protect_begin: 335 body += protect_begin 336 body += 'typedef ' + typeElem.get('category') 337 338 # This is an OpenXR-specific alternative where aliasing refers 339 # to an inheritance hierarchy of types rather than C-level type 340 # aliases. 341 if self.genOpts.genAliasMacro and self.typeMayAlias(typeName): 342 body += ' ' + self.genOpts.aliasMacro 343 344 body += ' ' + typeName + ' {\n' 345 346 targetLen = self.getMaxCParamTypeLength(typeinfo) 347 for member in typeElem.findall('.//member'): 348 body += self.makeCParamDecl(member, targetLen + 4) 349 body += ';\n' 350 body += '} ' + typeName + ';\n' 351 if protect_end: 352 body += protect_end 353 354 self.appendSection('struct', body) 355 356 def genGroup(self, groupinfo, groupName, alias=None): 357 """Generate groups (e.g. C "enum" type). 358 359 These are concatenated together with other types. 360 361 If alias is not None, it is the name of another group type 362 which aliases this type; just generate that alias.""" 363 OutputGenerator.genGroup(self, groupinfo, groupName, alias) 364 groupElem = groupinfo.elem 365 366 # After either enumerated type or alias paths, add the declaration 367 # to the appropriate section for the group being defined. 368 if groupElem.get('type') == 'bitmask': 369 section = 'bitmask' 370 else: 371 section = 'group' 372 373 if alias: 374 # If the group name is aliased, just emit a typedef declaration 375 # for the alias. 376 body = 'typedef ' + alias + ' ' + groupName + ';\n' 377 self.appendSection(section, body) 378 else: 379 (section, body) = self.buildEnumCDecl(self.genOpts.genEnumBeginEndRange, groupinfo, groupName) 380 self.appendSection(section, "\n" + body) 381 382 def genEnum(self, enuminfo, name, alias): 383 """Generate enumerants. 384 385 <enum> tags may specify their values in several ways, but are usually 386 just integers.""" 387 OutputGenerator.genEnum(self, enuminfo, name, alias) 388 (_, strVal) = self.enumToValue(enuminfo.elem, False) 389 body = '#define ' + name.ljust(33) + ' ' + strVal 390 self.appendSection('enum', body) 391 392 def genCmd(self, cmdinfo, name, alias): 393 "Command generation" 394 OutputGenerator.genCmd(self, cmdinfo, name, alias) 395 396 # if alias: 397 # prefix = '// ' + name + ' is an alias of command ' + alias + '\n' 398 # else: 399 # prefix = '' 400 401 prefix = '' 402 decls = self.makeCDecls(cmdinfo.elem) 403 self.appendSection('command', prefix + decls[0] + '\n') 404 if self.genOpts.genFuncPointers: 405 self.appendSection('commandPointer', decls[1]) 406