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