blob: 298a7742a82ff128dd99acff329aafd215b6c165 [file] [log] [blame]
Mark Lobodzinskif96a9322017-02-13 10:33:08 -07001#!/usr/bin/python3 -i
2#
3# Copyright (c) 2013-2017 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
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# 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
53class 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)
110class 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 self.appendSection(category, s)
244 #
245 # Struct (e.g. C "struct" type) generation.
246 # This is a special case of the <type> tag where the contents are
247 # interpreted as a set of <member> tags instead of freeform C
248 # C type declarations. The <member> tags are just like <param>
249 # tags - they are a declaration of a struct or union member.
250 # Only simple member declarations are supported (no nested
251 # structs etc.)
252 def genStruct(self, typeinfo, typeName):
253 OutputGenerator.genStruct(self, typeinfo, typeName)
254 body = 'typedef ' + typeinfo.elem.get('category') + ' ' + typeName + ' {\n'
255 # paramdecl = self.makeCParamDecl(typeinfo.elem, self.genOpts.alignFuncParam)
256 targetLen = 0;
257 for member in typeinfo.elem.findall('.//member'):
258 targetLen = max(targetLen, self.getCParamTypeLength(member))
259 for member in typeinfo.elem.findall('.//member'):
260 body += self.makeCParamDecl(member, targetLen + 4)
261 body += ';\n'
262 body += '} ' + typeName + ';\n'
263 self.appendSection('struct', body)
264 #
265 # Group (e.g. C "enum" type) generation.
266 # These are concatenated together with other types.
267 def genGroup(self, groupinfo, groupName):
268 OutputGenerator.genGroup(self, groupinfo, groupName)
269 groupElem = groupinfo.elem
270
Mark Lobodzinski1d218cc2017-03-14 10:12:43 -0600271 expandName = re.sub(r'([0-9a-z_])([A-Z0-9])',r'\1_\2',groupName).upper()
Mark Lobodzinskif96a9322017-02-13 10:33:08 -0700272
273 expandPrefix = expandName
274 expandSuffix = ''
275 expandSuffixMatch = re.search(r'[A-Z][A-Z]+$',groupName)
276 if expandSuffixMatch:
277 expandSuffix = '_' + expandSuffixMatch.group()
278 # Strip off the suffix from the prefix
279 expandPrefix = expandName.rsplit(expandSuffix, 1)[0]
280
281 # Prefix
282 body = "\ntypedef enum " + groupName + " {\n"
283
284 # @@ Should use the type="bitmask" attribute instead
285 isEnum = ('FLAG_BITS' not in expandPrefix)
286
287 # Loop over the nested 'enum' tags. Keep track of the minimum and
288 # maximum numeric values, if they can be determined; but only for
289 # core API enumerants, not extension enumerants. This is inferred
290 # by looking for 'extends' attributes.
291 minName = None
292 for elem in groupElem.findall('enum'):
293 # Convert the value to an integer and use that to track min/max.
294 # Values of form -(number) are accepted but nothing more complex.
295 # Should catch exceptions here for more complex constructs. Not yet.
296 (numVal,strVal) = self.enumToValue(elem, True)
297 name = elem.get('name')
298
299 # Extension enumerants are only included if they are required
300 if (self.isEnumRequired(elem)):
301 body += " " + name + " = " + strVal + ",\n"
302
303 if (isEnum and elem.get('extends') is None):
304 if (minName == None):
305 minName = maxName = name
306 minValue = maxValue = numVal
307 elif (numVal < minValue):
308 minName = name
309 minValue = numVal
310 elif (numVal > maxValue):
311 maxName = name
312 maxValue = numVal
313 # Generate min/max value tokens and a range-padding enum. Need some
314 # additional padding to generate correct names...
315 if isEnum:
316 body += " " + expandPrefix + "_BEGIN_RANGE" + expandSuffix + " = " + minName + ",\n"
317 body += " " + expandPrefix + "_END_RANGE" + expandSuffix + " = " + maxName + ",\n"
318 body += " " + expandPrefix + "_RANGE_SIZE" + expandSuffix + " = (" + maxName + " - " + minName + " + 1),\n"
319
320 body += " " + expandPrefix + "_MAX_ENUM" + expandSuffix + " = 0x7FFFFFFF\n"
321
322 # Postfix
323 body += "} " + groupName + ";"
324 if groupElem.get('type') == 'bitmask':
325 section = 'bitmask'
326 else:
327 section = 'group'
328 self.appendSection(section, body)
329 # Enumerant generation
330 # <enum> tags may specify their values in several ways, but are usually
331 # just integers.
332 def genEnum(self, enuminfo, name):
333 OutputGenerator.genEnum(self, enuminfo, name)
334 (numVal,strVal) = self.enumToValue(enuminfo.elem, False)
335 body = '#define ' + name.ljust(33) + ' ' + strVal
336 self.appendSection('enum', body)
337 #
338 # Command generation
339 def genCmd(self, cmdinfo, name):
340 OutputGenerator.genCmd(self, cmdinfo, name)
341 #
342 decls = self.makeCDecls(cmdinfo.elem)
343 self.appendSection('command', decls[0] + '\n')
344 if (self.genOpts.genFuncPointers):
345 self.appendSection('commandPointer', decls[1])