11#!/usr/bin/env python3 -i
22#
3- # Copyright 2013-2025 The Khronos Group Inc.
3+ # Copyright 2013-2026 The Khronos Group Inc.
44#
55# SPDX-License-Identifier: Apache-2.0
66"""Base class for source/header/doc generators, as well as some utility functions."""
@@ -51,65 +51,50 @@ def enquote(s):
5151 return None
5252
5353
54- def regSortCategoryKey (feature ):
55- """Sort key for regSortFeatures.
56- Sorts by category of the feature name string:
54+ def regSortFeatures (orderedFeatureNames , features ):
55+ """Default sort procedure for generated features.
5756
58- - Core API features (those defined with a `<feature>` tag)
59- - (sort VKSC after VK - this is Vulkan-specific)
60- - ARB/KHR/OES (Khronos extensions)
61- - other (EXT/vendor extensions)"""
62-
63- if feature .elem .tag == 'feature' :
64- if feature .name .startswith ('VKSC' ):
65- return 0.5
66- else :
67- return 0
68-
69- if feature .category .upper () in ('ARB' , 'KHR' , 'OES' ):
70- return 1
71-
72- return 2
73-
74-
75- def regSortOrderKey (feature ):
76- """Sort key for regSortFeatures - key is the sortorder attribute."""
77-
78- return feature .sortorder
79-
80-
81- def regSortNameKey (feature ):
82- """Sort key for regSortFeatures - key is the extension name."""
83-
84- return feature .name
57+ - orderedFeatureNames - list of feature / extension names to sort
58+ - features - dictionary of FeatureInfo keyed by names
8559
60+ - Sorts by explicit sort order (default 0) relative to other features
61+ - then by feature category ('feature' or 'extension'),
62+ - then by version number (for features)
63+ - then by extension number (for extensions)"""
8664
87- def regSortFeatureVersionKey (feature ):
88- """Sort key for regSortFeatures - key is the feature version.
89- `<extension>` elements all have version number 0."""
65+ # Sort by extension numbers; <feature>s all have extension number 0
66+ orderedFeatureNames .sort (key = lambda name : int (features [name ].number ))
9067
91- return float (feature .versionNumber )
68+ # Sort by feature version; <extension>s all have version number 0
69+ orderedFeatureNames .sort (key = lambda name : float (features [name ].versionNumber ))
9270
71+ # Sort by feature name category
72+ def categoryKey (feature ):
73+ """Helper function, too long to put in a sort key lambda
9374
94- def regSortExtensionNumberKey (feature ):
95- """Sort key for regSortFeatures - key is the extension number.
96- `<feature>` elements all have extension number 0."""
75+ - feature - FeatureInfo containing the XML element to sort
9776
98- return int ( feature . number )
77+ Sorts by:
9978
79+ - Core API features (those defined with a `<feature>` tag)
80+ - Sort VKSC core features after VK - Vulkan-specific
81+ - ARB/KHR/OES (Khronos extensions)
82+ - other (EXT/vendor extensions)"""
10083
101- def regSortFeatures (featureList ):
102- """Default sort procedure for features.
84+ if feature .elem .tag == 'feature' :
85+ if feature .name .startswith ('VKSC' ):
86+ return 0.5
87+ else :
88+ return 0
89+ elif feature .category .upper () in ('ARB' , 'KHR' , 'OES' ):
90+ return 1
91+ else :
92+ return 2
10393
104- - Sorts by explicit sort order (default 0) relative to other features
105- - then by feature category ('feature' or 'extension'),
106- - then by version number (for features)
107- - then by extension number (for extensions)"""
108- featureList .sort (key = regSortExtensionNumberKey )
109- featureList .sort (key = regSortFeatureVersionKey )
110- featureList .sort (key = regSortCategoryKey )
111- featureList .sort (key = regSortOrderKey )
94+ orderedFeatureNames .sort (key = lambda name : categoryKey (features [name ]))
11295
96+ # Sort by sortorder attribute
97+ orderedFeatureNames .sort (key = lambda name : features [name ].sortorder )
11398
11499class MissingGeneratorOptionsError (RuntimeError ):
115100 """Error raised when a Generator tries to do something that requires GeneratorOptions but it is None."""
@@ -154,6 +139,7 @@ def __init__(self,
154139 genpath = None ,
155140 apiname = None ,
156141 mergeApiNames = None ,
142+ mergeInternalApis = True ,
157143 profile = None ,
158144 versions = '.*' ,
159145 emitversions = '.*' ,
@@ -180,6 +166,7 @@ def __init__(self,
180166 - apiname - string matching `<api>` 'apiname' attribute, e.g. 'gl'.
181167 - mergeApiNames - If not None, a comma separated list of API names
182168 to merge into the API specified by 'apiname'
169+ - mergeInternalApis - whether to merge internal APIs into public APIs
183170 - profile - string specifying API profile , e.g. 'core', or None.
184171 - versions - regex matching API versions to process interfaces for.
185172 Normally `'.*'` or `'[0-9][.][0-9]'` to match all defined versions.
@@ -241,6 +228,9 @@ def __init__(self,
241228 self .mergeApiNames = mergeApiNames
242229 "comma separated list of API names to merge into the API specified by 'apiname'"
243230
231+ self .mergeInternalApis = mergeInternalApis
232+ "whether to merge internal APIs into public APIs"
233+
244234 self .profile = profile
245235 "string specifying API profile , e.g. 'core', or None."
246236
@@ -588,14 +578,14 @@ def deprecationComment(self, elem, indent = 0):
588578 name = elem .get ('name' )
589579
590580 if reason == 'aliased' :
591- return f'{ padding } // { name } is a deprecated alias\n '
581+ return f'{ padding } // { name } is a legacy alias\n '
592582 elif reason == 'ignored' :
593- return f'{ padding } // { name } is deprecated and should not be used\n '
583+ return f'{ padding } // { name } is legacy and should not be used\n '
594584 elif reason == 'true' :
595- return f'{ padding } // { name } is deprecated , but no reason was given in the API XML\n '
585+ return f'{ padding } // { name } is legacy , but no reason was given in the API XML\n '
596586 else :
597587 # This can be caught by schema validation
598- self .logMsg ('error' , f"{ name } has an unknown deprecation attribute value '{ reason } '" )
588+ self .logMsg ('error' , f"{ name } has an unknown legacy attribute value '{ reason } '" )
599589 exit (1 )
600590
601591 def buildEnumCDecl (self , expand , groupinfo , groupName ):
@@ -995,13 +985,19 @@ def makeProtoName(self, name, tail):
995985 raise MissingGeneratorOptionsError ()
996986 return self .genOpts .apientry + name + tail
997987
998- def makeTypedefName (self , name , tail ):
999- """Make the function-pointer typedef name for a command."""
988+ def makeTypedefName (self , name , tail , isfuncpointer ):
989+ """Make the function-pointer typedef name for a command or
990+ funcpointer name.
991+ If this is a function pointer <type> tag, do not prepend a PFN_
992+ to the name, as that is actually the type name and already
993+ included.
994+ """
1000995 if self .genOpts is None :
1001996 raise MissingGeneratorOptionsError ()
1002- return f"({ self .genOpts .apientryp } PFN_{ name } { tail } )"
1003997
1004- def makeCParamDecl (self , param , aligncol ):
998+ prefix = '' if isfuncpointer else 'PFN_'
999+
1000+ return f'({ self .genOpts .apientryp } { prefix } { name } { tail } )'
10051001 """Return a string which is an indented, formatted
10061002 declaration for a `<param>` or `<member>` block (e.g. function parameter
10071003 or structure/union member).
@@ -1238,11 +1234,15 @@ def isEnumRequired(self, elem):
12381234
12391235 def makeCDecls (self , cmd ):
12401236 """Return C prototype and function pointer typedef for a
1241- `<command>` Element, as a two-element list of strings.
1237+ `<command>` or `type category="funcpointer"` Element, as a
1238+ two-element list of strings [prototype, typedef].
12421239
1243- - cmd - Element containing a `< command>` tag"""
1240+ - cmd - Element containing a command or funcpointer tag"""
12441241 if self .genOpts is None :
12451242 raise MissingGeneratorOptionsError ()
1243+
1244+ isfuncpointer = (cmd .tag == 'type' )
1245+
12461246 proto = cmd .find ('proto' )
12471247 params = cmd .findall ('param' )
12481248 # Begin accumulating prototype and typedef strings
@@ -1270,7 +1270,7 @@ def makeCDecls(self, cmd):
12701270
12711271 if elem .tag == 'name' :
12721272 pdecl += self .makeProtoName (text , tail )
1273- tdecl += self .makeTypedefName (text , tail )
1273+ tdecl += self .makeTypedefName (text , tail , isfuncpointer )
12741274
12751275 Globals .headerText += 'extern PFN_' + text + ' ' + text + ';\n \n '
12761276 Globals .functionDefinitionsText += 'PFN_' + text + ' ' + text + ';\n \n '
@@ -1294,8 +1294,8 @@ def makeCDecls(self, cmd):
12941294 # a <param> node without the tags. No tree walking required
12951295 # since all tags are ignored.
12961296 # Uses: self.indentFuncProto
1297- # self.indentFuncPointer
1298- # self.alignFuncParam
1297+ # self.indentFuncPointer
1298+ # self.alignFuncParam
12991299 n = len (params )
13001300
13011301 if n > 0 and functionName :
@@ -1316,7 +1316,7 @@ def makeCDecls(self, cmd):
13161316 # Indented parameters
13171317 if n > 0 :
13181318 indentdecl = '(\n '
1319- indentdecl += ',\n ' .join (self .makeCParamDecl (p , self .genOpts .alignFuncParam )
1319+ indentdecl += ',\n ' .join (self .makeCParamDecl (p , self .genOpts .alignFuncParam )[ 0 ]
13201320 for p in params )
13211321 indentdecl += ');'
13221322 else :
@@ -1351,7 +1351,12 @@ def makeCDecls(self, cmd):
13511351 paramdecl += 'void'
13521352 paramdecl += ");"
13531353
1354- return [pdecl + indentdecl , tdecl + paramdecl ]
1354+ # For funcpointer types, only the typedef is returned,
1355+ # and it is formatted more attractively.
1356+ if isfuncpointer :
1357+ return [None , tdecl + indentdecl ]
1358+ else :
1359+ return [pdecl + indentdecl , tdecl + paramdecl ]
13551360
13561361 def newline (self ):
13571362 """Print a newline to the output file (utility function)"""
0 commit comments