1 #!/usr/bin/python3 -i 2 # 3 # Copyright (c) 2013-2018 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 17 import os,re,sys 18 from 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 # indentFuncProto - True if prototype declarations should put each 48 # parameter on a separate line 49 # indentFuncPointer - True if typedefed function pointers should put each 50 # parameter on a separate line 51 # alignFuncParam - if nonzero and parameters are being put on a 52 # separate line, align parameter names at the specified column 53 class CGeneratorOptions(GeneratorOptions): 54 """Represents options during C interface generation for headers""" 55 def __init__(self, 56 filename = None, 57 directory = '.', 58 apiname = None, 59 profile = None, 60 versions = '.*', 61 emitversions = '.*', 62 defaultExtensions = None, 63 addExtensions = None, 64 removeExtensions = None, 65 sortProcedure = regSortFeatures, 66 prefixText = "", 67 genFuncPointers = True, 68 protectFile = True, 69 protectFeature = True, 70 protectProto = None, 71 protectProtoStr = None, 72 apicall = '', 73 apientry = '', 74 apientryp = '', 75 indentFuncProto = True, 76 indentFuncPointer = False, 77 alignFuncParam = 0): 78 GeneratorOptions.__init__(self, filename, directory, apiname, profile, 79 versions, emitversions, defaultExtensions, 80 addExtensions, removeExtensions, sortProcedure) 81 self.prefixText = prefixText 82 self.genFuncPointers = genFuncPointers 83 self.protectFile = protectFile 84 self.protectFeature = protectFeature 85 self.protectProto = protectProto 86 self.protectProtoStr = protectProtoStr 87 self.apicall = apicall 88 self.apientry = apientry 89 self.apientryp = apientryp 90 self.indentFuncProto = indentFuncProto 91 self.indentFuncPointer = indentFuncPointer 92 self.alignFuncParam = alignFuncParam 93 94 # COutputGenerator - subclass of OutputGenerator. 95 # Generates C-language API interfaces. 96 # 97 # ---- methods ---- 98 # COutputGenerator(errFile, warnFile, diagFile) - args as for 99 # OutputGenerator. Defines additional internal state. 100 # ---- methods overriding base class ---- 101 # beginFile(genOpts) 102 # endFile() 103 # beginFeature(interface, emit) 104 # endFeature() 105 # genType(typeinfo,name) 106 # genStruct(typeinfo,name) 107 # genGroup(groupinfo,name) 108 # genEnum(enuminfo, name) 109 # genCmd(cmdinfo) 110 class COutputGenerator(OutputGenerator): 111 """Generate specified API interfaces in a specific style, such as a C header""" 112 # This is an ordered list of sections in the header file. 113 TYPE_SECTIONS = ['include', 'define', 'basetype', 'handle', 'enum', 114 'group', 'bitmask', 'funcpointer', 'struct'] 115 ALL_SECTIONS = TYPE_SECTIONS + ['commandPointer', 'command'] 116 def __init__(self, 117 errFile = sys.stderr, 118 warnFile = sys.stderr, 119 diagFile = sys.stdout): 120 OutputGenerator.__init__(self, errFile, warnFile, diagFile) 121 # Internal state - accumulators for different inner block text 122 self.sections = dict([(section, []) for section in self.ALL_SECTIONS]) 123 # 124 def beginFile(self, genOpts): 125 OutputGenerator.beginFile(self, genOpts) 126 # C-specific 127 # 128 # Multiple inclusion protection & C++ wrappers. 129 if (genOpts.protectFile and self.genOpts.filename): 130 headerSym = re.sub('\.h', '_h_', 131 os.path.basename(self.genOpts.filename)).upper() 132 write('#ifndef', headerSym, file=self.outFile) 133 write('#define', headerSym, '1', file=self.outFile) 134 self.newline() 135 write('#ifdef __cplusplus', file=self.outFile) 136 write('extern "C" {', file=self.outFile) 137 write('#endif', file=self.outFile) 138 self.newline() 139 # 140 # User-supplied prefix text, if any (list of strings) 141 if (genOpts.prefixText): 142 for s in genOpts.prefixText: 143 write(s, file=self.outFile) 144 # 145 # Some boilerplate describing what was generated - this 146 # will probably be removed later since the extensions 147 # pattern may be very long. 148 # write('/* Generated C header for:', file=self.outFile) 149 # write(' * API:', genOpts.apiname, file=self.outFile) 150 # if (genOpts.profile): 151 # write(' * Profile:', genOpts.profile, file=self.outFile) 152 # write(' * Versions considered:', genOpts.versions, file=self.outFile) 153 # write(' * Versions emitted:', genOpts.emitversions, file=self.outFile) 154 # write(' * Default extensions included:', genOpts.defaultExtensions, file=self.outFile) 155 # write(' * Additional extensions included:', genOpts.addExtensions, file=self.outFile) 156 # write(' * Extensions removed:', genOpts.removeExtensions, file=self.outFile) 157 # write(' */', file=self.outFile) 158 def endFile(self): 159 # C-specific 160 # Finish C++ wrapper and multiple inclusion protection 161 self.newline() 162 write('#ifdef __cplusplus', file=self.outFile) 163 write('}', file=self.outFile) 164 write('#endif', file=self.outFile) 165 if (self.genOpts.protectFile and self.genOpts.filename): 166 self.newline() 167 write('#endif', file=self.outFile) 168 # Finish processing in superclass 169 OutputGenerator.endFile(self) 170 def beginFeature(self, interface, emit): 171 # Start processing in superclass 172 OutputGenerator.beginFeature(self, interface, emit) 173 # C-specific 174 # Accumulate includes, defines, types, enums, function pointer typedefs, 175 # end function prototypes separately for this feature. They're only 176 # printed in endFeature(). 177 self.sections = dict([(section, []) for section in self.ALL_SECTIONS]) 178 def endFeature(self): 179 # C-specific 180 # Actually write the interface to the output file. 181 if (self.emit): 182 self.newline() 183 if (self.genOpts.protectFeature): 184 write('#ifndef', self.featureName, file=self.outFile) 185 # If type declarations are needed by other features based on 186 # this one, it may be necessary to suppress the ExtraProtect, 187 # or move it below the 'for section...' loop. 188 if (self.featureExtraProtect != None): 189 write('#ifdef', self.featureExtraProtect, file=self.outFile) 190 write('#define', self.featureName, '1', file=self.outFile) 191 for section in self.TYPE_SECTIONS: 192 contents = self.sections[section] 193 if contents: 194 write('\n'.join(contents), file=self.outFile) 195 self.newline() 196 if (self.genOpts.genFuncPointers and self.sections['commandPointer']): 197 write('\n'.join(self.sections['commandPointer']), file=self.outFile) 198 self.newline() 199 if (self.sections['command']): 200 if (self.genOpts.protectProto): 201 write(self.genOpts.protectProto, 202 self.genOpts.protectProtoStr, file=self.outFile) 203 write('\n'.join(self.sections['command']), end='', file=self.outFile) 204 if (self.genOpts.protectProto): 205 write('#endif', file=self.outFile) 206 else: 207 self.newline() 208 if (self.featureExtraProtect != None): 209 write('#endif /*', self.featureExtraProtect, '*/', file=self.outFile) 210 if (self.genOpts.protectFeature): 211 write('#endif /*', self.featureName, '*/', file=self.outFile) 212 # Finish processing in superclass 213 OutputGenerator.endFeature(self) 214 # 215 # Append a definition to the specified section 216 def appendSection(self, section, text): 217 # self.sections[section].append('SECTION: ' + section + '\n') 218 self.sections[section].append(text) 219 # 220 # Type generation 221 def genType(self, typeinfo, name): 222 OutputGenerator.genType(self, typeinfo, name) 223 typeElem = typeinfo.elem 224 # If the type is a struct type, traverse the imbedded <member> tags 225 # generating a structure. Otherwise, emit the tag text. 226 category = typeElem.get('category') 227 if (category == 'struct' or category == 'union'): 228 self.genStruct(typeinfo, name) 229 else: 230 # Replace <apientry /> tags with an APIENTRY-style string 231 # (from self.genOpts). Copy other text through unchanged. 232 # If the resulting text is an empty string, don't emit it. 233 s = noneStr(typeElem.text) 234 for elem in typeElem: 235 if (elem.tag == 'apientry'): 236 s += self.genOpts.apientry + noneStr(elem.tail) 237 else: 238 s += noneStr(elem.text) + noneStr(elem.tail) 239 if s: 240 # Add extra newline after multi-line entries. 241 if '\n' in s: 242 s += '\n' 243 # This is a temporary workaround for internal issue #877, 244 # while we consider other approaches. The problem is that 245 # function pointer types can have dependencies on structures 246 # and vice-versa, so they can't be strictly separated into 247 # sections. The workaround is to define those types in the 248 # same section, in dependency order. 249 if (category == 'funcpointer'): 250 self.appendSection('struct', s) 251 else: 252 self.appendSection(category, s) 253 # 254 # Struct (e.g. C "struct" type) generation. 255 # This is a special case of the <type> tag where the contents are 256 # interpreted as a set of <member> tags instead of freeform C 257 # C type declarations. The <member> tags are just like <param> 258 # tags - they are a declaration of a struct or union member. 259 # Only simple member declarations are supported (no nested 260 # structs etc.) 261 def genStruct(self, typeinfo, typeName): 262 OutputGenerator.genStruct(self, typeinfo, typeName) 263 body = 'typedef ' + typeinfo.elem.get('category') + ' ' + typeName + ' {\n' 264 # paramdecl = self.makeCParamDecl(typeinfo.elem, self.genOpts.alignFuncParam) 265 targetLen = 0; 266 for member in typeinfo.elem.findall('.//member'): 267 targetLen = max(targetLen, self.getCParamTypeLength(member)) 268 for member in typeinfo.elem.findall('.//member'): 269 body += self.makeCParamDecl(member, targetLen + 4) 270 body += ';\n' 271 body += '} ' + typeName + ';\n' 272 self.appendSection('struct', body) 273 # 274 # Group (e.g. C "enum" type) generation. 275 # These are concatenated together with other types. 276 def genGroup(self, groupinfo, groupName): 277 OutputGenerator.genGroup(self, groupinfo, groupName) 278 groupElem = groupinfo.elem 279 280 expandName = re.sub(r'([0-9a-z_])([A-Z0-9])',r'\1_\2',groupName).upper() 281 282 expandPrefix = expandName 283 expandSuffix = '' 284 expandSuffixMatch = re.search(r'[A-Z][A-Z]+$',groupName) 285 if expandSuffixMatch: 286 expandSuffix = '_' + expandSuffixMatch.group() 287 # Strip off the suffix from the prefix 288 expandPrefix = expandName.rsplit(expandSuffix, 1)[0] 289 290 # Prefix 291 body = "\ntypedef enum " + groupName + " {\n" 292 293 # @@ Should use the type="bitmask" attribute instead 294 isEnum = ('FLAG_BITS' not in expandPrefix) 295 296 # Loop over the nested 'enum' tags. Keep track of the minimum and 297 # maximum numeric values, if they can be determined; but only for 298 # core API enumerants, not extension enumerants. This is inferred 299 # by looking for 'extends' attributes. 300 minName = None 301 for elem in groupElem.findall('enum'): 302 # Convert the value to an integer and use that to track min/max. 303 # Values of form -(number) are accepted but nothing more complex. 304 # Should catch exceptions here for more complex constructs. Not yet. 305 (numVal,strVal) = self.enumToValue(elem, True) 306 name = elem.get('name') 307 308 # Check for duplicate enum values and raise an error if found. 309 for elem2 in groupElem.findall('enum'): 310 if (elem != elem2): 311 (numVal2,strVal2) = self.enumToValue(elem2, True) 312 if (numVal2 == numVal): 313 raise UserWarning('Duplicate enum ' + name + ' = ' + elem2.get('name') + ' = ' + strVal) 314 315 # Extension enumerants are only included if they are required 316 if (self.isEnumRequired(elem)): 317 body += " " + name + " = " + strVal + ",\n" 318 319 if (isEnum and elem.get('extends') is None): 320 if (minName == None): 321 minName = maxName = name 322 minValue = maxValue = numVal 323 elif (numVal < minValue): 324 minName = name 325 minValue = numVal 326 elif (numVal > maxValue): 327 maxName = name 328 maxValue = numVal 329 # Generate min/max value tokens and a range-padding enum. Need some 330 # additional padding to generate correct names... 331 if isEnum: 332 body += " " + expandPrefix + "_BEGIN_RANGE" + expandSuffix + " = " + minName + ",\n" 333 body += " " + expandPrefix + "_END_RANGE" + expandSuffix + " = " + maxName + ",\n" 334 body += " " + expandPrefix + "_RANGE_SIZE" + expandSuffix + " = (" + maxName + " - " + minName + " + 1),\n" 335 336 body += " " + expandPrefix + "_MAX_ENUM" + expandSuffix + " = 0x7FFFFFFF\n" 337 338 # Postfix 339 body += "} " + groupName + ";" 340 if groupElem.get('type') == 'bitmask': 341 section = 'bitmask' 342 else: 343 section = 'group' 344 self.appendSection(section, body) 345 # Enumerant generation 346 # <enum> tags may specify their values in several ways, but are usually 347 # just integers. 348 def genEnum(self, enuminfo, name): 349 OutputGenerator.genEnum(self, enuminfo, name) 350 (numVal,strVal) = self.enumToValue(enuminfo.elem, False) 351 body = '#define ' + name.ljust(33) + ' ' + strVal 352 self.appendSection('enum', body) 353 # 354 # Command generation 355 def genCmd(self, cmdinfo, name): 356 OutputGenerator.genCmd(self, cmdinfo, name) 357 # 358 decls = self.makeCDecls(cmdinfo.elem) 359 self.appendSection('command', decls[0] + '\n') 360 if (self.genOpts.genFuncPointers): 361 self.appendSection('commandPointer', decls[1]) 362