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