comparison fuhtark_test/Vulkan-Headers-1.4.334/registry/cgenerator.py @ 1501:f40d9d814c08 default tip

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