1#!/usr/bin/python3 -i 2# 3# Copyright (c) 2013-2019 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,re,sys,pdb 18from generator import * 19 20# CGeneratorOptions - subclass of GeneratorOptions. 21# 22# Adds options used by COutputGenerator objects during C language header 23# generation. 24# 25# Additional members 26# prefixText - list of strings to prefix generated header with 27# (usually a copyright statement + calling convention macros). 28# protectFile - True if multiple inclusion protection should be 29# generated (based on the filename) around the entire header. 30# protectFeature - True if #ifndef..#endif protection should be 31# generated around a feature interface in the header file. 32# genFuncPointers - True if function pointer typedefs should be 33# generated 34# protectProto - If conditional protection should be generated 35# around prototype declarations, set to either '#ifdef' 36# to require opt-in (#ifdef protectProtoStr) or '#ifndef' 37# to require opt-out (#ifndef protectProtoStr). Otherwise 38# set to None. 39# protectProtoStr - #ifdef/#ifndef symbol to use around prototype 40# declarations, if protectProto is set 41# apicall - string to use for the function declaration prefix, 42# such as APICALL on Windows. 43# apientry - string to use for the calling convention macro, 44# in typedefs, such as APIENTRY. 45# apientryp - string to use for the calling convention macro 46# in function pointer typedefs, such as APIENTRYP. 47# directory - directory into which to generate include files 48# indentFuncProto - True if prototype declarations should put each 49# parameter on a separate line 50# indentFuncPointer - True if typedefed function pointers should put each 51# parameter on a separate line 52# alignFuncParam - if nonzero and parameters are being put on a 53# separate line, align parameter names at the specified column 54class CGeneratorOptions(GeneratorOptions): 55 """Represents options during C interface generation for headers""" 56 def __init__(self, 57 filename = None, 58 directory = '.', 59 apiname = None, 60 profile = None, 61 versions = '.*', 62 emitversions = '.*', 63 defaultExtensions = None, 64 addExtensions = None, 65 removeExtensions = None, 66 emitExtensions = None, 67 sortProcedure = regSortFeatures, 68 prefixText = "", 69 genFuncPointers = True, 70 protectFile = True, 71 protectFeature = True, 72 protectProto = None, 73 protectProtoStr = None, 74 apicall = '', 75 apientry = '', 76 apientryp = '', 77 indentFuncProto = True, 78 indentFuncPointer = False, 79 alignFuncParam = 0): 80 GeneratorOptions.__init__(self, filename, directory, apiname, profile, 81 versions, emitversions, defaultExtensions, 82 addExtensions, removeExtensions, 83 emitExtensions, sortProcedure) 84 self.prefixText = prefixText 85 self.genFuncPointers = genFuncPointers 86 self.protectFile = protectFile 87 self.protectFeature = protectFeature 88 self.protectProto = protectProto 89 self.protectProtoStr = protectProtoStr 90 self.apicall = apicall 91 self.apientry = apientry 92 self.apientryp = apientryp 93 self.indentFuncProto = indentFuncProto 94 self.indentFuncPointer = indentFuncPointer 95 self.alignFuncParam = alignFuncParam 96 97# COutputGenerator - subclass of OutputGenerator. 98# Generates C-language API interfaces. 99# 100# ---- methods ---- 101# COutputGenerator(errFile, warnFile, diagFile) - args as for 102# OutputGenerator. Defines additional internal state. 103# ---- methods overriding base class ---- 104# beginFile(genOpts) 105# endFile() 106# beginFeature(interface, emit) 107# endFeature() 108# genType(typeinfo,name) 109# genStruct(typeinfo,name) 110# genGroup(groupinfo,name) 111# genEnum(enuminfo, name) 112# genCmd(cmdinfo) 113class COutputGenerator(OutputGenerator): 114 """Generate specified API interfaces in a specific style, such as a C header""" 115 # This is an ordered list of sections in the header file. 116 TYPE_SECTIONS = ['include', 'define', 'basetype', 'handle', 'enum', 117 'group', 'bitmask', 'funcpointer', 'struct'] 118 ALL_SECTIONS = TYPE_SECTIONS + ['commandPointer', 'command'] 119 def __init__(self, 120 errFile = sys.stderr, 121 warnFile = sys.stderr, 122 diagFile = sys.stdout): 123 OutputGenerator.__init__(self, errFile, warnFile, diagFile) 124 # Internal state - accumulators for different inner block text 125 self.sections = dict([(section, []) for section in self.ALL_SECTIONS]) 126 # 127 def beginFile(self, genOpts): 128 OutputGenerator.beginFile(self, genOpts) 129 # C-specific 130 # 131 # Multiple inclusion protection & C++ wrappers. 132 if (genOpts.protectFile and self.genOpts.filename): 133 headerSym = re.sub('\.h', '_h_', 134 os.path.basename(self.genOpts.filename)).upper() 135 write('#ifndef', headerSym, file=self.outFile) 136 write('#define', headerSym, '1', file=self.outFile) 137 self.newline() 138 write('#ifdef __cplusplus', file=self.outFile) 139 write('extern "C" {', file=self.outFile) 140 write('#endif', file=self.outFile) 141 self.newline() 142 # 143 # User-supplied prefix text, if any (list of strings) 144 if (genOpts.prefixText): 145 for s in genOpts.prefixText: 146 write(s, file=self.outFile) 147 # 148 # Some boilerplate describing what was generated - this 149 # will probably be removed later since the extensions 150 # pattern may be very long. 151 # write('/* Generated C header for:', file=self.outFile) 152 # write(' * API:', genOpts.apiname, file=self.outFile) 153 # if (genOpts.profile): 154 # write(' * Profile:', genOpts.profile, file=self.outFile) 155 # write(' * Versions considered:', genOpts.versions, file=self.outFile) 156 # write(' * Versions emitted:', genOpts.emitversions, file=self.outFile) 157 # write(' * Default extensions included:', genOpts.defaultExtensions, file=self.outFile) 158 # write(' * Additional extensions included:', genOpts.addExtensions, file=self.outFile) 159 # write(' * Extensions removed:', genOpts.removeExtensions, file=self.outFile) 160 # write(' * Extensions emitted:', genOpts.emitExtensions, file=self.outFile) 161 # write(' */', file=self.outFile) 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 def beginFeature(self, interface, emit): 175 # Start processing in superclass 176 OutputGenerator.beginFeature(self, interface, emit) 177 # C-specific 178 # Accumulate includes, defines, types, enums, function pointer typedefs, 179 # end function prototypes separately for this feature. They're only 180 # printed in endFeature(). 181 self.sections = dict([(section, []) for section in self.ALL_SECTIONS]) 182 def endFeature(self): 183 # C-specific 184 # Actually write the interface to the output file. 185 if (self.emit): 186 self.newline() 187 if (self.genOpts.protectFeature): 188 write('#ifndef', self.featureName, file=self.outFile) 189 # If type declarations are needed by other features based on 190 # this one, it may be necessary to suppress the ExtraProtect, 191 # or move it below the 'for section...' loop. 192 if (self.featureExtraProtect != None): 193 write('#ifdef', self.featureExtraProtect, file=self.outFile) 194 write('#define', self.featureName, '1', file=self.outFile) 195 for section in self.TYPE_SECTIONS: 196 contents = self.sections[section] 197 if contents: 198 write('\n'.join(contents), file=self.outFile) 199 self.newline() 200 if (self.genOpts.genFuncPointers and self.sections['commandPointer']): 201 write('\n'.join(self.sections['commandPointer']), file=self.outFile) 202 self.newline() 203 if (self.sections['command']): 204 if (self.genOpts.protectProto): 205 write(self.genOpts.protectProto, 206 self.genOpts.protectProtoStr, file=self.outFile) 207 write('\n'.join(self.sections['command']), end='', file=self.outFile) 208 if (self.genOpts.protectProto): 209 write('#endif', file=self.outFile) 210 else: 211 self.newline() 212 if (self.featureExtraProtect != None): 213 write('#endif /*', self.featureExtraProtect, '*/', file=self.outFile) 214 if (self.genOpts.protectFeature): 215 write('#endif /*', self.featureName, '*/', file=self.outFile) 216 # Finish processing in superclass 217 OutputGenerator.endFeature(self) 218 # 219 # Append a definition to the specified section 220 def appendSection(self, section, text): 221 # self.sections[section].append('SECTION: ' + section + '\n') 222 self.sections[section].append(text) 223 # self.logMsg('diag', 'appendSection(section =', section, 'text =', text) 224 # 225 # Type generation 226 def genType(self, typeinfo, name, alias): 227 OutputGenerator.genType(self, typeinfo, name, alias) 228 typeElem = typeinfo.elem 229 230 # Determine the category of the type, and the type section to add 231 # its definition to. 232 # 'funcpointer' is added to the 'struct' section as a workaround for 233 # internal issue #877, since structures and function pointer types 234 # can have cross-dependencies. 235 category = typeElem.get('category') 236 if category == 'funcpointer': 237 section = 'struct' 238 else: 239 section = category 240 241 if category == 'struct' or category == 'union': 242 # If the type is a struct type, generate it using the 243 # special-purpose generator. 244 self.genStruct(typeinfo, name, alias) 245 else: 246 if alias: 247 # If the type is an alias, just emit a typedef declaration 248 body = 'typedef ' + alias + ' ' + name + ';\n' 249 else: 250 # Replace <apientry /> tags with an APIENTRY-style string 251 # (from self.genOpts). Copy other text through unchanged. 252 # If the resulting text is an empty string, don't emit it. 253 body = noneStr(typeElem.text) 254 for elem in typeElem: 255 if (elem.tag == 'apientry'): 256 body += self.genOpts.apientry + noneStr(elem.tail) 257 else: 258 body += noneStr(elem.text) + noneStr(elem.tail) 259 260 if body: 261 # Add extra newline after multi-line entries. 262 if '\n' in body[0:-1]: 263 body += '\n' 264 self.appendSection(section, body) 265 # 266 # Struct (e.g. C "struct" type) generation. 267 # This is a special case of the <type> tag where the contents are 268 # interpreted as a set of <member> tags instead of freeform C 269 # C type declarations. The <member> tags are just like <param> 270 # tags - they are a declaration of a struct or union member. 271 # Only simple member declarations are supported (no nested 272 # structs etc.) 273 # If alias != None, then this struct aliases another; just 274 # generate a typedef of that alias. 275 def genStruct(self, typeinfo, typeName, alias): 276 OutputGenerator.genStruct(self, typeinfo, typeName, alias) 277 278 typeElem = typeinfo.elem 279 280 if alias: 281 body = 'typedef ' + alias + ' ' + typeName + ';\n' 282 else: 283 body = 'typedef ' + typeElem.get('category') + ' ' + typeName + ' {\n' 284 285 targetLen = 0; 286 for member in typeElem.findall('.//member'): 287 targetLen = max(targetLen, self.getCParamTypeLength(member)) 288 for member in typeElem.findall('.//member'): 289 body += self.makeCParamDecl(member, targetLen + 4) 290 body += ';\n' 291 body += '} ' + typeName + ';\n' 292 293 self.appendSection('struct', body) 294 # 295 # Group (e.g. C "enum" type) generation. 296 # These are concatenated together with other types. 297 # If alias != None, it is the name of another group type 298 # which aliases this type; just generate that alias. 299 def genGroup(self, groupinfo, groupName, alias = None): 300 OutputGenerator.genGroup(self, groupinfo, groupName, alias) 301 groupElem = groupinfo.elem 302 303 if alias: 304 # If the group name is aliased, just emit a typedef declaration 305 # for the alias. 306 body = 'typedef ' + alias + ' ' + groupName + ';\n' 307 else: 308 self.logMsg('diag', 'CGenerator.genGroup group =', groupName, 'alias =', alias) 309 310 # Otherwise, emit an actual enumerated type declaration 311 expandName = re.sub(r'([0-9a-z_])([A-Z0-9])',r'\1_\2',groupName).upper() 312 313 expandPrefix = expandName 314 expandSuffix = '' 315 expandSuffixMatch = re.search(r'[A-Z][A-Z]+$',groupName) 316 if expandSuffixMatch: 317 expandSuffix = '_' + expandSuffixMatch.group() 318 # Strip off the suffix from the prefix 319 expandPrefix = expandName.rsplit(expandSuffix, 1)[0] 320 321 # Prefix 322 body = "\ntypedef enum " + groupName + " {\n" 323 324 # @@ Should use the type="bitmask" attribute instead 325 isEnum = ('FLAG_BITS' not in expandPrefix) 326 327 # Get a list of nested 'enum' tags. 328 enums = groupElem.findall('enum') 329 330 # Check for and report duplicates, and return a list with them 331 # removed. 332 enums = self.checkDuplicateEnums(enums) 333 334 # Loop over the nested 'enum' tags. Keep track of the minimum and 335 # maximum numeric values, if they can be determined; but only for 336 # core API enumerants, not extension enumerants. This is inferred 337 # by looking for 'extends' attributes. 338 minName = None 339 340 # Accumulate non-numeric enumerant values separately and append 341 # them following the numeric values, to allow for aliases. 342 # NOTE: this doesn't do a topological sort yet, so aliases of 343 # aliases can still get in the wrong order. 344 aliasText = "" 345 346 for elem in enums: 347 # Convert the value to an integer and use that to track min/max. 348 (numVal,strVal) = self.enumToValue(elem, True) 349 name = elem.get('name') 350 351 # Extension enumerants are only included if they are required 352 if self.isEnumRequired(elem): 353 decl = " " + name + " = " + strVal + ",\n" 354 if numVal != None: 355 body += decl 356 else: 357 aliasText += decl 358 359 # Don't track min/max for non-numbers (numVal == None) 360 if isEnum and numVal != None and elem.get('extends') is None: 361 if minName == None: 362 minName = maxName = name 363 minValue = maxValue = numVal 364 elif numVal < minValue: 365 minName = name 366 minValue = numVal 367 elif numVal > maxValue: 368 maxName = name 369 maxValue = numVal 370 371 # Now append the non-numeric enumerant values 372 body += aliasText 373 374 # Generate min/max value tokens and a range-padding enum. Need some 375 # additional padding to generate correct names... 376 if isEnum: 377 body += " " + expandPrefix + "_BEGIN_RANGE" + expandSuffix + " = " + minName + ",\n" 378 body += " " + expandPrefix + "_END_RANGE" + expandSuffix + " = " + maxName + ",\n" 379 body += " " + expandPrefix + "_RANGE_SIZE" + expandSuffix + " = (" + maxName + " - " + minName + " + 1),\n" 380 381 body += " " + expandPrefix + "_MAX_ENUM" + expandSuffix + " = 0x7FFFFFFF\n" 382 383 # Postfix 384 body += "} " + groupName + ";" 385 386 # After either enumerated type or alias paths, add the declaration 387 # to the appropriate section for the group being defined. 388 if groupElem.get('type') == 'bitmask': 389 section = 'bitmask' 390 else: 391 section = 'group' 392 self.appendSection(section, body) 393 394 # Enumerant generation 395 # <enum> tags may specify their values in several ways, but are usually 396 # just integers. 397 def genEnum(self, enuminfo, name, alias): 398 OutputGenerator.genEnum(self, enuminfo, name, alias) 399 (numVal,strVal) = self.enumToValue(enuminfo.elem, False) 400 body = '#define ' + name.ljust(33) + ' ' + strVal 401 self.appendSection('enum', body) 402 403 # 404 # Command generation 405 def genCmd(self, cmdinfo, name, alias): 406 OutputGenerator.genCmd(self, cmdinfo, name, alias) 407 408 # if alias: 409 # prefix = '// ' + name + ' is an alias of command ' + alias + '\n' 410 # else: 411 # prefix = '' 412 413 prefix = '' 414 decls = self.makeCDecls(cmdinfo.elem) 415 self.appendSection('command', prefix + decls[0] + '\n') 416 if (self.genOpts.genFuncPointers): 417 self.appendSection('commandPointer', decls[1]) 418