| Mike Stroyan | a451fa8 | 2016-01-07 15:35:37 -0700 | [diff] [blame] | 1 | #!/usr/bin/python3 -i | 
| Dustin Graves | b8458d1 | 2016-03-28 16:17:38 -0600 | [diff] [blame] | 2 | # | 
|  | 3 | # Copyright (c) 2013-2016 The Khronos Group Inc. | 
|  | 4 | # | 
|  | 5 | # Permission is hereby granted, free of charge, to any person obtaining a | 
|  | 6 | # copy of this software and/or associated documentation files (the | 
|  | 7 | # "Materials"), to deal in the Materials without restriction, including | 
|  | 8 | # without limitation the rights to use, copy, modify, merge, publish, | 
|  | 9 | # distribute, sublicense, and/or sell copies of the Materials, and to | 
|  | 10 | # permit persons to whom the Materials are furnished to do so, subject to | 
|  | 11 | # the following conditions: | 
|  | 12 | # | 
|  | 13 | # The above copyright notice and this permission notice shall be included | 
|  | 14 | # in all copies or substantial portions of the Materials. | 
|  | 15 | # | 
|  | 16 | # THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | 
|  | 17 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | 
|  | 18 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | 
|  | 19 | # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY | 
|  | 20 | # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, | 
|  | 21 | # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE | 
|  | 22 | # MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. | 
|  | 23 |  | 
| Mike Stroyan | a451fa8 | 2016-01-07 15:35:37 -0700 | [diff] [blame] | 24 | import os,re,sys | 
| Dustin Graves | f69e377 | 2016-02-11 10:10:14 -0700 | [diff] [blame] | 25 | from collections import namedtuple | 
| Mike Stroyan | 0cf28a2 | 2016-04-05 16:40:30 -0600 | [diff] [blame] | 26 | import xml.etree.ElementTree as etree | 
| Mike Stroyan | a451fa8 | 2016-01-07 15:35:37 -0700 | [diff] [blame] | 27 |  | 
|  | 28 | def write( *args, **kwargs ): | 
|  | 29 | file = kwargs.pop('file',sys.stdout) | 
|  | 30 | end = kwargs.pop( 'end','\n') | 
|  | 31 | file.write( ' '.join([str(arg) for arg in args]) ) | 
|  | 32 | file.write( end ) | 
|  | 33 |  | 
|  | 34 | # noneStr - returns string argument, or "" if argument is None. | 
| Mike Stroyan | 0cf28a2 | 2016-04-05 16:40:30 -0600 | [diff] [blame] | 35 | # Used in converting etree Elements into text. | 
| Mike Stroyan | a451fa8 | 2016-01-07 15:35:37 -0700 | [diff] [blame] | 36 | #   str - string to convert | 
|  | 37 | def noneStr(str): | 
|  | 38 | if (str): | 
|  | 39 | return str | 
|  | 40 | else: | 
|  | 41 | return "" | 
|  | 42 |  | 
|  | 43 | # enquote - returns string argument with surrounding quotes, | 
|  | 44 | #   for serialization into Python code. | 
|  | 45 | def enquote(str): | 
|  | 46 | if (str): | 
|  | 47 | return "'" + str + "'" | 
|  | 48 | else: | 
|  | 49 | return None | 
|  | 50 |  | 
|  | 51 | # Primary sort key for regSortFeatures. | 
|  | 52 | # Sorts by category of the feature name string: | 
|  | 53 | #   Core API features (those defined with a <feature> tag) | 
|  | 54 | #   ARB/KHR/OES (Khronos extensions) | 
|  | 55 | #   other       (EXT/vendor extensions) | 
|  | 56 | # This will need changing for Vulkan! | 
|  | 57 | def regSortCategoryKey(feature): | 
|  | 58 | if (feature.elem.tag == 'feature'): | 
|  | 59 | return 0 | 
|  | 60 | elif (feature.category == 'ARB' or | 
|  | 61 | feature.category == 'KHR' or | 
|  | 62 | feature.category == 'OES'): | 
|  | 63 | return 1 | 
|  | 64 | else: | 
|  | 65 | return 2 | 
|  | 66 |  | 
|  | 67 | # Secondary sort key for regSortFeatures. | 
|  | 68 | # Sorts by extension name. | 
|  | 69 | def regSortNameKey(feature): | 
|  | 70 | return feature.name | 
|  | 71 |  | 
|  | 72 | # Second sort key for regSortFeatures. | 
|  | 73 | # Sorts by feature version. <extension> elements all have version number "0" | 
|  | 74 | def regSortFeatureVersionKey(feature): | 
|  | 75 | return float(feature.version) | 
|  | 76 |  | 
|  | 77 | # Tertiary sort key for regSortFeatures. | 
|  | 78 | # Sorts by extension number. <feature> elements all have extension number 0. | 
|  | 79 | def regSortExtensionNumberKey(feature): | 
|  | 80 | return int(feature.number) | 
|  | 81 |  | 
|  | 82 | # regSortFeatures - default sort procedure for features. | 
|  | 83 | # Sorts by primary key of feature category ('feature' or 'extension') | 
|  | 84 | #   then by version number (for features) | 
|  | 85 | #   then by extension number (for extensions) | 
|  | 86 | def regSortFeatures(featureList): | 
|  | 87 | featureList.sort(key = regSortExtensionNumberKey) | 
|  | 88 | featureList.sort(key = regSortFeatureVersionKey) | 
|  | 89 | featureList.sort(key = regSortCategoryKey) | 
|  | 90 |  | 
|  | 91 | # GeneratorOptions - base class for options used during header production | 
|  | 92 | # These options are target language independent, and used by | 
|  | 93 | # Registry.apiGen() and by base OutputGenerator objects. | 
|  | 94 | # | 
|  | 95 | # Members | 
|  | 96 | #   filename - name of file to generate, or None to write to stdout. | 
|  | 97 | #   apiname - string matching <api> 'apiname' attribute, e.g. 'gl'. | 
|  | 98 | #   profile - string specifying API profile , e.g. 'core', or None. | 
|  | 99 | #   versions - regex matching API versions to process interfaces for. | 
|  | 100 | #     Normally '.*' or '[0-9]\.[0-9]' to match all defined versions. | 
|  | 101 | #   emitversions - regex matching API versions to actually emit | 
|  | 102 | #    interfaces for (though all requested versions are considered | 
|  | 103 | #    when deciding which interfaces to generate). For GL 4.3 glext.h, | 
|  | 104 | #     this might be '1\.[2-5]|[2-4]\.[0-9]'. | 
|  | 105 | #   defaultExtensions - If not None, a string which must in its | 
|  | 106 | #     entirety match the pattern in the "supported" attribute of | 
|  | 107 | #     the <extension>. Defaults to None. Usually the same as apiname. | 
|  | 108 | #   addExtensions - regex matching names of additional extensions | 
|  | 109 | #     to include. Defaults to None. | 
|  | 110 | #   removeExtensions - regex matching names of extensions to | 
|  | 111 | #     remove (after defaultExtensions and addExtensions). Defaults | 
|  | 112 | #     to None. | 
|  | 113 | #   sortProcedure - takes a list of FeatureInfo objects and sorts | 
|  | 114 | #     them in place to a preferred order in the generated output. | 
|  | 115 | #     Default is core API versions, ARB/KHR/OES extensions, all | 
|  | 116 | #     other extensions, alphabetically within each group. | 
|  | 117 | # The regex patterns can be None or empty, in which case they match | 
|  | 118 | #   nothing. | 
|  | 119 | class GeneratorOptions: | 
|  | 120 | """Represents options during header production from an API registry""" | 
|  | 121 | def __init__(self, | 
|  | 122 | filename = None, | 
|  | 123 | apiname = None, | 
|  | 124 | profile = None, | 
|  | 125 | versions = '.*', | 
|  | 126 | emitversions = '.*', | 
|  | 127 | defaultExtensions = None, | 
|  | 128 | addExtensions = None, | 
|  | 129 | removeExtensions = None, | 
|  | 130 | sortProcedure = regSortFeatures): | 
|  | 131 | self.filename          = filename | 
|  | 132 | self.apiname           = apiname | 
|  | 133 | self.profile           = profile | 
|  | 134 | self.versions          = self.emptyRegex(versions) | 
|  | 135 | self.emitversions      = self.emptyRegex(emitversions) | 
|  | 136 | self.defaultExtensions = defaultExtensions | 
|  | 137 | self.addExtensions     = self.emptyRegex(addExtensions) | 
|  | 138 | self.removeExtensions  = self.emptyRegex(removeExtensions) | 
|  | 139 | self.sortProcedure     = sortProcedure | 
|  | 140 | # | 
|  | 141 | # Substitute a regular expression which matches no version | 
|  | 142 | # or extension names for None or the empty string. | 
|  | 143 | def emptyRegex(self,pat): | 
|  | 144 | if (pat == None or pat == ''): | 
|  | 145 | return '_nomatch_^' | 
|  | 146 | else: | 
|  | 147 | return pat | 
|  | 148 |  | 
|  | 149 | # CGeneratorOptions - subclass of GeneratorOptions. | 
|  | 150 | # | 
|  | 151 | # Adds options used by COutputGenerator objects during C language header | 
|  | 152 | # generation. | 
|  | 153 | # | 
|  | 154 | # Additional members | 
|  | 155 | #   prefixText - list of strings to prefix generated header with | 
|  | 156 | #     (usually a copyright statement + calling convention macros). | 
|  | 157 | #   protectFile - True if multiple inclusion protection should be | 
|  | 158 | #     generated (based on the filename) around the entire header. | 
|  | 159 | #   protectFeature - True if #ifndef..#endif protection should be | 
|  | 160 | #     generated around a feature interface in the header file. | 
|  | 161 | #   genFuncPointers - True if function pointer typedefs should be | 
|  | 162 | #     generated | 
|  | 163 | #   protectProto - If conditional protection should be generated | 
|  | 164 | #     around prototype declarations, set to either '#ifdef' | 
|  | 165 | #     to require opt-in (#ifdef protectProtoStr) or '#ifndef' | 
|  | 166 | #     to require opt-out (#ifndef protectProtoStr). Otherwise | 
|  | 167 | #     set to None. | 
|  | 168 | #   protectProtoStr - #ifdef/#ifndef symbol to use around prototype | 
|  | 169 | #     declarations, if protectProto is set | 
|  | 170 | #   apicall - string to use for the function declaration prefix, | 
|  | 171 | #     such as APICALL on Windows. | 
|  | 172 | #   apientry - string to use for the calling convention macro, | 
|  | 173 | #     in typedefs, such as APIENTRY. | 
|  | 174 | #   apientryp - string to use for the calling convention macro | 
|  | 175 | #     in function pointer typedefs, such as APIENTRYP. | 
|  | 176 | #   indentFuncProto - True if prototype declarations should put each | 
|  | 177 | #     parameter on a separate line | 
|  | 178 | #   indentFuncPointer - True if typedefed function pointers should put each | 
|  | 179 | #     parameter on a separate line | 
|  | 180 | #   alignFuncParam - if nonzero and parameters are being put on a | 
|  | 181 | #     separate line, align parameter names at the specified column | 
|  | 182 | class CGeneratorOptions(GeneratorOptions): | 
|  | 183 | """Represents options during C interface generation for headers""" | 
|  | 184 | def __init__(self, | 
|  | 185 | filename = None, | 
|  | 186 | apiname = None, | 
|  | 187 | profile = None, | 
|  | 188 | versions = '.*', | 
|  | 189 | emitversions = '.*', | 
|  | 190 | defaultExtensions = None, | 
|  | 191 | addExtensions = None, | 
|  | 192 | removeExtensions = None, | 
|  | 193 | sortProcedure = regSortFeatures, | 
|  | 194 | prefixText = "", | 
|  | 195 | genFuncPointers = True, | 
|  | 196 | protectFile = True, | 
|  | 197 | protectFeature = True, | 
|  | 198 | protectProto = None, | 
|  | 199 | protectProtoStr = None, | 
|  | 200 | apicall = '', | 
|  | 201 | apientry = '', | 
|  | 202 | apientryp = '', | 
|  | 203 | indentFuncProto = True, | 
|  | 204 | indentFuncPointer = False, | 
|  | 205 | alignFuncParam = 0): | 
|  | 206 | GeneratorOptions.__init__(self, filename, apiname, profile, | 
|  | 207 | versions, emitversions, defaultExtensions, | 
|  | 208 | addExtensions, removeExtensions, sortProcedure) | 
|  | 209 | self.prefixText      = prefixText | 
|  | 210 | self.genFuncPointers = genFuncPointers | 
|  | 211 | self.protectFile     = protectFile | 
|  | 212 | self.protectFeature  = protectFeature | 
|  | 213 | self.protectProto    = protectProto | 
|  | 214 | self.protectProtoStr = protectProtoStr | 
|  | 215 | self.apicall         = apicall | 
|  | 216 | self.apientry        = apientry | 
|  | 217 | self.apientryp       = apientryp | 
|  | 218 | self.indentFuncProto = indentFuncProto | 
|  | 219 | self.indentFuncPointer = indentFuncPointer | 
|  | 220 | self.alignFuncParam  = alignFuncParam | 
|  | 221 |  | 
|  | 222 | # DocGeneratorOptions - subclass of GeneratorOptions. | 
|  | 223 | # | 
|  | 224 | # Shares many members with CGeneratorOptions, since | 
|  | 225 | # both are writing C-style declarations: | 
|  | 226 | # | 
|  | 227 | #   prefixText - list of strings to prefix generated header with | 
|  | 228 | #     (usually a copyright statement + calling convention macros). | 
|  | 229 | #   apicall - string to use for the function declaration prefix, | 
|  | 230 | #     such as APICALL on Windows. | 
|  | 231 | #   apientry - string to use for the calling convention macro, | 
|  | 232 | #     in typedefs, such as APIENTRY. | 
|  | 233 | #   apientryp - string to use for the calling convention macro | 
|  | 234 | #     in function pointer typedefs, such as APIENTRYP. | 
|  | 235 | #   genDirectory - directory into which to generate include files | 
|  | 236 | #   indentFuncProto - True if prototype declarations should put each | 
|  | 237 | #     parameter on a separate line | 
|  | 238 | #   indentFuncPointer - True if typedefed function pointers should put each | 
|  | 239 | #     parameter on a separate line | 
|  | 240 | #   alignFuncParam - if nonzero and parameters are being put on a | 
|  | 241 | #     separate line, align parameter names at the specified column | 
|  | 242 | # | 
|  | 243 | # Additional members: | 
|  | 244 | # | 
|  | 245 | class DocGeneratorOptions(GeneratorOptions): | 
|  | 246 | """Represents options during C interface generation for Asciidoc""" | 
|  | 247 | def __init__(self, | 
|  | 248 | filename = None, | 
|  | 249 | apiname = None, | 
|  | 250 | profile = None, | 
|  | 251 | versions = '.*', | 
|  | 252 | emitversions = '.*', | 
|  | 253 | defaultExtensions = None, | 
|  | 254 | addExtensions = None, | 
|  | 255 | removeExtensions = None, | 
|  | 256 | sortProcedure = regSortFeatures, | 
|  | 257 | prefixText = "", | 
|  | 258 | apicall = '', | 
|  | 259 | apientry = '', | 
|  | 260 | apientryp = '', | 
|  | 261 | genDirectory = 'gen', | 
|  | 262 | indentFuncProto = True, | 
|  | 263 | indentFuncPointer = False, | 
|  | 264 | alignFuncParam = 0, | 
|  | 265 | expandEnumerants = True): | 
|  | 266 | GeneratorOptions.__init__(self, filename, apiname, profile, | 
|  | 267 | versions, emitversions, defaultExtensions, | 
|  | 268 | addExtensions, removeExtensions, sortProcedure) | 
|  | 269 | self.prefixText      = prefixText | 
|  | 270 | self.apicall         = apicall | 
|  | 271 | self.apientry        = apientry | 
|  | 272 | self.apientryp       = apientryp | 
|  | 273 | self.genDirectory    = genDirectory | 
|  | 274 | self.indentFuncProto = indentFuncProto | 
|  | 275 | self.indentFuncPointer = indentFuncPointer | 
|  | 276 | self.alignFuncParam  = alignFuncParam | 
|  | 277 | self.expandEnumerants = expandEnumerants | 
|  | 278 |  | 
| Mike Stroyan | 8849f9a | 2015-11-02 15:30:20 -0700 | [diff] [blame] | 279 | # ThreadGeneratorOptions - subclass of GeneratorOptions. | 
|  | 280 | # | 
|  | 281 | # Adds options used by COutputGenerator objects during C language header | 
|  | 282 | # generation. | 
|  | 283 | # | 
|  | 284 | # Additional members | 
|  | 285 | #   prefixText - list of strings to prefix generated header with | 
|  | 286 | #     (usually a copyright statement + calling convention macros). | 
|  | 287 | #   protectFile - True if multiple inclusion protection should be | 
|  | 288 | #     generated (based on the filename) around the entire header. | 
|  | 289 | #   protectFeature - True if #ifndef..#endif protection should be | 
|  | 290 | #     generated around a feature interface in the header file. | 
|  | 291 | #   genFuncPointers - True if function pointer typedefs should be | 
|  | 292 | #     generated | 
|  | 293 | #   protectProto - True if #ifdef..#endif protection should be | 
|  | 294 | #     generated around prototype declarations | 
|  | 295 | #   protectProtoStr - #ifdef symbol to use around prototype | 
|  | 296 | #     declarations, if protected | 
|  | 297 | #   apicall - string to use for the function declaration prefix, | 
|  | 298 | #     such as APICALL on Windows. | 
|  | 299 | #   apientry - string to use for the calling convention macro, | 
|  | 300 | #     in typedefs, such as APIENTRY. | 
|  | 301 | #   apientryp - string to use for the calling convention macro | 
|  | 302 | #     in function pointer typedefs, such as APIENTRYP. | 
|  | 303 | #   indentFuncProto - True if prototype declarations should put each | 
|  | 304 | #     parameter on a separate line | 
|  | 305 | #   indentFuncPointer - True if typedefed function pointers should put each | 
|  | 306 | #     parameter on a separate line | 
|  | 307 | #   alignFuncParam - if nonzero and parameters are being put on a | 
|  | 308 | #     separate line, align parameter names at the specified column | 
|  | 309 | class ThreadGeneratorOptions(GeneratorOptions): | 
|  | 310 | """Represents options during C interface generation for headers""" | 
|  | 311 | def __init__(self, | 
|  | 312 | filename = None, | 
|  | 313 | apiname = None, | 
|  | 314 | profile = None, | 
|  | 315 | versions = '.*', | 
|  | 316 | emitversions = '.*', | 
|  | 317 | defaultExtensions = None, | 
|  | 318 | addExtensions = None, | 
|  | 319 | removeExtensions = None, | 
|  | 320 | sortProcedure = regSortFeatures, | 
|  | 321 | prefixText = "", | 
|  | 322 | genFuncPointers = True, | 
|  | 323 | protectFile = True, | 
|  | 324 | protectFeature = True, | 
|  | 325 | protectProto = True, | 
|  | 326 | protectProtoStr = True, | 
|  | 327 | apicall = '', | 
|  | 328 | apientry = '', | 
|  | 329 | apientryp = '', | 
|  | 330 | indentFuncProto = True, | 
|  | 331 | indentFuncPointer = False, | 
|  | 332 | alignFuncParam = 0): | 
|  | 333 | GeneratorOptions.__init__(self, filename, apiname, profile, | 
|  | 334 | versions, emitversions, defaultExtensions, | 
|  | 335 | addExtensions, removeExtensions, sortProcedure) | 
|  | 336 | self.prefixText      = prefixText | 
|  | 337 | self.genFuncPointers = genFuncPointers | 
|  | 338 | self.protectFile     = protectFile | 
|  | 339 | self.protectFeature  = protectFeature | 
|  | 340 | self.protectProto    = protectProto | 
|  | 341 | self.protectProtoStr = protectProtoStr | 
|  | 342 | self.apicall         = apicall | 
|  | 343 | self.apientry        = apientry | 
|  | 344 | self.apientryp       = apientryp | 
|  | 345 | self.indentFuncProto = indentFuncProto | 
|  | 346 | self.indentFuncPointer = indentFuncPointer | 
|  | 347 | self.alignFuncParam  = alignFuncParam | 
|  | 348 |  | 
|  | 349 |  | 
| Dustin Graves | f69e377 | 2016-02-11 10:10:14 -0700 | [diff] [blame] | 350 | # ParamCheckerGeneratorOptions - subclass of GeneratorOptions. | 
|  | 351 | # | 
| Mark Lobodzinski | 1c33357 | 2016-03-17 15:08:18 -0600 | [diff] [blame] | 352 | # Adds options used by ParamCheckerOutputGenerator objects during parameter validation | 
| Dustin Graves | f69e377 | 2016-02-11 10:10:14 -0700 | [diff] [blame] | 353 | # generation. | 
|  | 354 | # | 
|  | 355 | # Additional members | 
|  | 356 | #   prefixText - list of strings to prefix generated header with | 
|  | 357 | #     (usually a copyright statement + calling convention macros). | 
|  | 358 | #   protectFile - True if multiple inclusion protection should be | 
|  | 359 | #     generated (based on the filename) around the entire header. | 
|  | 360 | #   protectFeature - True if #ifndef..#endif protection should be | 
|  | 361 | #     generated around a feature interface in the header file. | 
|  | 362 | #   genFuncPointers - True if function pointer typedefs should be | 
|  | 363 | #     generated | 
|  | 364 | #   protectProto - If conditional protection should be generated | 
|  | 365 | #     around prototype declarations, set to either '#ifdef' | 
|  | 366 | #     to require opt-in (#ifdef protectProtoStr) or '#ifndef' | 
|  | 367 | #     to require opt-out (#ifndef protectProtoStr). Otherwise | 
|  | 368 | #     set to None. | 
|  | 369 | #   protectProtoStr - #ifdef/#ifndef symbol to use around prototype | 
|  | 370 | #     declarations, if protectProto is set | 
|  | 371 | #   apicall - string to use for the function declaration prefix, | 
|  | 372 | #     such as APICALL on Windows. | 
|  | 373 | #   apientry - string to use for the calling convention macro, | 
|  | 374 | #     in typedefs, such as APIENTRY. | 
|  | 375 | #   apientryp - string to use for the calling convention macro | 
|  | 376 | #     in function pointer typedefs, such as APIENTRYP. | 
|  | 377 | #   indentFuncProto - True if prototype declarations should put each | 
|  | 378 | #     parameter on a separate line | 
|  | 379 | #   indentFuncPointer - True if typedefed function pointers should put each | 
|  | 380 | #     parameter on a separate line | 
|  | 381 | #   alignFuncParam - if nonzero and parameters are being put on a | 
|  | 382 | #     separate line, align parameter names at the specified column | 
|  | 383 | class ParamCheckerGeneratorOptions(GeneratorOptions): | 
|  | 384 | """Represents options during C interface generation for headers""" | 
|  | 385 | def __init__(self, | 
|  | 386 | filename = None, | 
|  | 387 | apiname = None, | 
|  | 388 | profile = None, | 
|  | 389 | versions = '.*', | 
|  | 390 | emitversions = '.*', | 
|  | 391 | defaultExtensions = None, | 
|  | 392 | addExtensions = None, | 
|  | 393 | removeExtensions = None, | 
|  | 394 | sortProcedure = regSortFeatures, | 
|  | 395 | prefixText = "", | 
|  | 396 | genFuncPointers = True, | 
|  | 397 | protectFile = True, | 
|  | 398 | protectFeature = True, | 
|  | 399 | protectProto = None, | 
|  | 400 | protectProtoStr = None, | 
|  | 401 | apicall = '', | 
|  | 402 | apientry = '', | 
|  | 403 | apientryp = '', | 
|  | 404 | indentFuncProto = True, | 
|  | 405 | indentFuncPointer = False, | 
|  | 406 | alignFuncParam = 0): | 
|  | 407 | GeneratorOptions.__init__(self, filename, apiname, profile, | 
|  | 408 | versions, emitversions, defaultExtensions, | 
|  | 409 | addExtensions, removeExtensions, sortProcedure) | 
|  | 410 | self.prefixText      = prefixText | 
|  | 411 | self.genFuncPointers = genFuncPointers | 
|  | 412 | self.protectFile     = protectFile | 
|  | 413 | self.protectFeature  = protectFeature | 
|  | 414 | self.protectProto    = protectProto | 
|  | 415 | self.protectProtoStr = protectProtoStr | 
|  | 416 | self.apicall         = apicall | 
|  | 417 | self.apientry        = apientry | 
|  | 418 | self.apientryp       = apientryp | 
|  | 419 | self.indentFuncProto = indentFuncProto | 
|  | 420 | self.indentFuncPointer = indentFuncPointer | 
|  | 421 | self.alignFuncParam  = alignFuncParam | 
|  | 422 |  | 
|  | 423 |  | 
| Mike Stroyan | a451fa8 | 2016-01-07 15:35:37 -0700 | [diff] [blame] | 424 | # OutputGenerator - base class for generating API interfaces. | 
|  | 425 | # Manages basic logic, logging, and output file control | 
|  | 426 | # Derived classes actually generate formatted output. | 
|  | 427 | # | 
|  | 428 | # ---- methods ---- | 
|  | 429 | # OutputGenerator(errFile, warnFile, diagFile) | 
|  | 430 | #   errFile, warnFile, diagFile - file handles to write errors, | 
|  | 431 | #     warnings, diagnostics to. May be None to not write. | 
|  | 432 | # logMsg(level, *args) - log messages of different categories | 
|  | 433 | #   level - 'error', 'warn', or 'diag'. 'error' will also | 
|  | 434 | #     raise a UserWarning exception | 
|  | 435 | #   *args - print()-style arguments | 
|  | 436 | # setExtMap(map) - specify a dictionary map from extension names to | 
|  | 437 | #   numbers, used in creating values for extension enumerants. | 
|  | 438 | # beginFile(genOpts) - start a new interface file | 
|  | 439 | #   genOpts - GeneratorOptions controlling what's generated and how | 
|  | 440 | # endFile() - finish an interface file, closing it when done | 
|  | 441 | # beginFeature(interface, emit) - write interface for a feature | 
|  | 442 | # and tag generated features as having been done. | 
|  | 443 | #   interface - element for the <version> / <extension> to generate | 
|  | 444 | #   emit - actually write to the header only when True | 
|  | 445 | # endFeature() - finish an interface. | 
|  | 446 | # genType(typeinfo,name) - generate interface for a type | 
|  | 447 | #   typeinfo - TypeInfo for a type | 
|  | 448 | # genStruct(typeinfo,name) - generate interface for a C "struct" type. | 
|  | 449 | #   typeinfo - TypeInfo for a type interpreted as a struct | 
|  | 450 | # genGroup(groupinfo,name) - generate interface for a group of enums (C "enum") | 
|  | 451 | #   groupinfo - GroupInfo for a group | 
|  | 452 | # genEnum(enuminfo, name) - generate interface for an enum (constant) | 
|  | 453 | #   enuminfo - EnumInfo for an enum | 
|  | 454 | #   name - enum name | 
|  | 455 | # genCmd(cmdinfo) - generate interface for a command | 
|  | 456 | #   cmdinfo - CmdInfo for a command | 
|  | 457 | # makeCDecls(cmd) - return C prototype and function pointer typedef for a | 
|  | 458 | #     <command> Element, as a list of two strings | 
|  | 459 | #   cmd - Element for the <command> | 
|  | 460 | # newline() - print a newline to the output file (utility function) | 
|  | 461 | # | 
|  | 462 | class OutputGenerator: | 
|  | 463 | """Generate specified API interfaces in a specific style, such as a C header""" | 
|  | 464 | def __init__(self, | 
|  | 465 | errFile = sys.stderr, | 
|  | 466 | warnFile = sys.stderr, | 
|  | 467 | diagFile = sys.stdout): | 
|  | 468 | self.outFile = None | 
|  | 469 | self.errFile = errFile | 
|  | 470 | self.warnFile = warnFile | 
|  | 471 | self.diagFile = diagFile | 
|  | 472 | # Internal state | 
|  | 473 | self.featureName = None | 
|  | 474 | self.genOpts = None | 
|  | 475 | self.registry = None | 
|  | 476 | # Used for extension enum value generation | 
|  | 477 | self.extBase      = 1000000000 | 
|  | 478 | self.extBlockSize = 1000 | 
|  | 479 | # | 
|  | 480 | # logMsg - write a message of different categories to different | 
|  | 481 | #   destinations. | 
|  | 482 | # level - | 
|  | 483 | #   'diag' (diagnostic, voluminous) | 
|  | 484 | #   'warn' (warning) | 
|  | 485 | #   'error' (fatal error - raises exception after logging) | 
|  | 486 | # *args - print()-style arguments to direct to corresponding log | 
|  | 487 | def logMsg(self, level, *args): | 
|  | 488 | """Log a message at the given level. Can be ignored or log to a file""" | 
|  | 489 | if (level == 'error'): | 
|  | 490 | strfile = io.StringIO() | 
|  | 491 | write('ERROR:', *args, file=strfile) | 
|  | 492 | if (self.errFile != None): | 
|  | 493 | write(strfile.getvalue(), file=self.errFile) | 
|  | 494 | raise UserWarning(strfile.getvalue()) | 
|  | 495 | elif (level == 'warn'): | 
|  | 496 | if (self.warnFile != None): | 
|  | 497 | write('WARNING:', *args, file=self.warnFile) | 
|  | 498 | elif (level == 'diag'): | 
|  | 499 | if (self.diagFile != None): | 
|  | 500 | write('DIAG:', *args, file=self.diagFile) | 
|  | 501 | else: | 
|  | 502 | raise UserWarning( | 
|  | 503 | '*** FATAL ERROR in Generator.logMsg: unknown level:' + level) | 
|  | 504 | # | 
|  | 505 | # enumToValue - parses and converts an <enum> tag into a value. | 
|  | 506 | # Returns a list | 
|  | 507 | #   first element - integer representation of the value, or None | 
|  | 508 | #       if needsNum is False. The value must be a legal number | 
|  | 509 | #       if needsNum is True. | 
|  | 510 | #   second element - string representation of the value | 
|  | 511 | # There are several possible representations of values. | 
|  | 512 | #   A 'value' attribute simply contains the value. | 
|  | 513 | #   A 'bitpos' attribute defines a value by specifying the bit | 
|  | 514 | #       position which is set in that value. | 
|  | 515 | #   A 'offset','extbase','extends' triplet specifies a value | 
|  | 516 | #       as an offset to a base value defined by the specified | 
|  | 517 | #       'extbase' extension name, which is then cast to the | 
|  | 518 | #       typename specified by 'extends'. This requires probing | 
|  | 519 | #       the registry database, and imbeds knowledge of the | 
|  | 520 | #       Vulkan extension enum scheme in this function. | 
|  | 521 | def enumToValue(self, elem, needsNum): | 
|  | 522 | name = elem.get('name') | 
|  | 523 | numVal = None | 
|  | 524 | if ('value' in elem.keys()): | 
|  | 525 | value = elem.get('value') | 
|  | 526 | # print('About to translate value =', value, 'type =', type(value)) | 
|  | 527 | if (needsNum): | 
|  | 528 | numVal = int(value, 0) | 
|  | 529 | # If there's a non-integer, numeric 'type' attribute (e.g. 'u' or | 
|  | 530 | # 'ull'), append it to the string value. | 
|  | 531 | # t = enuminfo.elem.get('type') | 
|  | 532 | # if (t != None and t != '' and t != 'i' and t != 's'): | 
|  | 533 | #     value += enuminfo.type | 
|  | 534 | self.logMsg('diag', 'Enum', name, '-> value [', numVal, ',', value, ']') | 
|  | 535 | return [numVal, value] | 
|  | 536 | if ('bitpos' in elem.keys()): | 
|  | 537 | value = elem.get('bitpos') | 
|  | 538 | numVal = int(value, 0) | 
|  | 539 | numVal = 1 << numVal | 
|  | 540 | value = '0x%08x' % numVal | 
|  | 541 | self.logMsg('diag', 'Enum', name, '-> bitpos [', numVal, ',', value, ']') | 
|  | 542 | return [numVal, value] | 
|  | 543 | if ('offset' in elem.keys()): | 
|  | 544 | # Obtain values in the mapping from the attributes | 
|  | 545 | enumNegative = False | 
|  | 546 | offset = int(elem.get('offset'),0) | 
|  | 547 | extnumber = int(elem.get('extnumber'),0) | 
|  | 548 | extends = elem.get('extends') | 
|  | 549 | if ('dir' in elem.keys()): | 
|  | 550 | enumNegative = True | 
|  | 551 | self.logMsg('diag', 'Enum', name, 'offset =', offset, | 
|  | 552 | 'extnumber =', extnumber, 'extends =', extends, | 
|  | 553 | 'enumNegative =', enumNegative) | 
|  | 554 | # Now determine the actual enumerant value, as defined | 
|  | 555 | # in the "Layers and Extensions" appendix of the spec. | 
|  | 556 | numVal = self.extBase + (extnumber - 1) * self.extBlockSize + offset | 
|  | 557 | if (enumNegative): | 
|  | 558 | numVal = -numVal | 
|  | 559 | value = '%d' % numVal | 
|  | 560 | # More logic needed! | 
|  | 561 | self.logMsg('diag', 'Enum', name, '-> offset [', numVal, ',', value, ']') | 
|  | 562 | return [numVal, value] | 
|  | 563 | return [None, None] | 
|  | 564 | # | 
|  | 565 | def beginFile(self, genOpts): | 
|  | 566 | self.genOpts = genOpts | 
|  | 567 | # | 
|  | 568 | # Open specified output file. Not done in constructor since a | 
|  | 569 | # Generator can be used without writing to a file. | 
|  | 570 | if (self.genOpts.filename != None): | 
|  | 571 | self.outFile = open(self.genOpts.filename, 'w') | 
|  | 572 | else: | 
|  | 573 | self.outFile = sys.stdout | 
|  | 574 | def endFile(self): | 
|  | 575 | self.errFile and self.errFile.flush() | 
|  | 576 | self.warnFile and self.warnFile.flush() | 
|  | 577 | self.diagFile and self.diagFile.flush() | 
|  | 578 | self.outFile.flush() | 
|  | 579 | if (self.outFile != sys.stdout and self.outFile != sys.stderr): | 
|  | 580 | self.outFile.close() | 
|  | 581 | self.genOpts = None | 
|  | 582 | # | 
|  | 583 | def beginFeature(self, interface, emit): | 
|  | 584 | self.emit = emit | 
|  | 585 | self.featureName = interface.get('name') | 
|  | 586 | # If there's an additional 'protect' attribute in the feature, save it | 
|  | 587 | self.featureExtraProtect = interface.get('protect') | 
|  | 588 | def endFeature(self): | 
|  | 589 | # Derived classes responsible for emitting feature | 
|  | 590 | self.featureName = None | 
|  | 591 | self.featureExtraProtect = None | 
|  | 592 | # Utility method to validate we're generating something only inside a | 
|  | 593 | # <feature> tag | 
|  | 594 | def validateFeature(self, featureType, featureName): | 
|  | 595 | if (self.featureName == None): | 
|  | 596 | raise UserWarning('Attempt to generate', featureType, name, | 
|  | 597 | 'when not in feature') | 
|  | 598 | # | 
|  | 599 | # Type generation | 
|  | 600 | def genType(self, typeinfo, name): | 
|  | 601 | self.validateFeature('type', name) | 
|  | 602 | # | 
|  | 603 | # Struct (e.g. C "struct" type) generation | 
|  | 604 | def genStruct(self, typeinfo, name): | 
|  | 605 | self.validateFeature('struct', name) | 
|  | 606 | # | 
|  | 607 | # Group (e.g. C "enum" type) generation | 
|  | 608 | def genGroup(self, groupinfo, name): | 
|  | 609 | self.validateFeature('group', name) | 
|  | 610 | # | 
|  | 611 | # Enumerant (really, constant) generation | 
|  | 612 | def genEnum(self, enuminfo, name): | 
|  | 613 | self.validateFeature('enum', name) | 
|  | 614 | # | 
|  | 615 | # Command generation | 
|  | 616 | def genCmd(self, cmd, name): | 
|  | 617 | self.validateFeature('command', name) | 
|  | 618 | # | 
|  | 619 | # Utility functions - turn a <proto> <name> into C-language prototype | 
|  | 620 | # and typedef declarations for that name. | 
|  | 621 | # name - contents of <name> tag | 
|  | 622 | # tail - whatever text follows that tag in the Element | 
|  | 623 | def makeProtoName(self, name, tail): | 
|  | 624 | return self.genOpts.apientry + name + tail | 
|  | 625 | def makeTypedefName(self, name, tail): | 
|  | 626 | return '(' + self.genOpts.apientryp + 'PFN_' + name + tail + ')' | 
|  | 627 | # | 
|  | 628 | # makeCParamDecl - return a string which is an indented, formatted | 
|  | 629 | # declaration for a <param> or <member> block (e.g. function parameter | 
|  | 630 | # or structure/union member). | 
|  | 631 | # param - Element (<param> or <member>) to format | 
|  | 632 | # aligncol - if non-zero, attempt to align the nested <name> element | 
|  | 633 | #   at this column | 
|  | 634 | def makeCParamDecl(self, param, aligncol): | 
|  | 635 | paramdecl = '    ' + noneStr(param.text) | 
|  | 636 | for elem in param: | 
|  | 637 | text = noneStr(elem.text) | 
|  | 638 | tail = noneStr(elem.tail) | 
|  | 639 | if (elem.tag == 'name' and aligncol > 0): | 
|  | 640 | self.logMsg('diag', 'Aligning parameter', elem.text, 'to column', self.genOpts.alignFuncParam) | 
|  | 641 | # Align at specified column, if possible | 
|  | 642 | paramdecl = paramdecl.rstrip() | 
|  | 643 | oldLen = len(paramdecl) | 
|  | 644 | paramdecl = paramdecl.ljust(aligncol) | 
|  | 645 | newLen = len(paramdecl) | 
|  | 646 | self.logMsg('diag', 'Adjust length of parameter decl from', oldLen, 'to', newLen, ':', paramdecl) | 
|  | 647 | paramdecl += text + tail | 
|  | 648 | return paramdecl | 
|  | 649 | # | 
|  | 650 | # getCParamTypeLength - return the length of the type field is an indented, formatted | 
|  | 651 | # declaration for a <param> or <member> block (e.g. function parameter | 
|  | 652 | # or structure/union member). | 
|  | 653 | # param - Element (<param> or <member>) to identify | 
|  | 654 | def getCParamTypeLength(self, param): | 
|  | 655 | paramdecl = '    ' + noneStr(param.text) | 
|  | 656 | for elem in param: | 
|  | 657 | text = noneStr(elem.text) | 
|  | 658 | tail = noneStr(elem.tail) | 
|  | 659 | if (elem.tag == 'name'): | 
|  | 660 | # Align at specified column, if possible | 
|  | 661 | newLen = len(paramdecl.rstrip()) | 
|  | 662 | self.logMsg('diag', 'Identifying length of', elem.text, 'as', newLen) | 
|  | 663 | paramdecl += text + tail | 
|  | 664 | return newLen | 
|  | 665 | # | 
|  | 666 | # makeCDecls - return C prototype and function pointer typedef for a | 
|  | 667 | #   command, as a two-element list of strings. | 
|  | 668 | # cmd - Element containing a <command> tag | 
|  | 669 | def makeCDecls(self, cmd): | 
|  | 670 | """Generate C function pointer typedef for <command> Element""" | 
|  | 671 | proto = cmd.find('proto') | 
|  | 672 | params = cmd.findall('param') | 
|  | 673 | # Begin accumulating prototype and typedef strings | 
|  | 674 | pdecl = self.genOpts.apicall | 
|  | 675 | tdecl = 'typedef ' | 
|  | 676 | # | 
|  | 677 | # Insert the function return type/name. | 
|  | 678 | # For prototypes, add APIENTRY macro before the name | 
|  | 679 | # For typedefs, add (APIENTRY *<name>) around the name and | 
|  | 680 | #   use the PFN_cmdnameproc naming convention. | 
|  | 681 | # Done by walking the tree for <proto> element by element. | 
| Mike Stroyan | 0cf28a2 | 2016-04-05 16:40:30 -0600 | [diff] [blame] | 682 | # etree has elem.text followed by (elem[i], elem[i].tail) | 
| Mike Stroyan | a451fa8 | 2016-01-07 15:35:37 -0700 | [diff] [blame] | 683 | #   for each child element and any following text | 
|  | 684 | # Leading text | 
|  | 685 | pdecl += noneStr(proto.text) | 
|  | 686 | tdecl += noneStr(proto.text) | 
|  | 687 | # For each child element, if it's a <name> wrap in appropriate | 
|  | 688 | # declaration. Otherwise append its contents and tail contents. | 
|  | 689 | for elem in proto: | 
|  | 690 | text = noneStr(elem.text) | 
|  | 691 | tail = noneStr(elem.tail) | 
|  | 692 | if (elem.tag == 'name'): | 
|  | 693 | pdecl += self.makeProtoName(text, tail) | 
|  | 694 | tdecl += self.makeTypedefName(text, tail) | 
|  | 695 | else: | 
|  | 696 | pdecl += text + tail | 
|  | 697 | tdecl += text + tail | 
|  | 698 | # Now add the parameter declaration list, which is identical | 
|  | 699 | # for prototypes and typedefs. Concatenate all the text from | 
|  | 700 | # a <param> node without the tags. No tree walking required | 
|  | 701 | # since all tags are ignored. | 
|  | 702 | # Uses: self.indentFuncProto | 
|  | 703 | # self.indentFuncPointer | 
|  | 704 | # self.alignFuncParam | 
|  | 705 | # Might be able to doubly-nest the joins, e.g. | 
|  | 706 | #   ','.join(('_'.join([l[i] for i in range(0,len(l))]) | 
|  | 707 | n = len(params) | 
|  | 708 | # Indented parameters | 
|  | 709 | if n > 0: | 
|  | 710 | indentdecl = '(\n' | 
|  | 711 | for i in range(0,n): | 
|  | 712 | paramdecl = self.makeCParamDecl(params[i], self.genOpts.alignFuncParam) | 
|  | 713 | if (i < n - 1): | 
|  | 714 | paramdecl += ',\n' | 
|  | 715 | else: | 
|  | 716 | paramdecl += ');' | 
|  | 717 | indentdecl += paramdecl | 
|  | 718 | else: | 
|  | 719 | indentdecl = '(void);' | 
|  | 720 | # Non-indented parameters | 
|  | 721 | paramdecl = '(' | 
|  | 722 | if n > 0: | 
|  | 723 | for i in range(0,n): | 
|  | 724 | paramdecl += ''.join([t for t in params[i].itertext()]) | 
|  | 725 | if (i < n - 1): | 
|  | 726 | paramdecl += ', ' | 
|  | 727 | else: | 
|  | 728 | paramdecl += 'void' | 
|  | 729 | paramdecl += ");"; | 
|  | 730 | return [ pdecl + indentdecl, tdecl + paramdecl ] | 
|  | 731 | # | 
|  | 732 | def newline(self): | 
|  | 733 | write('', file=self.outFile) | 
|  | 734 |  | 
|  | 735 | def setRegistry(self, registry): | 
|  | 736 | self.registry = registry | 
|  | 737 | # | 
|  | 738 |  | 
|  | 739 | # COutputGenerator - subclass of OutputGenerator. | 
|  | 740 | # Generates C-language API interfaces. | 
|  | 741 | # | 
|  | 742 | # ---- methods ---- | 
|  | 743 | # COutputGenerator(errFile, warnFile, diagFile) - args as for | 
|  | 744 | #   OutputGenerator. Defines additional internal state. | 
|  | 745 | # ---- methods overriding base class ---- | 
|  | 746 | # beginFile(genOpts) | 
|  | 747 | # endFile() | 
|  | 748 | # beginFeature(interface, emit) | 
|  | 749 | # endFeature() | 
|  | 750 | # genType(typeinfo,name) | 
|  | 751 | # genStruct(typeinfo,name) | 
|  | 752 | # genGroup(groupinfo,name) | 
|  | 753 | # genEnum(enuminfo, name) | 
|  | 754 | # genCmd(cmdinfo) | 
|  | 755 | class COutputGenerator(OutputGenerator): | 
|  | 756 | """Generate specified API interfaces in a specific style, such as a C header""" | 
|  | 757 | # This is an ordered list of sections in the header file. | 
|  | 758 | TYPE_SECTIONS = ['include', 'define', 'basetype', 'handle', 'enum', | 
|  | 759 | 'group', 'bitmask', 'funcpointer', 'struct'] | 
|  | 760 | ALL_SECTIONS = TYPE_SECTIONS + ['commandPointer', 'command'] | 
|  | 761 | def __init__(self, | 
|  | 762 | errFile = sys.stderr, | 
|  | 763 | warnFile = sys.stderr, | 
|  | 764 | diagFile = sys.stdout): | 
|  | 765 | OutputGenerator.__init__(self, errFile, warnFile, diagFile) | 
|  | 766 | # Internal state - accumulators for different inner block text | 
|  | 767 | self.sections = dict([(section, []) for section in self.ALL_SECTIONS]) | 
|  | 768 | # | 
|  | 769 | def beginFile(self, genOpts): | 
|  | 770 | OutputGenerator.beginFile(self, genOpts) | 
|  | 771 | # C-specific | 
|  | 772 | # | 
|  | 773 | # Multiple inclusion protection & C++ wrappers. | 
|  | 774 | if (genOpts.protectFile and self.genOpts.filename): | 
| Dustin Graves | b8458d1 | 2016-03-28 16:17:38 -0600 | [diff] [blame] | 775 | headerSym = re.sub('\.h', '_h_', | 
|  | 776 | os.path.basename(self.genOpts.filename)).upper() | 
| Mike Stroyan | a451fa8 | 2016-01-07 15:35:37 -0700 | [diff] [blame] | 777 | write('#ifndef', headerSym, file=self.outFile) | 
|  | 778 | write('#define', headerSym, '1', file=self.outFile) | 
|  | 779 | self.newline() | 
|  | 780 | write('#ifdef __cplusplus', file=self.outFile) | 
|  | 781 | write('extern "C" {', file=self.outFile) | 
|  | 782 | write('#endif', file=self.outFile) | 
|  | 783 | self.newline() | 
|  | 784 | # | 
|  | 785 | # User-supplied prefix text, if any (list of strings) | 
|  | 786 | if (genOpts.prefixText): | 
|  | 787 | for s in genOpts.prefixText: | 
|  | 788 | write(s, file=self.outFile) | 
|  | 789 | # | 
|  | 790 | # Some boilerplate describing what was generated - this | 
|  | 791 | # will probably be removed later since the extensions | 
|  | 792 | # pattern may be very long. | 
|  | 793 | # write('/* Generated C header for:', file=self.outFile) | 
|  | 794 | # write(' * API:', genOpts.apiname, file=self.outFile) | 
|  | 795 | # if (genOpts.profile): | 
|  | 796 | #     write(' * Profile:', genOpts.profile, file=self.outFile) | 
|  | 797 | # write(' * Versions considered:', genOpts.versions, file=self.outFile) | 
|  | 798 | # write(' * Versions emitted:', genOpts.emitversions, file=self.outFile) | 
|  | 799 | # write(' * Default extensions included:', genOpts.defaultExtensions, file=self.outFile) | 
|  | 800 | # write(' * Additional extensions included:', genOpts.addExtensions, file=self.outFile) | 
|  | 801 | # write(' * Extensions removed:', genOpts.removeExtensions, file=self.outFile) | 
|  | 802 | # write(' */', file=self.outFile) | 
|  | 803 | def endFile(self): | 
|  | 804 | # C-specific | 
|  | 805 | # Finish C++ wrapper and multiple inclusion protection | 
|  | 806 | self.newline() | 
|  | 807 | write('#ifdef __cplusplus', file=self.outFile) | 
|  | 808 | write('}', file=self.outFile) | 
|  | 809 | write('#endif', file=self.outFile) | 
|  | 810 | if (self.genOpts.protectFile and self.genOpts.filename): | 
|  | 811 | self.newline() | 
|  | 812 | write('#endif', file=self.outFile) | 
|  | 813 | # Finish processing in superclass | 
|  | 814 | OutputGenerator.endFile(self) | 
|  | 815 | def beginFeature(self, interface, emit): | 
|  | 816 | # Start processing in superclass | 
|  | 817 | OutputGenerator.beginFeature(self, interface, emit) | 
|  | 818 | # C-specific | 
|  | 819 | # Accumulate includes, defines, types, enums, function pointer typedefs, | 
|  | 820 | # end function prototypes separately for this feature. They're only | 
|  | 821 | # printed in endFeature(). | 
|  | 822 | self.sections = dict([(section, []) for section in self.ALL_SECTIONS]) | 
|  | 823 | def endFeature(self): | 
|  | 824 | # C-specific | 
|  | 825 | # Actually write the interface to the output file. | 
|  | 826 | if (self.emit): | 
|  | 827 | self.newline() | 
|  | 828 | if (self.genOpts.protectFeature): | 
|  | 829 | write('#ifndef', self.featureName, file=self.outFile) | 
|  | 830 | # If type declarations are needed by other features based on | 
|  | 831 | # this one, it may be necessary to suppress the ExtraProtect, | 
|  | 832 | # or move it below the 'for section...' loop. | 
|  | 833 | if (self.featureExtraProtect != None): | 
|  | 834 | write('#ifdef', self.featureExtraProtect, file=self.outFile) | 
|  | 835 | write('#define', self.featureName, '1', file=self.outFile) | 
|  | 836 | for section in self.TYPE_SECTIONS: | 
|  | 837 | contents = self.sections[section] | 
|  | 838 | if contents: | 
|  | 839 | write('\n'.join(contents), file=self.outFile) | 
|  | 840 | self.newline() | 
|  | 841 | if (self.genOpts.genFuncPointers and self.sections['commandPointer']): | 
|  | 842 | write('\n'.join(self.sections['commandPointer']), file=self.outFile) | 
|  | 843 | self.newline() | 
|  | 844 | if (self.sections['command']): | 
|  | 845 | if (self.genOpts.protectProto): | 
|  | 846 | write(self.genOpts.protectProto, | 
|  | 847 | self.genOpts.protectProtoStr, file=self.outFile) | 
|  | 848 | write('\n'.join(self.sections['command']), end='', file=self.outFile) | 
|  | 849 | if (self.genOpts.protectProto): | 
|  | 850 | write('#endif', file=self.outFile) | 
|  | 851 | else: | 
|  | 852 | self.newline() | 
|  | 853 | if (self.featureExtraProtect != None): | 
|  | 854 | write('#endif /*', self.featureExtraProtect, '*/', file=self.outFile) | 
|  | 855 | if (self.genOpts.protectFeature): | 
|  | 856 | write('#endif /*', self.featureName, '*/', file=self.outFile) | 
|  | 857 | # Finish processing in superclass | 
|  | 858 | OutputGenerator.endFeature(self) | 
|  | 859 | # | 
|  | 860 | # Append a definition to the specified section | 
|  | 861 | def appendSection(self, section, text): | 
|  | 862 | # self.sections[section].append('SECTION: ' + section + '\n') | 
|  | 863 | self.sections[section].append(text) | 
|  | 864 | # | 
|  | 865 | # Type generation | 
|  | 866 | def genType(self, typeinfo, name): | 
|  | 867 | OutputGenerator.genType(self, typeinfo, name) | 
|  | 868 | typeElem = typeinfo.elem | 
|  | 869 | # If the type is a struct type, traverse the imbedded <member> tags | 
|  | 870 | # generating a structure. Otherwise, emit the tag text. | 
|  | 871 | category = typeElem.get('category') | 
|  | 872 | if (category == 'struct' or category == 'union'): | 
|  | 873 | self.genStruct(typeinfo, name) | 
|  | 874 | else: | 
|  | 875 | # Replace <apientry /> tags with an APIENTRY-style string | 
|  | 876 | # (from self.genOpts). Copy other text through unchanged. | 
|  | 877 | # If the resulting text is an empty string, don't emit it. | 
|  | 878 | s = noneStr(typeElem.text) | 
|  | 879 | for elem in typeElem: | 
|  | 880 | if (elem.tag == 'apientry'): | 
|  | 881 | s += self.genOpts.apientry + noneStr(elem.tail) | 
|  | 882 | else: | 
|  | 883 | s += noneStr(elem.text) + noneStr(elem.tail) | 
|  | 884 | if s: | 
|  | 885 | # Add extra newline after multi-line entries. | 
|  | 886 | if '\n' in s: | 
|  | 887 | s += '\n' | 
|  | 888 | self.appendSection(category, s) | 
|  | 889 | # | 
|  | 890 | # Struct (e.g. C "struct" type) generation. | 
|  | 891 | # This is a special case of the <type> tag where the contents are | 
|  | 892 | # interpreted as a set of <member> tags instead of freeform C | 
|  | 893 | # C type declarations. The <member> tags are just like <param> | 
|  | 894 | # tags - they are a declaration of a struct or union member. | 
|  | 895 | # Only simple member declarations are supported (no nested | 
|  | 896 | # structs etc.) | 
|  | 897 | def genStruct(self, typeinfo, typeName): | 
|  | 898 | OutputGenerator.genStruct(self, typeinfo, typeName) | 
|  | 899 | body = 'typedef ' + typeinfo.elem.get('category') + ' ' + typeName + ' {\n' | 
|  | 900 | # paramdecl = self.makeCParamDecl(typeinfo.elem, self.genOpts.alignFuncParam) | 
|  | 901 | targetLen = 0; | 
|  | 902 | for member in typeinfo.elem.findall('.//member'): | 
|  | 903 | targetLen = max(targetLen, self.getCParamTypeLength(member)) | 
|  | 904 | for member in typeinfo.elem.findall('.//member'): | 
|  | 905 | body += self.makeCParamDecl(member, targetLen + 4) | 
|  | 906 | body += ';\n' | 
|  | 907 | body += '} ' + typeName + ';\n' | 
|  | 908 | self.appendSection('struct', body) | 
|  | 909 | # | 
|  | 910 | # Group (e.g. C "enum" type) generation. | 
|  | 911 | # These are concatenated together with other types. | 
|  | 912 | def genGroup(self, groupinfo, groupName): | 
|  | 913 | OutputGenerator.genGroup(self, groupinfo, groupName) | 
|  | 914 | groupElem = groupinfo.elem | 
| Dustin Graves | b8458d1 | 2016-03-28 16:17:38 -0600 | [diff] [blame] | 915 |  | 
|  | 916 | expandName = re.sub(r'([0-9a-z_])([A-Z0-9][^A-Z0-9]?)',r'\1_\2',groupName).upper() | 
|  | 917 |  | 
|  | 918 | expandPrefix = expandName | 
|  | 919 | expandSuffix = '' | 
|  | 920 | expandSuffixMatch = re.search(r'[A-Z][A-Z]+$',groupName) | 
|  | 921 | if expandSuffixMatch: | 
|  | 922 | expandSuffix = '_' + expandSuffixMatch.group() | 
|  | 923 | # Strip off the suffix from the prefix | 
|  | 924 | expandPrefix = expandName.rsplit(expandSuffix, 1)[0] | 
|  | 925 |  | 
| Mike Stroyan | a451fa8 | 2016-01-07 15:35:37 -0700 | [diff] [blame] | 926 | # Prefix | 
|  | 927 | body = "\ntypedef enum " + groupName + " {\n" | 
|  | 928 |  | 
| Dustin Graves | b8458d1 | 2016-03-28 16:17:38 -0600 | [diff] [blame] | 929 | isEnum = ('FLAG_BITS' not in expandPrefix) | 
|  | 930 |  | 
| Mike Stroyan | a451fa8 | 2016-01-07 15:35:37 -0700 | [diff] [blame] | 931 | # Loop over the nested 'enum' tags. Keep track of the minimum and | 
|  | 932 | # maximum numeric values, if they can be determined; but only for | 
|  | 933 | # core API enumerants, not extension enumerants. This is inferred | 
|  | 934 | # by looking for 'extends' attributes. | 
|  | 935 | minName = None | 
|  | 936 | for elem in groupElem.findall('enum'): | 
|  | 937 | # Convert the value to an integer and use that to track min/max. | 
|  | 938 | # Values of form -(number) are accepted but nothing more complex. | 
|  | 939 | # Should catch exceptions here for more complex constructs. Not yet. | 
|  | 940 | (numVal,strVal) = self.enumToValue(elem, True) | 
|  | 941 | name = elem.get('name') | 
| Dustin Graves | b8458d1 | 2016-03-28 16:17:38 -0600 | [diff] [blame] | 942 |  | 
|  | 943 | # Extension enumerants are only included if they are requested | 
|  | 944 | # in addExtensions or match defaultExtensions. | 
|  | 945 | if (elem.get('extname') is None or | 
|  | 946 | re.match(self.genOpts.addExtensions,elem.get('extname')) is not None or | 
|  | 947 | self.genOpts.defaultExtensions == elem.get('supported')): | 
|  | 948 | body += "    " + name + " = " + strVal + ",\n" | 
|  | 949 |  | 
|  | 950 | if (isEnum  and elem.get('extends') is None): | 
| Mike Stroyan | a451fa8 | 2016-01-07 15:35:37 -0700 | [diff] [blame] | 951 | if (minName == None): | 
|  | 952 | minName = maxName = name | 
|  | 953 | minValue = maxValue = numVal | 
|  | 954 | elif (numVal < minValue): | 
|  | 955 | minName = name | 
|  | 956 | minValue = numVal | 
|  | 957 | elif (numVal > maxValue): | 
|  | 958 | maxName = name | 
|  | 959 | maxValue = numVal | 
|  | 960 | # Generate min/max value tokens and a range-padding enum. Need some | 
|  | 961 | # additional padding to generate correct names... | 
| Dustin Graves | b8458d1 | 2016-03-28 16:17:38 -0600 | [diff] [blame] | 962 | if isEnum: | 
|  | 963 | body += "    " + expandPrefix + "_BEGIN_RANGE" + expandSuffix + " = " + minName + ",\n" | 
|  | 964 | body += "    " + expandPrefix + "_END_RANGE" + expandSuffix + " = " + maxName + ",\n" | 
|  | 965 | body += "    " + expandPrefix + "_RANGE_SIZE" + expandSuffix + " = (" + maxName + " - " + minName + " + 1),\n" | 
|  | 966 |  | 
|  | 967 | body += "    " + expandPrefix + "_MAX_ENUM" + expandSuffix + " = 0x7FFFFFFF\n" | 
|  | 968 |  | 
| Mike Stroyan | a451fa8 | 2016-01-07 15:35:37 -0700 | [diff] [blame] | 969 | # Postfix | 
|  | 970 | body += "} " + groupName + ";" | 
|  | 971 | if groupElem.get('type') == 'bitmask': | 
|  | 972 | section = 'bitmask' | 
|  | 973 | else: | 
|  | 974 | section = 'group' | 
|  | 975 | self.appendSection(section, body) | 
|  | 976 | # Enumerant generation | 
|  | 977 | # <enum> tags may specify their values in several ways, but are usually | 
|  | 978 | # just integers. | 
|  | 979 | def genEnum(self, enuminfo, name): | 
|  | 980 | OutputGenerator.genEnum(self, enuminfo, name) | 
|  | 981 | (numVal,strVal) = self.enumToValue(enuminfo.elem, False) | 
|  | 982 | body = '#define ' + name.ljust(33) + ' ' + strVal | 
|  | 983 | self.appendSection('enum', body) | 
|  | 984 | # | 
|  | 985 | # Command generation | 
|  | 986 | def genCmd(self, cmdinfo, name): | 
|  | 987 | OutputGenerator.genCmd(self, cmdinfo, name) | 
|  | 988 | # | 
|  | 989 | decls = self.makeCDecls(cmdinfo.elem) | 
|  | 990 | self.appendSection('command', decls[0] + '\n') | 
|  | 991 | if (self.genOpts.genFuncPointers): | 
|  | 992 | self.appendSection('commandPointer', decls[1]) | 
|  | 993 |  | 
|  | 994 | # DocOutputGenerator - subclass of OutputGenerator. | 
|  | 995 | # Generates AsciiDoc includes with C-language API interfaces, for reference | 
|  | 996 | # pages and the Vulkan specification. Similar to COutputGenerator, but | 
|  | 997 | # each interface is written into a different file as determined by the | 
|  | 998 | # options, only actual C types are emitted, and none of the boilerplate | 
|  | 999 | # preprocessor code is emitted. | 
|  | 1000 | # | 
|  | 1001 | # ---- methods ---- | 
|  | 1002 | # DocOutputGenerator(errFile, warnFile, diagFile) - args as for | 
|  | 1003 | #   OutputGenerator. Defines additional internal state. | 
|  | 1004 | # ---- methods overriding base class ---- | 
|  | 1005 | # beginFile(genOpts) | 
|  | 1006 | # endFile() | 
|  | 1007 | # beginFeature(interface, emit) | 
|  | 1008 | # endFeature() | 
|  | 1009 | # genType(typeinfo,name) | 
|  | 1010 | # genStruct(typeinfo,name) | 
|  | 1011 | # genGroup(groupinfo,name) | 
|  | 1012 | # genEnum(enuminfo, name) | 
|  | 1013 | # genCmd(cmdinfo) | 
|  | 1014 | class DocOutputGenerator(OutputGenerator): | 
|  | 1015 | """Generate specified API interfaces in a specific style, such as a C header""" | 
|  | 1016 | def __init__(self, | 
|  | 1017 | errFile = sys.stderr, | 
|  | 1018 | warnFile = sys.stderr, | 
|  | 1019 | diagFile = sys.stdout): | 
|  | 1020 | OutputGenerator.__init__(self, errFile, warnFile, diagFile) | 
|  | 1021 | # | 
|  | 1022 | def beginFile(self, genOpts): | 
|  | 1023 | OutputGenerator.beginFile(self, genOpts) | 
|  | 1024 | def endFile(self): | 
|  | 1025 | OutputGenerator.endFile(self) | 
|  | 1026 | def beginFeature(self, interface, emit): | 
|  | 1027 | # Start processing in superclass | 
|  | 1028 | OutputGenerator.beginFeature(self, interface, emit) | 
|  | 1029 | def endFeature(self): | 
|  | 1030 | # Finish processing in superclass | 
|  | 1031 | OutputGenerator.endFeature(self) | 
|  | 1032 | # | 
|  | 1033 | # Generate an include file | 
|  | 1034 | # | 
|  | 1035 | # directory - subdirectory to put file in | 
|  | 1036 | # basename - base name of the file | 
|  | 1037 | # contents - contents of the file (Asciidoc boilerplate aside) | 
|  | 1038 | def writeInclude(self, directory, basename, contents): | 
|  | 1039 | # Create file | 
|  | 1040 | filename = self.genOpts.genDirectory + '/' + directory + '/' + basename + '.txt' | 
|  | 1041 | self.logMsg('diag', '# Generating include file:', filename) | 
|  | 1042 | fp = open(filename, 'w') | 
|  | 1043 | # Asciidoc anchor | 
| Dustin Graves | b8458d1 | 2016-03-28 16:17:38 -0600 | [diff] [blame] | 1044 | write('// WARNING: DO NOT MODIFY! This file is automatically generated from the vk.xml registry', file=fp) | 
|  | 1045 | write('ifndef::doctype-manpage[]', file=fp) | 
| Mike Stroyan | a451fa8 | 2016-01-07 15:35:37 -0700 | [diff] [blame] | 1046 | write('[[{0},{0}]]'.format(basename), file=fp) | 
|  | 1047 | write('["source","{basebackend@docbook:c++:cpp}",title=""]', file=fp) | 
| Dustin Graves | b8458d1 | 2016-03-28 16:17:38 -0600 | [diff] [blame] | 1048 | write('endif::doctype-manpage[]', file=fp) | 
|  | 1049 | write('ifdef::doctype-manpage[]', file=fp) | 
|  | 1050 | write('["source","{basebackend@docbook:c++:cpp}"]', file=fp) | 
|  | 1051 | write('endif::doctype-manpage[]', file=fp) | 
| Mike Stroyan | a451fa8 | 2016-01-07 15:35:37 -0700 | [diff] [blame] | 1052 | write('------------------------------------------------------------------------------', file=fp) | 
|  | 1053 | write(contents, file=fp) | 
|  | 1054 | write('------------------------------------------------------------------------------', file=fp) | 
|  | 1055 | fp.close() | 
|  | 1056 | # | 
|  | 1057 | # Type generation | 
|  | 1058 | def genType(self, typeinfo, name): | 
|  | 1059 | OutputGenerator.genType(self, typeinfo, name) | 
|  | 1060 | typeElem = typeinfo.elem | 
|  | 1061 | # If the type is a struct type, traverse the imbedded <member> tags | 
|  | 1062 | # generating a structure. Otherwise, emit the tag text. | 
|  | 1063 | category = typeElem.get('category') | 
|  | 1064 | if (category == 'struct' or category == 'union'): | 
|  | 1065 | self.genStruct(typeinfo, name) | 
|  | 1066 | else: | 
|  | 1067 | # Replace <apientry /> tags with an APIENTRY-style string | 
|  | 1068 | # (from self.genOpts). Copy other text through unchanged. | 
|  | 1069 | # If the resulting text is an empty string, don't emit it. | 
|  | 1070 | s = noneStr(typeElem.text) | 
|  | 1071 | for elem in typeElem: | 
|  | 1072 | if (elem.tag == 'apientry'): | 
|  | 1073 | s += self.genOpts.apientry + noneStr(elem.tail) | 
|  | 1074 | else: | 
|  | 1075 | s += noneStr(elem.text) + noneStr(elem.tail) | 
|  | 1076 | if (len(s) > 0): | 
|  | 1077 | if (category == 'bitmask'): | 
|  | 1078 | self.writeInclude('flags', name, s + '\n') | 
|  | 1079 | elif (category == 'enum'): | 
|  | 1080 | self.writeInclude('enums', name, s + '\n') | 
|  | 1081 | elif (category == 'funcpointer'): | 
|  | 1082 | self.writeInclude('funcpointers', name, s+ '\n') | 
|  | 1083 | else: | 
|  | 1084 | self.logMsg('diag', '# NOT writing include file for type:', | 
|  | 1085 | name, 'category: ', category) | 
|  | 1086 | else: | 
|  | 1087 | self.logMsg('diag', '# NOT writing empty include file for type', name) | 
|  | 1088 | # | 
|  | 1089 | # Struct (e.g. C "struct" type) generation. | 
|  | 1090 | # This is a special case of the <type> tag where the contents are | 
|  | 1091 | # interpreted as a set of <member> tags instead of freeform C | 
|  | 1092 | # C type declarations. The <member> tags are just like <param> | 
|  | 1093 | # tags - they are a declaration of a struct or union member. | 
|  | 1094 | # Only simple member declarations are supported (no nested | 
|  | 1095 | # structs etc.) | 
|  | 1096 | def genStruct(self, typeinfo, typeName): | 
|  | 1097 | OutputGenerator.genStruct(self, typeinfo, typeName) | 
|  | 1098 | s = 'typedef ' + typeinfo.elem.get('category') + ' ' + typeName + ' {\n' | 
|  | 1099 | # paramdecl = self.makeCParamDecl(typeinfo.elem, self.genOpts.alignFuncParam) | 
|  | 1100 | targetLen = 0; | 
|  | 1101 | for member in typeinfo.elem.findall('.//member'): | 
|  | 1102 | targetLen = max(targetLen, self.getCParamTypeLength(member)) | 
|  | 1103 | for member in typeinfo.elem.findall('.//member'): | 
|  | 1104 | s += self.makeCParamDecl(member, targetLen + 4) | 
|  | 1105 | s += ';\n' | 
|  | 1106 | s += '} ' + typeName + ';' | 
|  | 1107 | self.writeInclude('structs', typeName, s) | 
|  | 1108 | # | 
|  | 1109 | # Group (e.g. C "enum" type) generation. | 
|  | 1110 | # These are concatenated together with other types. | 
|  | 1111 | def genGroup(self, groupinfo, groupName): | 
|  | 1112 | OutputGenerator.genGroup(self, groupinfo, groupName) | 
|  | 1113 | groupElem = groupinfo.elem | 
| Dustin Graves | b8458d1 | 2016-03-28 16:17:38 -0600 | [diff] [blame] | 1114 |  | 
|  | 1115 | # See if we need min/max/num/padding at end | 
|  | 1116 | expand = self.genOpts.expandEnumerants | 
|  | 1117 |  | 
|  | 1118 | if expand: | 
|  | 1119 | expandName = re.sub(r'([0-9a-z_])([A-Z0-9][^A-Z0-9]?)',r'\1_\2',groupName).upper() | 
|  | 1120 | isEnum = ('FLAG_BITS' not in expandName) | 
|  | 1121 |  | 
|  | 1122 | expandPrefix = expandName | 
|  | 1123 | expandSuffix = '' | 
|  | 1124 |  | 
|  | 1125 | # Look for a suffix | 
|  | 1126 | expandSuffixMatch = re.search(r'[A-Z][A-Z]+$',groupName) | 
|  | 1127 | if expandSuffixMatch: | 
|  | 1128 | expandSuffix = '_' + expandSuffixMatch.group() | 
|  | 1129 | # Strip off the suffix from the prefix | 
|  | 1130 | expandPrefix = expandName.rsplit(expandSuffix, 1)[0] | 
|  | 1131 |  | 
| Mike Stroyan | a451fa8 | 2016-01-07 15:35:37 -0700 | [diff] [blame] | 1132 | # Prefix | 
|  | 1133 | s = "typedef enum " + groupName + " {\n" | 
|  | 1134 |  | 
|  | 1135 | # Loop over the nested 'enum' tags. Keep track of the minimum and | 
|  | 1136 | # maximum numeric values, if they can be determined. | 
|  | 1137 | minName = None | 
|  | 1138 | for elem in groupElem.findall('enum'): | 
|  | 1139 | # Convert the value to an integer and use that to track min/max. | 
|  | 1140 | # Values of form -(number) are accepted but nothing more complex. | 
|  | 1141 | # Should catch exceptions here for more complex constructs. Not yet. | 
|  | 1142 | (numVal,strVal) = self.enumToValue(elem, True) | 
|  | 1143 | name = elem.get('name') | 
| Dustin Graves | b8458d1 | 2016-03-28 16:17:38 -0600 | [diff] [blame] | 1144 |  | 
|  | 1145 | # Extension enumerants are only included if they are requested | 
|  | 1146 | # in addExtensions or match defaultExtensions. | 
|  | 1147 | if (elem.get('extname') is None or | 
|  | 1148 | re.match(self.genOpts.addExtensions,elem.get('extname')) is not None or | 
|  | 1149 | self.genOpts.defaultExtensions == elem.get('supported')): | 
|  | 1150 | s += "    " + name + " = " + strVal + ",\n" | 
|  | 1151 |  | 
|  | 1152 | if (expand and isEnum and elem.get('extends') is None): | 
| Mike Stroyan | a451fa8 | 2016-01-07 15:35:37 -0700 | [diff] [blame] | 1153 | if (minName == None): | 
|  | 1154 | minName = maxName = name | 
|  | 1155 | minValue = maxValue = numVal | 
|  | 1156 | elif (numVal < minValue): | 
|  | 1157 | minName = name | 
|  | 1158 | minValue = numVal | 
|  | 1159 | elif (numVal > maxValue): | 
|  | 1160 | maxName = name | 
|  | 1161 | maxValue = numVal | 
|  | 1162 | # Generate min/max value tokens and a range-padding enum. Need some | 
|  | 1163 | # additional padding to generate correct names... | 
|  | 1164 | if (expand): | 
|  | 1165 | s += "\n" | 
| Dustin Graves | b8458d1 | 2016-03-28 16:17:38 -0600 | [diff] [blame] | 1166 | if isEnum: | 
|  | 1167 | s += "    " + expandPrefix + "_BEGIN_RANGE" + expandSuffix + " = " + minName + ",\n" | 
|  | 1168 | s += "    " + expandPrefix + "_END_RANGE" + expandSuffix + " = " + maxName + ",\n" | 
|  | 1169 | s += "    " + expandPrefix + "_RANGE_SIZE" + expandSuffix + " = (" + maxName + " - " + minName + " + 1),\n" | 
|  | 1170 |  | 
|  | 1171 | s += "    " + expandPrefix + "_MAX_ENUM" + expandSuffix + " = 0x7FFFFFFF\n" | 
| Mike Stroyan | a451fa8 | 2016-01-07 15:35:37 -0700 | [diff] [blame] | 1172 | # Postfix | 
|  | 1173 | s += "} " + groupName + ";" | 
|  | 1174 | self.writeInclude('enums', groupName, s) | 
|  | 1175 | # Enumerant generation | 
|  | 1176 | # <enum> tags may specify their values in several ways, but are usually | 
|  | 1177 | # just integers. | 
|  | 1178 | def genEnum(self, enuminfo, name): | 
|  | 1179 | OutputGenerator.genEnum(self, enuminfo, name) | 
|  | 1180 | (numVal,strVal) = self.enumToValue(enuminfo.elem, False) | 
|  | 1181 | s = '#define ' + name.ljust(33) + ' ' + strVal | 
|  | 1182 | self.logMsg('diag', '# NOT writing compile-time constant', name) | 
|  | 1183 | # self.writeInclude('consts', name, s) | 
|  | 1184 | # | 
|  | 1185 | # Command generation | 
|  | 1186 | def genCmd(self, cmdinfo, name): | 
|  | 1187 | OutputGenerator.genCmd(self, cmdinfo, name) | 
|  | 1188 | # | 
|  | 1189 | decls = self.makeCDecls(cmdinfo.elem) | 
|  | 1190 | self.writeInclude('protos', name, decls[0]) | 
|  | 1191 |  | 
|  | 1192 | # PyOutputGenerator - subclass of OutputGenerator. | 
|  | 1193 | # Generates Python data structures describing API names. | 
|  | 1194 | # Similar to DocOutputGenerator, but writes a single | 
|  | 1195 | # file. | 
|  | 1196 | # | 
|  | 1197 | # ---- methods ---- | 
|  | 1198 | # PyOutputGenerator(errFile, warnFile, diagFile) - args as for | 
|  | 1199 | #   OutputGenerator. Defines additional internal state. | 
|  | 1200 | # ---- methods overriding base class ---- | 
|  | 1201 | # beginFile(genOpts) | 
|  | 1202 | # endFile() | 
|  | 1203 | # genType(typeinfo,name) | 
|  | 1204 | # genStruct(typeinfo,name) | 
|  | 1205 | # genGroup(groupinfo,name) | 
|  | 1206 | # genEnum(enuminfo, name) | 
|  | 1207 | # genCmd(cmdinfo) | 
|  | 1208 | class PyOutputGenerator(OutputGenerator): | 
|  | 1209 | """Generate specified API interfaces in a specific style, such as a C header""" | 
|  | 1210 | def __init__(self, | 
|  | 1211 | errFile = sys.stderr, | 
|  | 1212 | warnFile = sys.stderr, | 
|  | 1213 | diagFile = sys.stdout): | 
|  | 1214 | OutputGenerator.__init__(self, errFile, warnFile, diagFile) | 
|  | 1215 | # | 
|  | 1216 | def beginFile(self, genOpts): | 
|  | 1217 | OutputGenerator.beginFile(self, genOpts) | 
|  | 1218 | for dict in [ 'flags', 'enums', 'structs', 'consts', 'enums', | 
|  | 1219 | 'consts', 'protos', 'funcpointers' ]: | 
|  | 1220 | write(dict, '= {}', file=self.outFile) | 
|  | 1221 | def endFile(self): | 
|  | 1222 | OutputGenerator.endFile(self) | 
|  | 1223 | # | 
|  | 1224 | # Add a name from the interface | 
|  | 1225 | # | 
|  | 1226 | # dict - type of name (see beginFile above) | 
|  | 1227 | # name - name to add | 
|  | 1228 | # value - A serializable Python value for the name | 
|  | 1229 | def addName(self, dict, name, value=None): | 
|  | 1230 | write(dict + "['" + name + "'] = ", value, file=self.outFile) | 
|  | 1231 | # | 
|  | 1232 | # Type generation | 
|  | 1233 | # For 'struct' or 'union' types, defer to genStruct() to | 
|  | 1234 | #   add to the dictionary. | 
|  | 1235 | # For 'bitmask' types, add the type name to the 'flags' dictionary, | 
|  | 1236 | #   with the value being the corresponding 'enums' name defining | 
|  | 1237 | #   the acceptable flag bits. | 
|  | 1238 | # For 'enum' types, add the type name to the 'enums' dictionary, | 
|  | 1239 | #   with the value being '@STOPHERE@' (because this case seems | 
|  | 1240 | #   never to happen). | 
|  | 1241 | # For 'funcpointer' types, add the type name to the 'funcpointers' | 
|  | 1242 | #   dictionary. | 
|  | 1243 | # For 'handle' and 'define' types, add the handle or #define name | 
|  | 1244 | #   to the 'struct' dictionary, because that's how the spec sources | 
|  | 1245 | #   tag these types even though they aren't structs. | 
|  | 1246 | def genType(self, typeinfo, name): | 
|  | 1247 | OutputGenerator.genType(self, typeinfo, name) | 
|  | 1248 | typeElem = typeinfo.elem | 
|  | 1249 | # If the type is a struct type, traverse the imbedded <member> tags | 
|  | 1250 | # generating a structure. Otherwise, emit the tag text. | 
|  | 1251 | category = typeElem.get('category') | 
|  | 1252 | if (category == 'struct' or category == 'union'): | 
|  | 1253 | self.genStruct(typeinfo, name) | 
|  | 1254 | else: | 
|  | 1255 | # Extract the type name | 
|  | 1256 | # (from self.genOpts). Copy other text through unchanged. | 
|  | 1257 | # If the resulting text is an empty string, don't emit it. | 
|  | 1258 | count = len(noneStr(typeElem.text)) | 
|  | 1259 | for elem in typeElem: | 
|  | 1260 | count += len(noneStr(elem.text)) + len(noneStr(elem.tail)) | 
|  | 1261 | if (count > 0): | 
|  | 1262 | if (category == 'bitmask'): | 
|  | 1263 | requiredEnum = typeElem.get('requires') | 
|  | 1264 | self.addName('flags', name, enquote(requiredEnum)) | 
|  | 1265 | elif (category == 'enum'): | 
|  | 1266 | # This case never seems to come up! | 
|  | 1267 | # @enums   C 'enum' name           Dictionary of enumerant names | 
|  | 1268 | self.addName('enums', name, enquote('@STOPHERE@')) | 
|  | 1269 | elif (category == 'funcpointer'): | 
|  | 1270 | self.addName('funcpointers', name, None) | 
|  | 1271 | elif (category == 'handle' or category == 'define'): | 
|  | 1272 | self.addName('structs', name, None) | 
|  | 1273 | else: | 
|  | 1274 | write('# Unprocessed type:', name, 'category:', category, file=self.outFile) | 
|  | 1275 | else: | 
|  | 1276 | write('# Unprocessed type:', name, file=self.outFile) | 
|  | 1277 | # | 
|  | 1278 | # Struct (e.g. C "struct" type) generation. | 
|  | 1279 | # | 
|  | 1280 | # Add the struct name to the 'structs' dictionary, with the | 
|  | 1281 | # value being an ordered list of the struct member names. | 
|  | 1282 | def genStruct(self, typeinfo, typeName): | 
|  | 1283 | OutputGenerator.genStruct(self, typeinfo, typeName) | 
|  | 1284 |  | 
|  | 1285 | members = [member.text for member in typeinfo.elem.findall('.//member/name')] | 
|  | 1286 | self.addName('structs', typeName, members) | 
|  | 1287 | # | 
|  | 1288 | # Group (e.g. C "enum" type) generation. | 
|  | 1289 | # These are concatenated together with other types. | 
|  | 1290 | # | 
|  | 1291 | # Add the enum type name to the 'enums' dictionary, with | 
|  | 1292 | #   the value being an ordered list of the enumerant names. | 
|  | 1293 | # Add each enumerant name to the 'consts' dictionary, with | 
|  | 1294 | #   the value being the enum type the enumerant is part of. | 
|  | 1295 | def genGroup(self, groupinfo, groupName): | 
|  | 1296 | OutputGenerator.genGroup(self, groupinfo, groupName) | 
|  | 1297 | groupElem = groupinfo.elem | 
|  | 1298 |  | 
|  | 1299 | # @enums   C 'enum' name           Dictionary of enumerant names | 
|  | 1300 | # @consts  C enumerant/const name  Name of corresponding 'enums' key | 
|  | 1301 |  | 
|  | 1302 | # Loop over the nested 'enum' tags. Keep track of the minimum and | 
|  | 1303 | # maximum numeric values, if they can be determined. | 
|  | 1304 | enumerants = [elem.get('name') for elem in groupElem.findall('enum')] | 
|  | 1305 | for name in enumerants: | 
|  | 1306 | self.addName('consts', name, enquote(groupName)) | 
|  | 1307 | self.addName('enums', groupName, enumerants) | 
|  | 1308 | # Enumerant generation (compile-time constants) | 
|  | 1309 | # | 
|  | 1310 | # Add the constant name to the 'consts' dictionary, with the | 
|  | 1311 | #   value being None to indicate that the constant isn't | 
|  | 1312 | #   an enumeration value. | 
|  | 1313 | def genEnum(self, enuminfo, name): | 
|  | 1314 | OutputGenerator.genEnum(self, enuminfo, name) | 
|  | 1315 |  | 
|  | 1316 | # @consts  C enumerant/const name  Name of corresponding 'enums' key | 
|  | 1317 |  | 
|  | 1318 | self.addName('consts', name, None) | 
|  | 1319 | # | 
|  | 1320 | # Command generation | 
|  | 1321 | # | 
|  | 1322 | # Add the command name to the 'protos' dictionary, with the | 
|  | 1323 | #   value being an ordered list of the parameter names. | 
|  | 1324 | def genCmd(self, cmdinfo, name): | 
|  | 1325 | OutputGenerator.genCmd(self, cmdinfo, name) | 
|  | 1326 |  | 
|  | 1327 | params = [param.text for param in cmdinfo.elem.findall('param/name')] | 
|  | 1328 | self.addName('protos', name, params) | 
|  | 1329 |  | 
|  | 1330 | # ValidityOutputGenerator - subclass of OutputGenerator. | 
|  | 1331 | # Generates AsciiDoc includes of valid usage information, for reference | 
|  | 1332 | # pages and the Vulkan specification. Similar to DocOutputGenerator. | 
|  | 1333 | # | 
|  | 1334 | # ---- methods ---- | 
|  | 1335 | # ValidityOutputGenerator(errFile, warnFile, diagFile) - args as for | 
|  | 1336 | #   OutputGenerator. Defines additional internal state. | 
|  | 1337 | # ---- methods overriding base class ---- | 
|  | 1338 | # beginFile(genOpts) | 
|  | 1339 | # endFile() | 
|  | 1340 | # beginFeature(interface, emit) | 
|  | 1341 | # endFeature() | 
|  | 1342 | # genCmd(cmdinfo) | 
|  | 1343 | class ValidityOutputGenerator(OutputGenerator): | 
|  | 1344 | """Generate specified API interfaces in a specific style, such as a C header""" | 
|  | 1345 | def __init__(self, | 
|  | 1346 | errFile = sys.stderr, | 
|  | 1347 | warnFile = sys.stderr, | 
|  | 1348 | diagFile = sys.stdout): | 
|  | 1349 | OutputGenerator.__init__(self, errFile, warnFile, diagFile) | 
|  | 1350 |  | 
|  | 1351 | def beginFile(self, genOpts): | 
|  | 1352 | OutputGenerator.beginFile(self, genOpts) | 
|  | 1353 | def endFile(self): | 
|  | 1354 | OutputGenerator.endFile(self) | 
|  | 1355 | def beginFeature(self, interface, emit): | 
|  | 1356 | # Start processing in superclass | 
|  | 1357 | OutputGenerator.beginFeature(self, interface, emit) | 
|  | 1358 | def endFeature(self): | 
|  | 1359 | # Finish processing in superclass | 
|  | 1360 | OutputGenerator.endFeature(self) | 
|  | 1361 |  | 
|  | 1362 | def makeParameterName(self, name): | 
|  | 1363 | return 'pname:' + name | 
|  | 1364 |  | 
|  | 1365 | def makeStructName(self, name): | 
|  | 1366 | return 'sname:' + name | 
|  | 1367 |  | 
|  | 1368 | def makeBaseTypeName(self, name): | 
|  | 1369 | return 'basetype:' + name | 
|  | 1370 |  | 
|  | 1371 | def makeEnumerationName(self, name): | 
|  | 1372 | return 'elink:' + name | 
|  | 1373 |  | 
|  | 1374 | def makeEnumerantName(self, name): | 
|  | 1375 | return 'ename:' + name | 
|  | 1376 |  | 
|  | 1377 | def makeFLink(self, name): | 
|  | 1378 | return 'flink:' + name | 
|  | 1379 |  | 
|  | 1380 | # | 
|  | 1381 | # Generate an include file | 
|  | 1382 | # | 
|  | 1383 | # directory - subdirectory to put file in | 
|  | 1384 | # basename - base name of the file | 
|  | 1385 | # contents - contents of the file (Asciidoc boilerplate aside) | 
|  | 1386 | def writeInclude(self, directory, basename, validity, threadsafety, commandpropertiesentry, successcodes, errorcodes): | 
|  | 1387 | # Create file | 
|  | 1388 | filename = self.genOpts.genDirectory + '/' + directory + '/' + basename + '.txt' | 
|  | 1389 | self.logMsg('diag', '# Generating include file:', filename) | 
|  | 1390 | fp = open(filename, 'w') | 
|  | 1391 | # Asciidoc anchor | 
| Dustin Graves | b8458d1 | 2016-03-28 16:17:38 -0600 | [diff] [blame] | 1392 | write('// WARNING: DO NOT MODIFY! This file is automatically generated from the vk.xml registry', file=fp) | 
| Mike Stroyan | a451fa8 | 2016-01-07 15:35:37 -0700 | [diff] [blame] | 1393 |  | 
|  | 1394 | # Valid Usage | 
|  | 1395 | if validity is not None: | 
| Dustin Graves | b8458d1 | 2016-03-28 16:17:38 -0600 | [diff] [blame] | 1396 | write('ifndef::doctype-manpage[]', file=fp) | 
| Mike Stroyan | a451fa8 | 2016-01-07 15:35:37 -0700 | [diff] [blame] | 1397 | write('.Valid Usage', file=fp) | 
|  | 1398 | write('*' * 80, file=fp) | 
| Dustin Graves | b8458d1 | 2016-03-28 16:17:38 -0600 | [diff] [blame] | 1399 | write('endif::doctype-manpage[]', file=fp) | 
|  | 1400 | write('ifdef::doctype-manpage[]', file=fp) | 
|  | 1401 | write('Valid Usage', file=fp) | 
|  | 1402 | write('-----------', file=fp) | 
|  | 1403 | write('endif::doctype-manpage[]', file=fp) | 
| Mike Stroyan | a451fa8 | 2016-01-07 15:35:37 -0700 | [diff] [blame] | 1404 | write(validity, file=fp, end='') | 
| Dustin Graves | b8458d1 | 2016-03-28 16:17:38 -0600 | [diff] [blame] | 1405 | write('ifndef::doctype-manpage[]', file=fp) | 
| Mike Stroyan | a451fa8 | 2016-01-07 15:35:37 -0700 | [diff] [blame] | 1406 | write('*' * 80, file=fp) | 
| Dustin Graves | b8458d1 | 2016-03-28 16:17:38 -0600 | [diff] [blame] | 1407 | write('endif::doctype-manpage[]', file=fp) | 
| Mike Stroyan | a451fa8 | 2016-01-07 15:35:37 -0700 | [diff] [blame] | 1408 | write('', file=fp) | 
|  | 1409 |  | 
|  | 1410 | # Host Synchronization | 
|  | 1411 | if threadsafety is not None: | 
| Dustin Graves | b8458d1 | 2016-03-28 16:17:38 -0600 | [diff] [blame] | 1412 | write('ifndef::doctype-manpage[]', file=fp) | 
| Mike Stroyan | a451fa8 | 2016-01-07 15:35:37 -0700 | [diff] [blame] | 1413 | write('.Host Synchronization', file=fp) | 
|  | 1414 | write('*' * 80, file=fp) | 
| Dustin Graves | b8458d1 | 2016-03-28 16:17:38 -0600 | [diff] [blame] | 1415 | write('endif::doctype-manpage[]', file=fp) | 
|  | 1416 | write('ifdef::doctype-manpage[]', file=fp) | 
|  | 1417 | write('Host Synchronization', file=fp) | 
|  | 1418 | write('--------------------', file=fp) | 
|  | 1419 | write('endif::doctype-manpage[]', file=fp) | 
| Mike Stroyan | a451fa8 | 2016-01-07 15:35:37 -0700 | [diff] [blame] | 1420 | write(threadsafety, file=fp, end='') | 
| Dustin Graves | b8458d1 | 2016-03-28 16:17:38 -0600 | [diff] [blame] | 1421 | write('ifndef::doctype-manpage[]', file=fp) | 
| Mike Stroyan | a451fa8 | 2016-01-07 15:35:37 -0700 | [diff] [blame] | 1422 | write('*' * 80, file=fp) | 
| Dustin Graves | b8458d1 | 2016-03-28 16:17:38 -0600 | [diff] [blame] | 1423 | write('endif::doctype-manpage[]', file=fp) | 
| Mike Stroyan | a451fa8 | 2016-01-07 15:35:37 -0700 | [diff] [blame] | 1424 | write('', file=fp) | 
| Dustin Graves | f69e377 | 2016-02-11 10:10:14 -0700 | [diff] [blame] | 1425 |  | 
| Mike Stroyan | a451fa8 | 2016-01-07 15:35:37 -0700 | [diff] [blame] | 1426 | # Command Properties - contained within a block, to avoid table numbering | 
|  | 1427 | if commandpropertiesentry is not None: | 
| Dustin Graves | b8458d1 | 2016-03-28 16:17:38 -0600 | [diff] [blame] | 1428 | write('ifndef::doctype-manpage[]', file=fp) | 
| Mike Stroyan | a451fa8 | 2016-01-07 15:35:37 -0700 | [diff] [blame] | 1429 | write('.Command Properties', file=fp) | 
|  | 1430 | write('*' * 80, file=fp) | 
| Dustin Graves | b8458d1 | 2016-03-28 16:17:38 -0600 | [diff] [blame] | 1431 | write('endif::doctype-manpage[]', file=fp) | 
|  | 1432 | write('ifdef::doctype-manpage[]', file=fp) | 
|  | 1433 | write('Command Properties', file=fp) | 
|  | 1434 | write('------------------', file=fp) | 
|  | 1435 | write('endif::doctype-manpage[]', file=fp) | 
| Mike Stroyan | a451fa8 | 2016-01-07 15:35:37 -0700 | [diff] [blame] | 1436 | write('[options="header", width="100%"]', file=fp) | 
|  | 1437 | write('|=====================', file=fp) | 
|  | 1438 | write('|Command Buffer Levels|Render Pass Scope|Supported Queue Types', file=fp) | 
|  | 1439 | write(commandpropertiesentry, file=fp) | 
|  | 1440 | write('|=====================', file=fp) | 
| Dustin Graves | b8458d1 | 2016-03-28 16:17:38 -0600 | [diff] [blame] | 1441 | write('ifndef::doctype-manpage[]', file=fp) | 
| Mike Stroyan | a451fa8 | 2016-01-07 15:35:37 -0700 | [diff] [blame] | 1442 | write('*' * 80, file=fp) | 
| Dustin Graves | b8458d1 | 2016-03-28 16:17:38 -0600 | [diff] [blame] | 1443 | write('endif::doctype-manpage[]', file=fp) | 
| Mike Stroyan | a451fa8 | 2016-01-07 15:35:37 -0700 | [diff] [blame] | 1444 | write('', file=fp) | 
|  | 1445 |  | 
|  | 1446 | # Success Codes - contained within a block, to avoid table numbering | 
|  | 1447 | if successcodes is not None or errorcodes is not None: | 
| Dustin Graves | b8458d1 | 2016-03-28 16:17:38 -0600 | [diff] [blame] | 1448 | write('ifndef::doctype-manpage[]', file=fp) | 
| Mike Stroyan | a451fa8 | 2016-01-07 15:35:37 -0700 | [diff] [blame] | 1449 | write('.Return Codes', file=fp) | 
|  | 1450 | write('*' * 80, file=fp) | 
| Dustin Graves | b8458d1 | 2016-03-28 16:17:38 -0600 | [diff] [blame] | 1451 | write('endif::doctype-manpage[]', file=fp) | 
|  | 1452 | write('ifdef::doctype-manpage[]', file=fp) | 
|  | 1453 | write('Return Codes', file=fp) | 
|  | 1454 | write('------------', file=fp) | 
|  | 1455 | write('endif::doctype-manpage[]', file=fp) | 
| Mike Stroyan | a451fa8 | 2016-01-07 15:35:37 -0700 | [diff] [blame] | 1456 | if successcodes is not None: | 
| Dustin Graves | b8458d1 | 2016-03-28 16:17:38 -0600 | [diff] [blame] | 1457 | write('ifndef::doctype-manpage[]', file=fp) | 
| Mike Stroyan | a451fa8 | 2016-01-07 15:35:37 -0700 | [diff] [blame] | 1458 | write('<<fundamentals-successcodes,Success>>::', file=fp) | 
| Dustin Graves | b8458d1 | 2016-03-28 16:17:38 -0600 | [diff] [blame] | 1459 | write('endif::doctype-manpage[]', file=fp) | 
|  | 1460 | write('ifdef::doctype-manpage[]', file=fp) | 
|  | 1461 | write('On success, this command returns::', file=fp) | 
|  | 1462 | write('endif::doctype-manpage[]', file=fp) | 
| Mike Stroyan | a451fa8 | 2016-01-07 15:35:37 -0700 | [diff] [blame] | 1463 | write(successcodes, file=fp) | 
|  | 1464 | if errorcodes is not None: | 
| Dustin Graves | b8458d1 | 2016-03-28 16:17:38 -0600 | [diff] [blame] | 1465 | write('ifndef::doctype-manpage[]', file=fp) | 
| Mike Stroyan | a451fa8 | 2016-01-07 15:35:37 -0700 | [diff] [blame] | 1466 | write('<<fundamentals-errorcodes,Failure>>::', file=fp) | 
| Dustin Graves | b8458d1 | 2016-03-28 16:17:38 -0600 | [diff] [blame] | 1467 | write('endif::doctype-manpage[]', file=fp) | 
|  | 1468 | write('ifdef::doctype-manpage[]', file=fp) | 
|  | 1469 | write('On failure, this command returns::', file=fp) | 
|  | 1470 | write('endif::doctype-manpage[]', file=fp) | 
| Mike Stroyan | a451fa8 | 2016-01-07 15:35:37 -0700 | [diff] [blame] | 1471 | write(errorcodes, file=fp) | 
| Dustin Graves | b8458d1 | 2016-03-28 16:17:38 -0600 | [diff] [blame] | 1472 | write('ifndef::doctype-manpage[]', file=fp) | 
| Mike Stroyan | a451fa8 | 2016-01-07 15:35:37 -0700 | [diff] [blame] | 1473 | write('*' * 80, file=fp) | 
| Dustin Graves | b8458d1 | 2016-03-28 16:17:38 -0600 | [diff] [blame] | 1474 | write('endif::doctype-manpage[]', file=fp) | 
| Mike Stroyan | a451fa8 | 2016-01-07 15:35:37 -0700 | [diff] [blame] | 1475 | write('', file=fp) | 
| Dustin Graves | f69e377 | 2016-02-11 10:10:14 -0700 | [diff] [blame] | 1476 |  | 
| Mike Stroyan | a451fa8 | 2016-01-07 15:35:37 -0700 | [diff] [blame] | 1477 | fp.close() | 
|  | 1478 |  | 
|  | 1479 | # | 
|  | 1480 | # Check if the parameter passed in is a pointer | 
|  | 1481 | def paramIsPointer(self, param): | 
|  | 1482 | ispointer = False | 
|  | 1483 | paramtype = param.find('type') | 
|  | 1484 | if paramtype.tail is not None and '*' in paramtype.tail: | 
|  | 1485 | ispointer = True | 
|  | 1486 |  | 
|  | 1487 | return ispointer | 
|  | 1488 |  | 
|  | 1489 | # | 
|  | 1490 | # Check if the parameter passed in is a static array | 
|  | 1491 | def paramIsStaticArray(self, param): | 
|  | 1492 | if param.find('name').tail is not None: | 
|  | 1493 | if param.find('name').tail[0] == '[': | 
|  | 1494 | return True | 
|  | 1495 |  | 
|  | 1496 | # | 
|  | 1497 | # Get the length of a parameter that's been identified as a static array | 
|  | 1498 | def staticArrayLength(self, param): | 
|  | 1499 | paramname = param.find('name') | 
|  | 1500 | paramenumsize = param.find('enum') | 
|  | 1501 |  | 
|  | 1502 | if paramenumsize is not None: | 
|  | 1503 | return paramenumsize.text | 
|  | 1504 | else: | 
|  | 1505 | return paramname.tail[1:-1] | 
|  | 1506 |  | 
|  | 1507 | # | 
|  | 1508 | # Check if the parameter passed in is a pointer to an array | 
|  | 1509 | def paramIsArray(self, param): | 
|  | 1510 | return param.attrib.get('len') is not None | 
|  | 1511 |  | 
|  | 1512 | # | 
|  | 1513 | # Get the parent of a handle object | 
|  | 1514 | def getHandleParent(self, typename): | 
|  | 1515 | types = self.registry.findall("types/type") | 
|  | 1516 | for elem in types: | 
|  | 1517 | if (elem.find("name") is not None and elem.find('name').text == typename) or elem.attrib.get('name') == typename: | 
|  | 1518 | return elem.attrib.get('parent') | 
|  | 1519 |  | 
|  | 1520 | # | 
|  | 1521 | # Check if a parent object is dispatchable or not | 
|  | 1522 | def isHandleTypeDispatchable(self, handlename): | 
|  | 1523 | handle = self.registry.find("types/type/[name='" + handlename + "'][@category='handle']") | 
|  | 1524 | if handle is not None and handle.find('type').text == 'VK_DEFINE_HANDLE': | 
|  | 1525 | return True | 
|  | 1526 | else: | 
|  | 1527 | return False | 
|  | 1528 |  | 
|  | 1529 | def isHandleOptional(self, param, params): | 
|  | 1530 |  | 
|  | 1531 | # See if the handle is optional | 
|  | 1532 | isOptional = False | 
|  | 1533 |  | 
|  | 1534 | # Simple, if it's optional, return true | 
|  | 1535 | if param.attrib.get('optional') is not None: | 
|  | 1536 | return True | 
|  | 1537 |  | 
|  | 1538 | # If no validity is being generated, it usually means that validity is complex and not absolute, so let's say yes. | 
|  | 1539 | if param.attrib.get('noautovalidity') is not None: | 
|  | 1540 | return True | 
|  | 1541 |  | 
|  | 1542 | # If the parameter is an array and we haven't already returned, find out if any of the len parameters are optional | 
|  | 1543 | if self.paramIsArray(param): | 
|  | 1544 | lengths = param.attrib.get('len').split(',') | 
|  | 1545 | for length in lengths: | 
|  | 1546 | if (length) != 'null-terminated' and (length) != '1': | 
|  | 1547 | for otherparam in params: | 
|  | 1548 | if otherparam.find('name').text == length: | 
|  | 1549 | if otherparam.attrib.get('optional') is not None: | 
|  | 1550 | return True | 
|  | 1551 |  | 
|  | 1552 | return False | 
|  | 1553 | # | 
|  | 1554 | # Get the category of a type | 
|  | 1555 | def getTypeCategory(self, typename): | 
|  | 1556 | types = self.registry.findall("types/type") | 
|  | 1557 | for elem in types: | 
|  | 1558 | if (elem.find("name") is not None and elem.find('name').text == typename) or elem.attrib.get('name') == typename: | 
|  | 1559 | return elem.attrib.get('category') | 
|  | 1560 |  | 
|  | 1561 | # | 
|  | 1562 | # Make a chunk of text for the end of a parameter if it is an array | 
|  | 1563 | def makeAsciiDocPreChunk(self, param, params): | 
|  | 1564 | paramname = param.find('name') | 
|  | 1565 | paramtype = param.find('type') | 
|  | 1566 |  | 
|  | 1567 | # General pre-amble. Check optionality and add stuff. | 
|  | 1568 | asciidoc = '* ' | 
|  | 1569 |  | 
|  | 1570 | if self.paramIsStaticArray(param): | 
|  | 1571 | asciidoc += 'Any given element of ' | 
|  | 1572 |  | 
|  | 1573 | elif self.paramIsArray(param): | 
|  | 1574 | lengths = param.attrib.get('len').split(',') | 
|  | 1575 |  | 
|  | 1576 | # Find all the parameters that are called out as optional, so we can document that they might be zero, and the array may be ignored | 
|  | 1577 | optionallengths = [] | 
|  | 1578 | for length in lengths: | 
|  | 1579 | if (length) != 'null-terminated' and (length) != '1': | 
|  | 1580 | for otherparam in params: | 
|  | 1581 | if otherparam.find('name').text == length: | 
|  | 1582 | if otherparam.attrib.get('optional') is not None: | 
|  | 1583 | if self.paramIsPointer(otherparam): | 
|  | 1584 | optionallengths.append('the value referenced by ' + self.makeParameterName(length)) | 
|  | 1585 | else: | 
|  | 1586 | optionallengths.append(self.makeParameterName(length)) | 
|  | 1587 |  | 
|  | 1588 | # Document that these arrays may be ignored if any of the length values are 0 | 
|  | 1589 | if len(optionallengths) != 0 or param.attrib.get('optional') is not None: | 
|  | 1590 | asciidoc += 'If ' | 
|  | 1591 |  | 
|  | 1592 |  | 
|  | 1593 | if len(optionallengths) != 0: | 
|  | 1594 | if len(optionallengths) == 1: | 
|  | 1595 |  | 
|  | 1596 | asciidoc += optionallengths[0] | 
|  | 1597 | asciidoc += ' is ' | 
|  | 1598 |  | 
|  | 1599 | else: | 
|  | 1600 | asciidoc += ' or '.join(optionallengths) | 
|  | 1601 | asciidoc += ' are ' | 
|  | 1602 |  | 
|  | 1603 | asciidoc += 'not `0`, ' | 
|  | 1604 |  | 
|  | 1605 | if len(optionallengths) != 0 and param.attrib.get('optional') is not None: | 
|  | 1606 | asciidoc += 'and ' | 
|  | 1607 |  | 
|  | 1608 | if param.attrib.get('optional') is not None: | 
|  | 1609 | asciidoc += self.makeParameterName(paramname.text) | 
|  | 1610 | asciidoc += ' is not `NULL`, ' | 
|  | 1611 |  | 
|  | 1612 | elif param.attrib.get('optional') is not None: | 
|  | 1613 | # Don't generate this stub for bitflags | 
|  | 1614 | if self.getTypeCategory(paramtype.text) != 'bitmask': | 
|  | 1615 | if param.attrib.get('optional').split(',')[0] == 'true': | 
|  | 1616 | asciidoc += 'If ' | 
|  | 1617 | asciidoc += self.makeParameterName(paramname.text) | 
|  | 1618 | asciidoc += ' is not ' | 
|  | 1619 | if self.paramIsArray(param) or self.paramIsPointer(param) or self.isHandleTypeDispatchable(paramtype.text): | 
|  | 1620 | asciidoc += '`NULL`' | 
|  | 1621 | elif self.getTypeCategory(paramtype.text) == 'handle': | 
|  | 1622 | asciidoc += 'sname:VK_NULL_HANDLE' | 
|  | 1623 | else: | 
|  | 1624 | asciidoc += '`0`' | 
|  | 1625 |  | 
|  | 1626 | asciidoc += ', ' | 
|  | 1627 |  | 
|  | 1628 | return asciidoc | 
|  | 1629 |  | 
|  | 1630 | # | 
|  | 1631 | # Make the generic asciidoc line chunk portion used for all parameters. | 
|  | 1632 | # May return an empty string if nothing to validate. | 
|  | 1633 | def createValidationLineForParameterIntroChunk(self, param, params, typetext): | 
|  | 1634 | asciidoc = '' | 
|  | 1635 | paramname = param.find('name') | 
|  | 1636 | paramtype = param.find('type') | 
|  | 1637 |  | 
|  | 1638 | asciidoc += self.makeAsciiDocPreChunk(param, params) | 
|  | 1639 |  | 
|  | 1640 | asciidoc += self.makeParameterName(paramname.text) | 
|  | 1641 | asciidoc += ' must: be ' | 
|  | 1642 |  | 
|  | 1643 | if self.paramIsArray(param): | 
|  | 1644 | # Arrays. These are hard to get right, apparently | 
|  | 1645 |  | 
|  | 1646 | lengths = param.attrib.get('len').split(',') | 
|  | 1647 |  | 
|  | 1648 | if (lengths[0]) == 'null-terminated': | 
|  | 1649 | asciidoc += 'a null-terminated ' | 
|  | 1650 | elif (lengths[0]) == '1': | 
|  | 1651 | asciidoc += 'a pointer to ' | 
|  | 1652 | else: | 
|  | 1653 | asciidoc += 'a pointer to an array of ' | 
|  | 1654 |  | 
|  | 1655 | # Handle equations, which are currently denoted with latex | 
|  | 1656 | if 'latexmath:' in lengths[0]: | 
|  | 1657 | asciidoc += lengths[0] | 
|  | 1658 | else: | 
|  | 1659 | asciidoc += self.makeParameterName(lengths[0]) | 
|  | 1660 | asciidoc += ' ' | 
|  | 1661 |  | 
|  | 1662 | for length in lengths[1:]: | 
|  | 1663 | if (length) == 'null-terminated': # This should always be the last thing. If it ever isn't for some bizarre reason, then this will need some massaging. | 
|  | 1664 | asciidoc += 'null-terminated ' | 
|  | 1665 | elif (length) == '1': | 
|  | 1666 | asciidoc += 'pointers to ' | 
|  | 1667 | else: | 
|  | 1668 | asciidoc += 'pointers to arrays of ' | 
|  | 1669 | # Handle equations, which are currently denoted with latex | 
|  | 1670 | if 'latex:' in length: | 
|  | 1671 | asciidoc += length | 
|  | 1672 | else: | 
|  | 1673 | asciidoc += self.makeParameterName(length) | 
|  | 1674 | asciidoc += ' ' | 
|  | 1675 |  | 
|  | 1676 | # Void pointers don't actually point at anything - remove the word "to" | 
|  | 1677 | if paramtype.text == 'void': | 
|  | 1678 | if lengths[-1] == '1': | 
|  | 1679 | if len(lengths) > 1: | 
|  | 1680 | asciidoc = asciidoc[:-5]    # Take care of the extra s added by the post array chunk function. #HACK# | 
|  | 1681 | else: | 
|  | 1682 | asciidoc = asciidoc[:-4] | 
|  | 1683 | else: | 
|  | 1684 | # An array of void values is a byte array. | 
|  | 1685 | asciidoc += 'byte' | 
|  | 1686 |  | 
|  | 1687 | elif paramtype.text == 'char': | 
|  | 1688 | # A null terminated array of chars is a string | 
|  | 1689 | if lengths[-1] == 'null-terminated': | 
|  | 1690 | asciidoc += 'string' | 
|  | 1691 | else: | 
|  | 1692 | # Else it's just a bunch of chars | 
|  | 1693 | asciidoc += 'char value' | 
|  | 1694 | elif param.text is not None: | 
|  | 1695 | # If a value is "const" that means it won't get modified, so it must be valid going into the function. | 
|  | 1696 | if 'const' in param.text: | 
|  | 1697 | typecategory = self.getTypeCategory(paramtype.text) | 
|  | 1698 | if (typecategory != 'struct' and typecategory != 'union' and typecategory != 'basetype' and typecategory is not None) or not self.isStructAlwaysValid(paramtype.text): | 
|  | 1699 | asciidoc += 'valid ' | 
|  | 1700 |  | 
|  | 1701 | asciidoc += typetext | 
|  | 1702 |  | 
|  | 1703 | # pluralize | 
|  | 1704 | if len(lengths) > 1 or (lengths[0] != '1' and lengths[0] != 'null-terminated'): | 
|  | 1705 | asciidoc += 's' | 
|  | 1706 |  | 
|  | 1707 | elif self.paramIsPointer(param): | 
|  | 1708 | # Handle pointers - which are really special case arrays (i.e. they don't have a length) | 
|  | 1709 | pointercount = paramtype.tail.count('*') | 
|  | 1710 |  | 
|  | 1711 | # Could be multi-level pointers (e.g. ppData - pointer to a pointer). Handle that. | 
|  | 1712 | for i in range(0, pointercount): | 
|  | 1713 | asciidoc += 'a pointer to ' | 
|  | 1714 |  | 
|  | 1715 | if paramtype.text == 'void': | 
|  | 1716 | # If there's only one pointer, it's optional, and it doesn't point at anything in particular - we don't need any language. | 
|  | 1717 | if pointercount == 1 and param.attrib.get('optional') is not None: | 
|  | 1718 | return '' # early return | 
|  | 1719 | else: | 
|  | 1720 | # Pointer to nothing in particular - delete the " to " portion | 
|  | 1721 | asciidoc = asciidoc[:-4] | 
|  | 1722 | else: | 
|  | 1723 | # Add an article for English semantic win | 
|  | 1724 | asciidoc += 'a ' | 
|  | 1725 |  | 
|  | 1726 | # If a value is "const" that means it won't get modified, so it must be valid going into the function. | 
|  | 1727 | if param.text is not None and paramtype.text != 'void': | 
|  | 1728 | if 'const' in param.text: | 
|  | 1729 | asciidoc += 'valid ' | 
|  | 1730 |  | 
|  | 1731 | asciidoc += typetext | 
|  | 1732 |  | 
|  | 1733 | else: | 
|  | 1734 | # Non-pointer, non-optional things must be valid | 
|  | 1735 | asciidoc += 'a valid ' | 
|  | 1736 | asciidoc += typetext | 
|  | 1737 |  | 
|  | 1738 | if asciidoc != '': | 
|  | 1739 | asciidoc += '\n' | 
|  | 1740 |  | 
|  | 1741 | # Add additional line for non-optional bitmasks | 
|  | 1742 | if self.getTypeCategory(paramtype.text) == 'bitmask': | 
|  | 1743 | if param.attrib.get('optional') is None: | 
|  | 1744 | asciidoc += '* ' | 
|  | 1745 | if self.paramIsArray(param): | 
|  | 1746 | asciidoc += 'Each element of ' | 
|  | 1747 | asciidoc += 'pname:' | 
|  | 1748 | asciidoc += paramname.text | 
|  | 1749 | asciidoc += ' mustnot: be `0`' | 
|  | 1750 | asciidoc += '\n' | 
|  | 1751 |  | 
|  | 1752 | return asciidoc | 
|  | 1753 |  | 
|  | 1754 | def makeAsciiDocLineForParameter(self, param, params, typetext): | 
|  | 1755 | if param.attrib.get('noautovalidity') is not None: | 
|  | 1756 | return '' | 
|  | 1757 | asciidoc  = self.createValidationLineForParameterIntroChunk(param, params, typetext) | 
|  | 1758 |  | 
|  | 1759 | return asciidoc | 
|  | 1760 |  | 
|  | 1761 | # Try to do check if a structure is always considered valid (i.e. there's no rules to its acceptance) | 
|  | 1762 | def isStructAlwaysValid(self, structname): | 
|  | 1763 |  | 
|  | 1764 | struct = self.registry.find("types/type[@name='" + structname + "']") | 
|  | 1765 |  | 
|  | 1766 | params = struct.findall('member') | 
|  | 1767 | validity = struct.find('validity') | 
|  | 1768 |  | 
|  | 1769 | if validity is not None: | 
|  | 1770 | return False | 
|  | 1771 |  | 
|  | 1772 | for param in params: | 
|  | 1773 | paramname = param.find('name') | 
|  | 1774 | paramtype = param.find('type') | 
|  | 1775 | typecategory = self.getTypeCategory(paramtype.text) | 
|  | 1776 |  | 
|  | 1777 | if paramname.text == 'pNext': | 
|  | 1778 | return False | 
|  | 1779 |  | 
|  | 1780 | if paramname.text == 'sType': | 
|  | 1781 | return False | 
|  | 1782 |  | 
|  | 1783 | if paramtype.text == 'void' or paramtype.text == 'char' or self.paramIsArray(param) or self.paramIsPointer(param): | 
|  | 1784 | if self.makeAsciiDocLineForParameter(param, params, '') != '': | 
|  | 1785 | return False | 
|  | 1786 | elif typecategory == 'handle' or typecategory == 'enum' or typecategory == 'bitmask' or param.attrib.get('returnedonly') == 'true': | 
|  | 1787 | return False | 
|  | 1788 | elif typecategory == 'struct' or typecategory == 'union': | 
|  | 1789 | if self.isStructAlwaysValid(paramtype.text) is False: | 
|  | 1790 | return False | 
|  | 1791 |  | 
|  | 1792 | return True | 
|  | 1793 |  | 
|  | 1794 | # | 
|  | 1795 | # Make an entire asciidoc line for a given parameter | 
|  | 1796 | def createValidationLineForParameter(self, param, params, typecategory): | 
|  | 1797 | asciidoc = '' | 
|  | 1798 | paramname = param.find('name') | 
|  | 1799 | paramtype = param.find('type') | 
|  | 1800 |  | 
|  | 1801 | if paramtype.text == 'void' or paramtype.text == 'char': | 
|  | 1802 | # Chars and void are special cases - needs care inside the generator functions | 
|  | 1803 | # A null-terminated char array is a string, else it's chars. | 
|  | 1804 | # An array of void values is a byte array, a void pointer is just a pointer to nothing in particular | 
|  | 1805 | asciidoc += self.makeAsciiDocLineForParameter(param, params, '') | 
|  | 1806 | elif typecategory == 'bitmask': | 
|  | 1807 | bitsname = paramtype.text.replace('Flags', 'FlagBits') | 
|  | 1808 | if self.registry.find("enums[@name='" + bitsname + "']") is None: | 
|  | 1809 | asciidoc += '* ' | 
|  | 1810 | asciidoc += self.makeParameterName(paramname.text) | 
|  | 1811 | asciidoc += ' must: be `0`' | 
|  | 1812 | asciidoc += '\n' | 
|  | 1813 | else: | 
|  | 1814 | if self.paramIsArray(param): | 
|  | 1815 | asciidoc += self.makeAsciiDocLineForParameter(param, params, 'combinations of ' + self.makeEnumerationName(bitsname) + ' value') | 
|  | 1816 | else: | 
|  | 1817 | asciidoc += self.makeAsciiDocLineForParameter(param, params, 'combination of ' + self.makeEnumerationName(bitsname) + ' values') | 
|  | 1818 | elif typecategory == 'handle': | 
|  | 1819 | asciidoc += self.makeAsciiDocLineForParameter(param, params, self.makeStructName(paramtype.text) + ' handle') | 
|  | 1820 | elif typecategory == 'enum': | 
|  | 1821 | asciidoc += self.makeAsciiDocLineForParameter(param, params, self.makeEnumerationName(paramtype.text) + ' value') | 
|  | 1822 | elif typecategory == 'struct': | 
|  | 1823 | if (self.paramIsArray(param) or self.paramIsPointer(param)) or not self.isStructAlwaysValid(paramtype.text): | 
|  | 1824 | asciidoc += self.makeAsciiDocLineForParameter(param, params, self.makeStructName(paramtype.text) + ' structure') | 
|  | 1825 | elif typecategory == 'union': | 
|  | 1826 | if (self.paramIsArray(param) or self.paramIsPointer(param)) or not self.isStructAlwaysValid(paramtype.text): | 
|  | 1827 | asciidoc += self.makeAsciiDocLineForParameter(param, params, self.makeStructName(paramtype.text) + ' union') | 
|  | 1828 | elif self.paramIsArray(param) or self.paramIsPointer(param): | 
|  | 1829 | asciidoc += self.makeAsciiDocLineForParameter(param, params, self.makeBaseTypeName(paramtype.text) + ' value') | 
|  | 1830 |  | 
|  | 1831 | return asciidoc | 
|  | 1832 |  | 
|  | 1833 | # | 
|  | 1834 | # Make an asciidoc validity entry for a handle's parent object | 
|  | 1835 | def makeAsciiDocHandleParent(self, param, params): | 
|  | 1836 | asciidoc = '' | 
|  | 1837 | paramname = param.find('name') | 
|  | 1838 | paramtype = param.find('type') | 
|  | 1839 |  | 
|  | 1840 | # Deal with handle parents | 
|  | 1841 | handleparent = self.getHandleParent(paramtype.text) | 
|  | 1842 | if handleparent is not None: | 
|  | 1843 | parentreference = None | 
|  | 1844 | for otherparam in params: | 
|  | 1845 | if otherparam.find('type').text == handleparent: | 
|  | 1846 | parentreference = otherparam.find('name').text | 
|  | 1847 | if parentreference is not None: | 
|  | 1848 | asciidoc += '* ' | 
|  | 1849 |  | 
|  | 1850 | if self.isHandleOptional(param, params): | 
|  | 1851 | if self.paramIsArray(param): | 
|  | 1852 | asciidoc += 'Each element of ' | 
|  | 1853 | asciidoc += self.makeParameterName(paramname.text) | 
|  | 1854 | asciidoc += ' that is a valid handle' | 
|  | 1855 | else: | 
|  | 1856 | asciidoc += 'If ' | 
|  | 1857 | asciidoc += self.makeParameterName(paramname.text) | 
|  | 1858 | asciidoc += ' is a valid handle, it' | 
|  | 1859 | else: | 
|  | 1860 | if self.paramIsArray(param): | 
|  | 1861 | asciidoc += 'Each element of ' | 
|  | 1862 | asciidoc += self.makeParameterName(paramname.text) | 
|  | 1863 | asciidoc += ' must: have been created, allocated or retrieved from ' | 
|  | 1864 | asciidoc += self.makeParameterName(parentreference) | 
|  | 1865 |  | 
|  | 1866 | asciidoc += '\n' | 
|  | 1867 | return asciidoc | 
|  | 1868 |  | 
|  | 1869 | # | 
|  | 1870 | # Generate an asciidoc validity line for the sType value of a struct | 
|  | 1871 | def makeStructureType(self, blockname, param): | 
|  | 1872 | asciidoc = '* ' | 
|  | 1873 | paramname = param.find('name') | 
|  | 1874 | paramtype = param.find('type') | 
|  | 1875 |  | 
|  | 1876 | asciidoc += self.makeParameterName(paramname.text) | 
|  | 1877 | asciidoc += ' must: be ' | 
|  | 1878 |  | 
|  | 1879 | structuretype = '' | 
|  | 1880 | for elem in re.findall(r'(([A-Z][a-z]+)|([A-Z][A-Z]+))', blockname): | 
|  | 1881 | if elem[0] == 'Vk': | 
|  | 1882 | structuretype += 'VK_STRUCTURE_TYPE_' | 
|  | 1883 | else: | 
|  | 1884 | structuretype += elem[0].upper() | 
|  | 1885 | structuretype += '_' | 
|  | 1886 |  | 
|  | 1887 | asciidoc += self.makeEnumerantName(structuretype[:-1]) | 
|  | 1888 | asciidoc += '\n' | 
|  | 1889 |  | 
|  | 1890 | return asciidoc | 
|  | 1891 |  | 
|  | 1892 | # | 
|  | 1893 | # Generate an asciidoc validity line for the pNext value of a struct | 
|  | 1894 | def makeStructureExtensionPointer(self, param): | 
|  | 1895 | asciidoc = '* ' | 
|  | 1896 | paramname = param.find('name') | 
|  | 1897 | paramtype = param.find('type') | 
|  | 1898 |  | 
|  | 1899 | asciidoc += self.makeParameterName(paramname.text) | 
|  | 1900 |  | 
|  | 1901 | validextensionstructs = param.attrib.get('validextensionstructs') | 
|  | 1902 | if validextensionstructs is None: | 
|  | 1903 | asciidoc += ' must: be `NULL`' | 
|  | 1904 | else: | 
|  | 1905 | extensionstructs = validextensionstructs.split(',') | 
|  | 1906 | asciidoc += ' must: point to one of ' + extensionstructs[:-1].join(', ') + ' or ' + extensionstructs[-1] + 'if the extension that introduced them is enabled ' | 
|  | 1907 |  | 
|  | 1908 | asciidoc += '\n' | 
|  | 1909 |  | 
|  | 1910 | return asciidoc | 
|  | 1911 |  | 
|  | 1912 | # | 
|  | 1913 | # Generate all the valid usage information for a given struct or command | 
|  | 1914 | def makeValidUsageStatements(self, cmd, blockname, params, usages): | 
|  | 1915 | # Start the asciidoc block for this | 
|  | 1916 | asciidoc = '' | 
|  | 1917 |  | 
|  | 1918 | handles = [] | 
|  | 1919 | anyparentedhandlesoptional = False | 
|  | 1920 | parentdictionary = {} | 
|  | 1921 | arraylengths = set() | 
|  | 1922 | for param in params: | 
|  | 1923 | paramname = param.find('name') | 
|  | 1924 | paramtype = param.find('type') | 
|  | 1925 |  | 
|  | 1926 | # Get the type's category | 
|  | 1927 | typecategory = self.getTypeCategory(paramtype.text) | 
|  | 1928 |  | 
|  | 1929 | # Generate language to independently validate a parameter | 
|  | 1930 | if paramtype.text == 'VkStructureType' and paramname.text == 'sType': | 
|  | 1931 | asciidoc += self.makeStructureType(blockname, param) | 
|  | 1932 | elif paramtype.text == 'void' and paramname.text == 'pNext': | 
|  | 1933 | asciidoc += self.makeStructureExtensionPointer(param) | 
|  | 1934 | else: | 
|  | 1935 | asciidoc += self.createValidationLineForParameter(param, params, typecategory) | 
|  | 1936 |  | 
|  | 1937 | # Ensure that any parenting is properly validated, and list that a handle was found | 
|  | 1938 | if typecategory == 'handle': | 
|  | 1939 | # Don't detect a parent for return values! | 
|  | 1940 | if not self.paramIsPointer(param) or (param.text is not None and 'const' in param.text): | 
|  | 1941 | parent = self.getHandleParent(paramtype.text) | 
|  | 1942 | if parent is not None: | 
|  | 1943 | handles.append(param) | 
|  | 1944 |  | 
|  | 1945 | # If any param is optional, it affects the output | 
|  | 1946 | if self.isHandleOptional(param, params): | 
|  | 1947 | anyparentedhandlesoptional = True | 
|  | 1948 |  | 
|  | 1949 | # Find the first dispatchable parent | 
|  | 1950 | ancestor = parent | 
|  | 1951 | while ancestor is not None and not self.isHandleTypeDispatchable(ancestor): | 
|  | 1952 | ancestor = self.getHandleParent(ancestor) | 
|  | 1953 |  | 
|  | 1954 | # If one was found, add this parameter to the parent dictionary | 
|  | 1955 | if ancestor is not None: | 
|  | 1956 | if ancestor not in parentdictionary: | 
|  | 1957 | parentdictionary[ancestor] = [] | 
|  | 1958 |  | 
|  | 1959 | if self.paramIsArray(param): | 
|  | 1960 | parentdictionary[ancestor].append('the elements of ' + self.makeParameterName(paramname.text)) | 
|  | 1961 | else: | 
|  | 1962 | parentdictionary[ancestor].append(self.makeParameterName(paramname.text)) | 
|  | 1963 |  | 
|  | 1964 | # Get the array length for this parameter | 
|  | 1965 | arraylength = param.attrib.get('len') | 
|  | 1966 | if arraylength is not None: | 
|  | 1967 | for onelength in arraylength.split(','): | 
|  | 1968 | arraylengths.add(onelength) | 
|  | 1969 |  | 
|  | 1970 | # For any vkQueue* functions, there might be queue type data | 
|  | 1971 | if 'vkQueue' in blockname: | 
|  | 1972 | # The queue type must be valid | 
|  | 1973 | queuetypes = cmd.attrib.get('queues') | 
|  | 1974 | if queuetypes is not None: | 
|  | 1975 | queuebits = [] | 
|  | 1976 | for queuetype in re.findall(r'([^,]+)', queuetypes): | 
|  | 1977 | queuebits.append(queuetype.replace('_',' ')) | 
|  | 1978 |  | 
|  | 1979 | asciidoc += '* ' | 
|  | 1980 | asciidoc += 'The pname:queue must: support ' | 
|  | 1981 | if len(queuebits) == 1: | 
|  | 1982 | asciidoc += queuebits[0] | 
|  | 1983 | else: | 
|  | 1984 | asciidoc += (', ').join(queuebits[:-1]) | 
|  | 1985 | asciidoc += ' or ' | 
|  | 1986 | asciidoc += queuebits[-1] | 
|  | 1987 | asciidoc += ' operations' | 
|  | 1988 | asciidoc += '\n' | 
|  | 1989 |  | 
|  | 1990 | if 'vkCmd' in blockname: | 
|  | 1991 | # The commandBuffer parameter must be being recorded | 
|  | 1992 | asciidoc += '* ' | 
|  | 1993 | asciidoc += 'pname:commandBuffer must: be in the recording state' | 
|  | 1994 | asciidoc += '\n' | 
|  | 1995 |  | 
|  | 1996 | # The queue type must be valid | 
|  | 1997 | queuetypes = cmd.attrib.get('queues') | 
|  | 1998 | queuebits = [] | 
|  | 1999 | for queuetype in re.findall(r'([^,]+)', queuetypes): | 
|  | 2000 | queuebits.append(queuetype.replace('_',' ')) | 
|  | 2001 |  | 
|  | 2002 | asciidoc += '* ' | 
|  | 2003 | asciidoc += 'The sname:VkCommandPool that pname:commandBuffer was allocated from must: support ' | 
|  | 2004 | if len(queuebits) == 1: | 
|  | 2005 | asciidoc += queuebits[0] | 
|  | 2006 | else: | 
|  | 2007 | asciidoc += (', ').join(queuebits[:-1]) | 
|  | 2008 | asciidoc += ' or ' | 
|  | 2009 | asciidoc += queuebits[-1] | 
|  | 2010 | asciidoc += ' operations' | 
|  | 2011 | asciidoc += '\n' | 
|  | 2012 |  | 
|  | 2013 | # Must be called inside/outside a renderpass appropriately | 
|  | 2014 | renderpass = cmd.attrib.get('renderpass') | 
|  | 2015 |  | 
|  | 2016 | if renderpass != 'both': | 
|  | 2017 | asciidoc += '* This command must: only be called ' | 
|  | 2018 | asciidoc += renderpass | 
|  | 2019 | asciidoc += ' of a render pass instance' | 
|  | 2020 | asciidoc += '\n' | 
|  | 2021 |  | 
|  | 2022 | # Must be in the right level command buffer | 
|  | 2023 | cmdbufferlevel = cmd.attrib.get('cmdbufferlevel') | 
|  | 2024 |  | 
|  | 2025 | if cmdbufferlevel != 'primary,secondary': | 
|  | 2026 | asciidoc += '* pname:commandBuffer must: be a ' | 
|  | 2027 | asciidoc += cmdbufferlevel | 
|  | 2028 | asciidoc += ' sname:VkCommandBuffer' | 
|  | 2029 | asciidoc += '\n' | 
|  | 2030 |  | 
|  | 2031 | # Any non-optional arraylengths should specify they must be greater than 0 | 
|  | 2032 | for param in params: | 
|  | 2033 | paramname = param.find('name') | 
|  | 2034 |  | 
|  | 2035 | for arraylength in arraylengths: | 
|  | 2036 | if paramname.text == arraylength and param.attrib.get('optional') is None: | 
|  | 2037 | # Get all the array dependencies | 
|  | 2038 | arrays = cmd.findall("param/[@len='" + arraylength + "'][@optional='true']") | 
|  | 2039 |  | 
|  | 2040 | # Get all the optional array dependencies, including those not generating validity for some reason | 
|  | 2041 | optionalarrays = cmd.findall("param/[@len='" + arraylength + "'][@optional='true']") | 
|  | 2042 | optionalarrays.extend(cmd.findall("param/[@len='" + arraylength + "'][@noautovalidity='true']")) | 
|  | 2043 |  | 
|  | 2044 | asciidoc += '* ' | 
|  | 2045 |  | 
|  | 2046 | # Allow lengths to be arbitrary if all their dependents are optional | 
|  | 2047 | if len(optionalarrays) == len(arrays) and len(optionalarrays) != 0: | 
|  | 2048 | asciidoc += 'If ' | 
|  | 2049 | if len(optionalarrays) > 1: | 
|  | 2050 | asciidoc += 'any of ' | 
|  | 2051 |  | 
|  | 2052 | for array in optionalarrays[:-1]: | 
|  | 2053 | asciidoc += self.makeParameterName(optionalarrays.find('name').text) | 
|  | 2054 | asciidoc += ', ' | 
|  | 2055 |  | 
|  | 2056 | if len(optionalarrays) > 1: | 
|  | 2057 | asciidoc += 'and ' | 
|  | 2058 | asciidoc += self.makeParameterName(optionalarrays[-1].find('name').text) | 
|  | 2059 | asciidoc += ' are ' | 
|  | 2060 | else: | 
|  | 2061 | asciidoc += self.makeParameterName(optionalarrays[-1].find('name').text) | 
|  | 2062 | asciidoc += ' is ' | 
|  | 2063 |  | 
|  | 2064 | asciidoc += 'not `NULL`, ' | 
|  | 2065 |  | 
|  | 2066 | if self.paramIsPointer(param): | 
|  | 2067 | asciidoc += 'the value referenced by ' | 
| Mike Stroyan | a451fa8 | 2016-01-07 15:35:37 -0700 | [diff] [blame] | 2068 |  | 
|  | 2069 | elif self.paramIsPointer(param): | 
|  | 2070 | asciidoc += 'The value referenced by ' | 
| Mike Stroyan | a451fa8 | 2016-01-07 15:35:37 -0700 | [diff] [blame] | 2071 |  | 
|  | 2072 | asciidoc += self.makeParameterName(arraylength) | 
|  | 2073 | asciidoc += ' must: be greater than `0`' | 
|  | 2074 | asciidoc += '\n' | 
|  | 2075 |  | 
|  | 2076 | # Find the parents of all objects referenced in this command | 
|  | 2077 | for param in handles: | 
|  | 2078 | asciidoc += self.makeAsciiDocHandleParent(param, params) | 
|  | 2079 |  | 
|  | 2080 | # Find the common ancestors of objects | 
|  | 2081 | noancestorscount = 0 | 
|  | 2082 | while noancestorscount < len(parentdictionary): | 
|  | 2083 | noancestorscount = 0 | 
|  | 2084 | oldparentdictionary = parentdictionary.copy() | 
|  | 2085 | for parent in oldparentdictionary.items(): | 
|  | 2086 | ancestor = self.getHandleParent(parent[0]) | 
|  | 2087 |  | 
|  | 2088 | while ancestor is not None and ancestor not in parentdictionary: | 
|  | 2089 | ancestor = self.getHandleParent(ancestor) | 
|  | 2090 |  | 
|  | 2091 | if ancestor is not None: | 
|  | 2092 | parentdictionary[ancestor] += parentdictionary.pop(parent[0]) | 
|  | 2093 | else: | 
|  | 2094 | # No ancestors possible - so count it up | 
|  | 2095 | noancestorscount += 1 | 
|  | 2096 |  | 
|  | 2097 | # Add validation language about common ancestors | 
|  | 2098 | for parent in parentdictionary.items(): | 
|  | 2099 | if len(parent[1]) > 1: | 
|  | 2100 | parentlanguage = '* ' | 
|  | 2101 |  | 
|  | 2102 | parentlanguage += 'Each of ' | 
|  | 2103 | parentlanguage += ", ".join(parent[1][:-1]) | 
|  | 2104 | parentlanguage += ' and ' | 
|  | 2105 | parentlanguage += parent[1][-1] | 
|  | 2106 | if anyparentedhandlesoptional is True: | 
|  | 2107 | parentlanguage += ' that are valid handles' | 
|  | 2108 | parentlanguage += ' must: have been created, allocated or retrieved from the same ' | 
|  | 2109 | parentlanguage += self.makeStructName(parent[0]) | 
|  | 2110 | parentlanguage += '\n' | 
|  | 2111 |  | 
|  | 2112 | # Capitalize and add to the main language | 
|  | 2113 | asciidoc += parentlanguage | 
|  | 2114 |  | 
| Dustin Graves | b8458d1 | 2016-03-28 16:17:38 -0600 | [diff] [blame] | 2115 | # Add in any plain-text validation language that should be added | 
| Mike Stroyan | a451fa8 | 2016-01-07 15:35:37 -0700 | [diff] [blame] | 2116 | for usage in usages: | 
|  | 2117 | asciidoc += '* ' | 
| Dustin Graves | b8458d1 | 2016-03-28 16:17:38 -0600 | [diff] [blame] | 2118 | asciidoc += usage | 
| Mike Stroyan | a451fa8 | 2016-01-07 15:35:37 -0700 | [diff] [blame] | 2119 | asciidoc += '\n' | 
|  | 2120 |  | 
|  | 2121 | # In case there's nothing to report, return None | 
|  | 2122 | if asciidoc == '': | 
|  | 2123 | return None | 
|  | 2124 | # Delimit the asciidoc block | 
|  | 2125 | return asciidoc | 
|  | 2126 |  | 
|  | 2127 | def makeThreadSafetyBlock(self, cmd, paramtext): | 
|  | 2128 | """Generate C function pointer typedef for <command> Element""" | 
|  | 2129 | paramdecl = '' | 
|  | 2130 |  | 
|  | 2131 | # For any vkCmd* functions, the commandBuffer parameter must be being recorded | 
|  | 2132 | if cmd.find('proto/name') is not None and 'vkCmd' in cmd.find('proto/name'): | 
|  | 2133 | paramdecl += '* ' | 
|  | 2134 | paramdecl += 'The sname:VkCommandPool that pname:commandBuffer was created from' | 
|  | 2135 | paramdecl += '\n' | 
|  | 2136 |  | 
|  | 2137 | # Find and add any parameters that are thread unsafe | 
|  | 2138 | explicitexternsyncparams = cmd.findall(paramtext + "[@externsync]") | 
|  | 2139 | if (explicitexternsyncparams is not None): | 
|  | 2140 | for param in explicitexternsyncparams: | 
|  | 2141 | externsyncattribs = param.attrib.get('externsync') | 
|  | 2142 | paramname = param.find('name') | 
|  | 2143 | for externsyncattrib in externsyncattribs.split(','): | 
|  | 2144 | paramdecl += '* ' | 
|  | 2145 | paramdecl += 'Host access to ' | 
|  | 2146 | if externsyncattrib == 'true': | 
|  | 2147 | if self.paramIsArray(param): | 
|  | 2148 | paramdecl += 'each member of ' + self.makeParameterName(paramname.text) | 
|  | 2149 | elif self.paramIsPointer(param): | 
|  | 2150 | paramdecl += 'the object referenced by ' + self.makeParameterName(paramname.text) | 
|  | 2151 | else: | 
|  | 2152 | paramdecl += self.makeParameterName(paramname.text) | 
|  | 2153 | else: | 
|  | 2154 | paramdecl += 'pname:' | 
|  | 2155 | paramdecl += externsyncattrib | 
|  | 2156 | paramdecl += ' must: be externally synchronized\n' | 
|  | 2157 |  | 
|  | 2158 | # Find and add any "implicit" parameters that are thread unsafe | 
|  | 2159 | implicitexternsyncparams = cmd.find('implicitexternsyncparams') | 
|  | 2160 | if (implicitexternsyncparams is not None): | 
|  | 2161 | for elem in implicitexternsyncparams: | 
|  | 2162 | paramdecl += '* ' | 
|  | 2163 | paramdecl += 'Host access to ' | 
|  | 2164 | paramdecl += elem.text | 
|  | 2165 | paramdecl += ' must: be externally synchronized\n' | 
|  | 2166 |  | 
|  | 2167 | if (paramdecl == ''): | 
|  | 2168 | return None | 
|  | 2169 | else: | 
|  | 2170 | return paramdecl | 
|  | 2171 |  | 
|  | 2172 | def makeCommandPropertiesTableEntry(self, cmd, name): | 
| Dustin Graves | f69e377 | 2016-02-11 10:10:14 -0700 | [diff] [blame] | 2173 |  | 
| Mike Stroyan | a451fa8 | 2016-01-07 15:35:37 -0700 | [diff] [blame] | 2174 | if 'vkCmd' in name: | 
|  | 2175 | # Must be called inside/outside a renderpass appropriately | 
|  | 2176 | cmdbufferlevel = cmd.attrib.get('cmdbufferlevel') | 
|  | 2177 | cmdbufferlevel = (' + \n').join(cmdbufferlevel.title().split(',')) | 
| Dustin Graves | f69e377 | 2016-02-11 10:10:14 -0700 | [diff] [blame] | 2178 |  | 
| Mike Stroyan | a451fa8 | 2016-01-07 15:35:37 -0700 | [diff] [blame] | 2179 | renderpass = cmd.attrib.get('renderpass') | 
|  | 2180 | renderpass = renderpass.capitalize() | 
| Dustin Graves | f69e377 | 2016-02-11 10:10:14 -0700 | [diff] [blame] | 2181 |  | 
| Mike Stroyan | a451fa8 | 2016-01-07 15:35:37 -0700 | [diff] [blame] | 2182 | queues = cmd.attrib.get('queues') | 
|  | 2183 | queues = (' + \n').join(queues.upper().split(',')) | 
| Dustin Graves | f69e377 | 2016-02-11 10:10:14 -0700 | [diff] [blame] | 2184 |  | 
|  | 2185 | return '|' + cmdbufferlevel + '|' + renderpass + '|' + queues | 
| Mike Stroyan | a451fa8 | 2016-01-07 15:35:37 -0700 | [diff] [blame] | 2186 | elif 'vkQueue' in name: | 
|  | 2187 | # Must be called inside/outside a renderpass appropriately | 
| Dustin Graves | f69e377 | 2016-02-11 10:10:14 -0700 | [diff] [blame] | 2188 |  | 
| Mike Stroyan | a451fa8 | 2016-01-07 15:35:37 -0700 | [diff] [blame] | 2189 | queues = cmd.attrib.get('queues') | 
|  | 2190 | if queues is None: | 
|  | 2191 | queues = 'Any' | 
|  | 2192 | else: | 
|  | 2193 | queues = (' + \n').join(queues.upper().split(',')) | 
| Dustin Graves | f69e377 | 2016-02-11 10:10:14 -0700 | [diff] [blame] | 2194 |  | 
|  | 2195 | return '|-|-|' + queues | 
| Mike Stroyan | a451fa8 | 2016-01-07 15:35:37 -0700 | [diff] [blame] | 2196 |  | 
|  | 2197 | return None | 
| Dustin Graves | f69e377 | 2016-02-11 10:10:14 -0700 | [diff] [blame] | 2198 |  | 
| Mike Stroyan | a451fa8 | 2016-01-07 15:35:37 -0700 | [diff] [blame] | 2199 | def makeSuccessCodes(self, cmd, name): | 
|  | 2200 |  | 
|  | 2201 | successcodes = cmd.attrib.get('successcodes') | 
|  | 2202 | if successcodes is not None: | 
| Dustin Graves | f69e377 | 2016-02-11 10:10:14 -0700 | [diff] [blame] | 2203 |  | 
| Mike Stroyan | a451fa8 | 2016-01-07 15:35:37 -0700 | [diff] [blame] | 2204 | successcodeentry = '' | 
|  | 2205 | successcodes = successcodes.split(',') | 
| Dustin Graves | b8458d1 | 2016-03-28 16:17:38 -0600 | [diff] [blame] | 2206 | return '* ename:' + '\n* ename:'.join(successcodes) | 
| Mike Stroyan | a451fa8 | 2016-01-07 15:35:37 -0700 | [diff] [blame] | 2207 |  | 
|  | 2208 | return None | 
|  | 2209 |  | 
|  | 2210 | def makeErrorCodes(self, cmd, name): | 
|  | 2211 |  | 
|  | 2212 | errorcodes = cmd.attrib.get('errorcodes') | 
|  | 2213 | if errorcodes is not None: | 
| Dustin Graves | f69e377 | 2016-02-11 10:10:14 -0700 | [diff] [blame] | 2214 |  | 
| Mike Stroyan | a451fa8 | 2016-01-07 15:35:37 -0700 | [diff] [blame] | 2215 | errorcodeentry = '' | 
|  | 2216 | errorcodes = errorcodes.split(',') | 
| Dustin Graves | b8458d1 | 2016-03-28 16:17:38 -0600 | [diff] [blame] | 2217 | return '* ename:' + '\n* ename:'.join(errorcodes) | 
| Mike Stroyan | a451fa8 | 2016-01-07 15:35:37 -0700 | [diff] [blame] | 2218 |  | 
|  | 2219 | return None | 
|  | 2220 |  | 
|  | 2221 | # | 
|  | 2222 | # Command generation | 
|  | 2223 | def genCmd(self, cmdinfo, name): | 
|  | 2224 | OutputGenerator.genCmd(self, cmdinfo, name) | 
|  | 2225 | # | 
| Dustin Graves | b8458d1 | 2016-03-28 16:17:38 -0600 | [diff] [blame] | 2226 | # Get all the parameters | 
| Mike Stroyan | a451fa8 | 2016-01-07 15:35:37 -0700 | [diff] [blame] | 2227 | params = cmdinfo.elem.findall('param') | 
| Dustin Graves | b8458d1 | 2016-03-28 16:17:38 -0600 | [diff] [blame] | 2228 | usageelements = cmdinfo.elem.findall('validity/usage') | 
|  | 2229 | usages = [] | 
|  | 2230 |  | 
|  | 2231 | for usage in usageelements: | 
|  | 2232 | usages.append(usage.text) | 
|  | 2233 | for usage in cmdinfo.additionalValidity: | 
|  | 2234 | usages.append(usage.text) | 
|  | 2235 | for usage in cmdinfo.removedValidity: | 
|  | 2236 | usages.remove(usage.text) | 
| Mike Stroyan | a451fa8 | 2016-01-07 15:35:37 -0700 | [diff] [blame] | 2237 |  | 
|  | 2238 | validity = self.makeValidUsageStatements(cmdinfo.elem, name, params, usages) | 
|  | 2239 | threadsafety = self.makeThreadSafetyBlock(cmdinfo.elem, 'param') | 
|  | 2240 | commandpropertiesentry = self.makeCommandPropertiesTableEntry(cmdinfo.elem, name) | 
|  | 2241 | successcodes = self.makeSuccessCodes(cmdinfo.elem, name) | 
|  | 2242 | errorcodes = self.makeErrorCodes(cmdinfo.elem, name) | 
|  | 2243 |  | 
|  | 2244 | self.writeInclude('validity/protos', name, validity, threadsafety, commandpropertiesentry, successcodes, errorcodes) | 
|  | 2245 |  | 
|  | 2246 | # | 
|  | 2247 | # Struct Generation | 
|  | 2248 | def genStruct(self, typeinfo, typename): | 
|  | 2249 | OutputGenerator.genStruct(self, typeinfo, typename) | 
|  | 2250 |  | 
|  | 2251 | # Anything that's only ever returned can't be set by the user, so shouldn't have any validity information. | 
|  | 2252 | if typeinfo.elem.attrib.get('returnedonly') is None: | 
|  | 2253 | params = typeinfo.elem.findall('member') | 
| Dustin Graves | b8458d1 | 2016-03-28 16:17:38 -0600 | [diff] [blame] | 2254 |  | 
|  | 2255 | usageelements = typeinfo.elem.findall('validity/usage') | 
|  | 2256 | usages = [] | 
|  | 2257 |  | 
|  | 2258 | for usage in usageelements: | 
|  | 2259 | usages.append(usage.text) | 
|  | 2260 | for usage in typeinfo.additionalValidity: | 
|  | 2261 | usages.append(usage.text) | 
|  | 2262 | for usage in typeinfo.removedValidity: | 
|  | 2263 | usages.remove(usage.text) | 
| Mike Stroyan | a451fa8 | 2016-01-07 15:35:37 -0700 | [diff] [blame] | 2264 |  | 
|  | 2265 | validity = self.makeValidUsageStatements(typeinfo.elem, typename, params, usages) | 
|  | 2266 | threadsafety = self.makeThreadSafetyBlock(typeinfo.elem, 'member') | 
|  | 2267 |  | 
|  | 2268 | self.writeInclude('validity/structs', typename, validity, threadsafety, None, None, None) | 
|  | 2269 | else: | 
|  | 2270 | # Still generate files for return only structs, in case this state changes later | 
|  | 2271 | self.writeInclude('validity/structs', typename, None, None, None, None, None) | 
|  | 2272 |  | 
|  | 2273 | # | 
|  | 2274 | # Type Generation | 
|  | 2275 | def genType(self, typeinfo, typename): | 
|  | 2276 | OutputGenerator.genType(self, typeinfo, typename) | 
|  | 2277 |  | 
|  | 2278 | category = typeinfo.elem.get('category') | 
|  | 2279 | if (category == 'struct' or category == 'union'): | 
|  | 2280 | self.genStruct(typeinfo, typename) | 
|  | 2281 |  | 
|  | 2282 | # HostSynchronizationOutputGenerator - subclass of OutputGenerator. | 
|  | 2283 | # Generates AsciiDoc includes of the externsync parameter table for the | 
|  | 2284 | # fundamentals chapter of the Vulkan specification. Similar to | 
|  | 2285 | # DocOutputGenerator. | 
|  | 2286 | # | 
|  | 2287 | # ---- methods ---- | 
|  | 2288 | # HostSynchronizationOutputGenerator(errFile, warnFile, diagFile) - args as for | 
|  | 2289 | #   OutputGenerator. Defines additional internal state. | 
|  | 2290 | # ---- methods overriding base class ---- | 
|  | 2291 | # genCmd(cmdinfo) | 
|  | 2292 | class HostSynchronizationOutputGenerator(OutputGenerator): | 
|  | 2293 | # Generate Host Synchronized Parameters in a table at the top of the spec | 
|  | 2294 | def __init__(self, | 
|  | 2295 | errFile = sys.stderr, | 
|  | 2296 | warnFile = sys.stderr, | 
|  | 2297 | diagFile = sys.stdout): | 
|  | 2298 | OutputGenerator.__init__(self, errFile, warnFile, diagFile) | 
|  | 2299 |  | 
|  | 2300 | threadsafety = {'parameters': '', 'parameterlists': '', 'implicit': ''} | 
|  | 2301 |  | 
|  | 2302 | def makeParameterName(self, name): | 
|  | 2303 | return 'pname:' + name | 
|  | 2304 |  | 
|  | 2305 | def makeFLink(self, name): | 
|  | 2306 | return 'flink:' + name | 
|  | 2307 |  | 
|  | 2308 | # | 
|  | 2309 | # Generate an include file | 
|  | 2310 | # | 
|  | 2311 | # directory - subdirectory to put file in | 
|  | 2312 | # basename - base name of the file | 
|  | 2313 | # contents - contents of the file (Asciidoc boilerplate aside) | 
|  | 2314 | def writeInclude(self): | 
|  | 2315 |  | 
|  | 2316 | if self.threadsafety['parameters'] is not None: | 
|  | 2317 | # Create file | 
|  | 2318 | filename = self.genOpts.genDirectory + '/' + self.genOpts.filename + '/parameters.txt' | 
|  | 2319 | self.logMsg('diag', '# Generating include file:', filename) | 
|  | 2320 | fp = open(filename, 'w') | 
|  | 2321 |  | 
|  | 2322 | # Host Synchronization | 
| Dustin Graves | b8458d1 | 2016-03-28 16:17:38 -0600 | [diff] [blame] | 2323 | write('// WARNING: DO NOT MODIFY! This file is automatically generated from the vk.xml registry', file=fp) | 
| Mike Stroyan | a451fa8 | 2016-01-07 15:35:37 -0700 | [diff] [blame] | 2324 | write('.Externally Synchronized Parameters', file=fp) | 
|  | 2325 | write('*' * 80, file=fp) | 
|  | 2326 | write(self.threadsafety['parameters'], file=fp, end='') | 
|  | 2327 | write('*' * 80, file=fp) | 
|  | 2328 | write('', file=fp) | 
|  | 2329 |  | 
|  | 2330 | if self.threadsafety['parameterlists'] is not None: | 
|  | 2331 | # Create file | 
|  | 2332 | filename = self.genOpts.genDirectory + '/' + self.genOpts.filename + '/parameterlists.txt' | 
|  | 2333 | self.logMsg('diag', '# Generating include file:', filename) | 
|  | 2334 | fp = open(filename, 'w') | 
|  | 2335 |  | 
|  | 2336 | # Host Synchronization | 
| Dustin Graves | b8458d1 | 2016-03-28 16:17:38 -0600 | [diff] [blame] | 2337 | write('// WARNING: DO NOT MODIFY! This file is automatically generated from the vk.xml registry', file=fp) | 
| Mike Stroyan | a451fa8 | 2016-01-07 15:35:37 -0700 | [diff] [blame] | 2338 | write('.Externally Synchronized Parameter Lists', file=fp) | 
|  | 2339 | write('*' * 80, file=fp) | 
|  | 2340 | write(self.threadsafety['parameterlists'], file=fp, end='') | 
|  | 2341 | write('*' * 80, file=fp) | 
|  | 2342 | write('', file=fp) | 
|  | 2343 |  | 
|  | 2344 | if self.threadsafety['implicit'] is not None: | 
|  | 2345 | # Create file | 
|  | 2346 | filename = self.genOpts.genDirectory + '/' + self.genOpts.filename + '/implicit.txt' | 
|  | 2347 | self.logMsg('diag', '# Generating include file:', filename) | 
|  | 2348 | fp = open(filename, 'w') | 
|  | 2349 |  | 
|  | 2350 | # Host Synchronization | 
| Dustin Graves | b8458d1 | 2016-03-28 16:17:38 -0600 | [diff] [blame] | 2351 | write('// WARNING: DO NOT MODIFY! This file is automatically generated from the vk.xml registry', file=fp) | 
| Mike Stroyan | a451fa8 | 2016-01-07 15:35:37 -0700 | [diff] [blame] | 2352 | write('.Implicit Externally Synchronized Parameters', file=fp) | 
|  | 2353 | write('*' * 80, file=fp) | 
|  | 2354 | write(self.threadsafety['implicit'], file=fp, end='') | 
|  | 2355 | write('*' * 80, file=fp) | 
|  | 2356 | write('', file=fp) | 
|  | 2357 |  | 
|  | 2358 | fp.close() | 
|  | 2359 |  | 
|  | 2360 | # | 
|  | 2361 | # Check if the parameter passed in is a pointer to an array | 
|  | 2362 | def paramIsArray(self, param): | 
|  | 2363 | return param.attrib.get('len') is not None | 
|  | 2364 |  | 
|  | 2365 | # Check if the parameter passed in is a pointer | 
|  | 2366 | def paramIsPointer(self, param): | 
|  | 2367 | ispointer = False | 
|  | 2368 | paramtype = param.find('type') | 
|  | 2369 | if paramtype.tail is not None and '*' in paramtype.tail: | 
|  | 2370 | ispointer = True | 
|  | 2371 |  | 
|  | 2372 | return ispointer | 
|  | 2373 |  | 
|  | 2374 | # Turn the "name[].member[]" notation into plain English. | 
|  | 2375 | def makeThreadDereferenceHumanReadable(self, dereference): | 
|  | 2376 | matches = re.findall(r"[\w]+[^\w]*",dereference) | 
|  | 2377 | stringval = '' | 
|  | 2378 | for match in reversed(matches): | 
|  | 2379 | if '->' in match or '.' in match: | 
|  | 2380 | stringval += 'member of ' | 
|  | 2381 | if '[]' in match: | 
|  | 2382 | stringval += 'each element of ' | 
|  | 2383 |  | 
|  | 2384 | stringval += 'the ' | 
|  | 2385 | stringval += self.makeParameterName(re.findall(r"[\w]+",match)[0]) | 
|  | 2386 | stringval += ' ' | 
|  | 2387 |  | 
|  | 2388 | stringval += 'parameter' | 
|  | 2389 |  | 
|  | 2390 | return stringval[0].upper() + stringval[1:] | 
|  | 2391 |  | 
|  | 2392 | def makeThreadSafetyBlocks(self, cmd, paramtext): | 
|  | 2393 | protoname = cmd.find('proto/name').text | 
|  | 2394 |  | 
|  | 2395 | # Find and add any parameters that are thread unsafe | 
|  | 2396 | explicitexternsyncparams = cmd.findall(paramtext + "[@externsync]") | 
|  | 2397 | if (explicitexternsyncparams is not None): | 
|  | 2398 | for param in explicitexternsyncparams: | 
|  | 2399 | externsyncattribs = param.attrib.get('externsync') | 
|  | 2400 | paramname = param.find('name') | 
|  | 2401 | for externsyncattrib in externsyncattribs.split(','): | 
|  | 2402 |  | 
|  | 2403 | tempstring = '* ' | 
|  | 2404 | if externsyncattrib == 'true': | 
|  | 2405 | if self.paramIsArray(param): | 
|  | 2406 | tempstring += 'Each element of the ' | 
|  | 2407 | elif self.paramIsPointer(param): | 
|  | 2408 | tempstring += 'The object referenced by the ' | 
|  | 2409 | else: | 
|  | 2410 | tempstring += 'The ' | 
|  | 2411 |  | 
|  | 2412 | tempstring += self.makeParameterName(paramname.text) | 
|  | 2413 | tempstring += ' parameter' | 
|  | 2414 |  | 
|  | 2415 | else: | 
|  | 2416 | tempstring += self.makeThreadDereferenceHumanReadable(externsyncattrib) | 
|  | 2417 |  | 
|  | 2418 | tempstring += ' in ' | 
|  | 2419 | tempstring += self.makeFLink(protoname) | 
|  | 2420 | tempstring += '\n' | 
|  | 2421 |  | 
|  | 2422 |  | 
|  | 2423 | if ' element of ' in tempstring: | 
|  | 2424 | self.threadsafety['parameterlists'] += tempstring | 
|  | 2425 | else: | 
|  | 2426 | self.threadsafety['parameters'] += tempstring | 
|  | 2427 |  | 
|  | 2428 |  | 
|  | 2429 | # Find and add any "implicit" parameters that are thread unsafe | 
|  | 2430 | implicitexternsyncparams = cmd.find('implicitexternsyncparams') | 
|  | 2431 | if (implicitexternsyncparams is not None): | 
|  | 2432 | for elem in implicitexternsyncparams: | 
|  | 2433 | self.threadsafety['implicit'] += '* ' | 
|  | 2434 | self.threadsafety['implicit'] += elem.text[0].upper() | 
|  | 2435 | self.threadsafety['implicit'] += elem.text[1:] | 
|  | 2436 | self.threadsafety['implicit'] += ' in ' | 
|  | 2437 | self.threadsafety['implicit'] += self.makeFLink(protoname) | 
|  | 2438 | self.threadsafety['implicit'] += '\n' | 
|  | 2439 |  | 
|  | 2440 |  | 
|  | 2441 | # For any vkCmd* functions, the commandBuffer parameter must be being recorded | 
|  | 2442 | if protoname is not None and 'vkCmd' in protoname: | 
|  | 2443 | self.threadsafety['implicit'] += '* ' | 
|  | 2444 | self.threadsafety['implicit'] += 'The sname:VkCommandPool that pname:commandBuffer was allocated from, in ' | 
|  | 2445 | self.threadsafety['implicit'] += self.makeFLink(protoname) | 
|  | 2446 |  | 
|  | 2447 | self.threadsafety['implicit'] += '\n' | 
|  | 2448 |  | 
|  | 2449 | # | 
|  | 2450 | # Command generation | 
|  | 2451 | def genCmd(self, cmdinfo, name): | 
|  | 2452 | OutputGenerator.genCmd(self, cmdinfo, name) | 
|  | 2453 | # | 
|  | 2454 | # Get all thh parameters | 
|  | 2455 | params = cmdinfo.elem.findall('param') | 
|  | 2456 | usages = cmdinfo.elem.findall('validity/usage') | 
|  | 2457 |  | 
|  | 2458 | self.makeThreadSafetyBlocks(cmdinfo.elem, 'param') | 
|  | 2459 |  | 
|  | 2460 | self.writeInclude() | 
| Mike Stroyan | 8849f9a | 2015-11-02 15:30:20 -0700 | [diff] [blame] | 2461 |  | 
|  | 2462 | # ThreadOutputGenerator - subclass of OutputGenerator. | 
|  | 2463 | # Generates Thread checking framework | 
|  | 2464 | # | 
|  | 2465 | # ---- methods ---- | 
|  | 2466 | # ThreadOutputGenerator(errFile, warnFile, diagFile) - args as for | 
|  | 2467 | #   OutputGenerator. Defines additional internal state. | 
|  | 2468 | # ---- methods overriding base class ---- | 
|  | 2469 | # beginFile(genOpts) | 
|  | 2470 | # endFile() | 
|  | 2471 | # beginFeature(interface, emit) | 
|  | 2472 | # endFeature() | 
|  | 2473 | # genType(typeinfo,name) | 
|  | 2474 | # genStruct(typeinfo,name) | 
|  | 2475 | # genGroup(groupinfo,name) | 
|  | 2476 | # genEnum(enuminfo, name) | 
|  | 2477 | # genCmd(cmdinfo) | 
|  | 2478 | class ThreadOutputGenerator(OutputGenerator): | 
|  | 2479 | """Generate specified API interfaces in a specific style, such as a C header""" | 
|  | 2480 | # This is an ordered list of sections in the header file. | 
|  | 2481 | TYPE_SECTIONS = ['include', 'define', 'basetype', 'handle', 'enum', | 
|  | 2482 | 'group', 'bitmask', 'funcpointer', 'struct'] | 
|  | 2483 | ALL_SECTIONS = TYPE_SECTIONS + ['command'] | 
|  | 2484 | def __init__(self, | 
|  | 2485 | errFile = sys.stderr, | 
|  | 2486 | warnFile = sys.stderr, | 
|  | 2487 | diagFile = sys.stdout): | 
|  | 2488 | OutputGenerator.__init__(self, errFile, warnFile, diagFile) | 
|  | 2489 | # Internal state - accumulators for different inner block text | 
|  | 2490 | self.sections = dict([(section, []) for section in self.ALL_SECTIONS]) | 
|  | 2491 | self.intercepts = [] | 
|  | 2492 |  | 
|  | 2493 | # Check if the parameter passed in is a pointer to an array | 
|  | 2494 | def paramIsArray(self, param): | 
|  | 2495 | return param.attrib.get('len') is not None | 
|  | 2496 |  | 
|  | 2497 | # Check if the parameter passed in is a pointer | 
|  | 2498 | def paramIsPointer(self, param): | 
|  | 2499 | ispointer = False | 
|  | 2500 | for elem in param: | 
|  | 2501 | #write('paramIsPointer '+elem.text, file=sys.stderr) | 
|  | 2502 | #write('elem.tag '+elem.tag, file=sys.stderr) | 
|  | 2503 | #if (elem.tail is None): | 
|  | 2504 | #    write('elem.tail is None', file=sys.stderr) | 
|  | 2505 | #else: | 
|  | 2506 | #    write('elem.tail '+elem.tail, file=sys.stderr) | 
|  | 2507 | if ((elem.tag is not 'type') and (elem.tail is not None)) and '*' in elem.tail: | 
|  | 2508 | ispointer = True | 
|  | 2509 | #    write('is pointer', file=sys.stderr) | 
|  | 2510 | return ispointer | 
|  | 2511 | def makeThreadUseBlock(self, cmd, functionprefix): | 
|  | 2512 | """Generate C function pointer typedef for <command> Element""" | 
|  | 2513 | paramdecl = '' | 
|  | 2514 | thread_check_dispatchable_objects = [ | 
|  | 2515 | "VkCommandBuffer", | 
|  | 2516 | "VkDevice", | 
|  | 2517 | "VkInstance", | 
|  | 2518 | "VkQueue", | 
|  | 2519 | ] | 
|  | 2520 | thread_check_nondispatchable_objects = [ | 
|  | 2521 | "VkBuffer", | 
|  | 2522 | "VkBufferView", | 
|  | 2523 | "VkCommandPool", | 
|  | 2524 | "VkDescriptorPool", | 
|  | 2525 | "VkDescriptorSetLayout", | 
|  | 2526 | "VkDeviceMemory", | 
|  | 2527 | "VkEvent", | 
|  | 2528 | "VkFence", | 
|  | 2529 | "VkFramebuffer", | 
|  | 2530 | "VkImage", | 
|  | 2531 | "VkImageView", | 
|  | 2532 | "VkPipeline", | 
|  | 2533 | "VkPipelineCache", | 
|  | 2534 | "VkPipelineLayout", | 
|  | 2535 | "VkQueryPool", | 
|  | 2536 | "VkRenderPass", | 
|  | 2537 | "VkSampler", | 
|  | 2538 | "VkSemaphore", | 
|  | 2539 | "VkShaderModule", | 
|  | 2540 | ] | 
|  | 2541 |  | 
|  | 2542 | # Find and add any parameters that are thread unsafe | 
|  | 2543 | params = cmd.findall('param') | 
|  | 2544 | for param in params: | 
|  | 2545 | paramname = param.find('name') | 
|  | 2546 | if False: # self.paramIsPointer(param): | 
|  | 2547 | paramdecl += '    // not watching use of pointer ' + paramname.text + '\n' | 
|  | 2548 | else: | 
|  | 2549 | externsync = param.attrib.get('externsync') | 
|  | 2550 | if externsync == 'true': | 
|  | 2551 | if self.paramIsArray(param): | 
| Michael Mc Donnell | 3e04b61 | 2016-03-17 21:18:32 -0700 | [diff] [blame] | 2552 | paramdecl += '    for (uint32_t index=0;index<' + param.attrib.get('len') + ';index++) {\n' | 
| Mike Stroyan | 8849f9a | 2015-11-02 15:30:20 -0700 | [diff] [blame] | 2553 | paramdecl += '        ' + functionprefix + 'WriteObject(my_data, ' + paramname.text + '[index]);\n' | 
|  | 2554 | paramdecl += '    }\n' | 
|  | 2555 | else: | 
|  | 2556 | paramdecl += '    ' + functionprefix + 'WriteObject(my_data, ' + paramname.text + ');\n' | 
|  | 2557 | elif (param.attrib.get('externsync')): | 
|  | 2558 | if self.paramIsArray(param): | 
|  | 2559 | # Externsync can list pointers to arrays of members to synchronize | 
| Michael Mc Donnell | 3e04b61 | 2016-03-17 21:18:32 -0700 | [diff] [blame] | 2560 | paramdecl += '    for (uint32_t index=0;index<' + param.attrib.get('len') + ';index++) {\n' | 
| Mike Stroyan | 8849f9a | 2015-11-02 15:30:20 -0700 | [diff] [blame] | 2561 | for member in externsync.split(","): | 
|  | 2562 | # Replace first empty [] in member name with index | 
|  | 2563 | element = member.replace('[]','[index]',1) | 
|  | 2564 | if '[]' in element: | 
|  | 2565 | # Replace any second empty [] in element name with | 
|  | 2566 | # inner array index based on mapping array names like | 
|  | 2567 | # "pSomeThings[]" to "someThingCount" array size. | 
|  | 2568 | # This could be more robust by mapping a param member | 
|  | 2569 | # name to a struct type and "len" attribute. | 
|  | 2570 | limit = element[0:element.find('s[]')] + 'Count' | 
|  | 2571 | dotp = limit.rfind('.p') | 
|  | 2572 | limit = limit[0:dotp+1] + limit[dotp+2:dotp+3].lower() + limit[dotp+3:] | 
| Michael Mc Donnell | 3e04b61 | 2016-03-17 21:18:32 -0700 | [diff] [blame] | 2573 | paramdecl += '        for(uint32_t index2=0;index2<'+limit+';index2++)' | 
| Mike Stroyan | 8849f9a | 2015-11-02 15:30:20 -0700 | [diff] [blame] | 2574 | element = element.replace('[]','[index2]') | 
|  | 2575 | paramdecl += '        ' + functionprefix + 'WriteObject(my_data, ' + element + ');\n' | 
|  | 2576 | paramdecl += '    }\n' | 
|  | 2577 | else: | 
|  | 2578 | # externsync can list members to synchronize | 
|  | 2579 | for member in externsync.split(","): | 
|  | 2580 | paramdecl += '    ' + functionprefix + 'WriteObject(my_data, ' + member + ');\n' | 
|  | 2581 | else: | 
|  | 2582 | paramtype = param.find('type') | 
|  | 2583 | if paramtype is not None: | 
|  | 2584 | paramtype = paramtype.text | 
|  | 2585 | else: | 
|  | 2586 | paramtype = 'None' | 
|  | 2587 | if paramtype in thread_check_dispatchable_objects or paramtype in thread_check_nondispatchable_objects: | 
|  | 2588 | if self.paramIsArray(param) and ('pPipelines' != paramname.text): | 
| Michael Mc Donnell | 3e04b61 | 2016-03-17 21:18:32 -0700 | [diff] [blame] | 2589 | paramdecl += '    for (uint32_t index=0;index<' + param.attrib.get('len') + ';index++) {\n' | 
| Mike Stroyan | 8849f9a | 2015-11-02 15:30:20 -0700 | [diff] [blame] | 2590 | paramdecl += '        ' + functionprefix + 'ReadObject(my_data, ' + paramname.text + '[index]);\n' | 
|  | 2591 | paramdecl += '    }\n' | 
|  | 2592 | elif not self.paramIsPointer(param): | 
|  | 2593 | # Pointer params are often being created. | 
|  | 2594 | # They are not being read from. | 
|  | 2595 | paramdecl += '    ' + functionprefix + 'ReadObject(my_data, ' + paramname.text + ');\n' | 
|  | 2596 | explicitexternsyncparams = cmd.findall("param[@externsync]") | 
|  | 2597 | if (explicitexternsyncparams is not None): | 
|  | 2598 | for param in explicitexternsyncparams: | 
|  | 2599 | externsyncattrib = param.attrib.get('externsync') | 
|  | 2600 | paramname = param.find('name') | 
|  | 2601 | paramdecl += '// Host access to ' | 
|  | 2602 | if externsyncattrib == 'true': | 
|  | 2603 | if self.paramIsArray(param): | 
|  | 2604 | paramdecl += 'each member of ' + paramname.text | 
|  | 2605 | elif self.paramIsPointer(param): | 
|  | 2606 | paramdecl += 'the object referenced by ' + paramname.text | 
|  | 2607 | else: | 
|  | 2608 | paramdecl += paramname.text | 
|  | 2609 | else: | 
|  | 2610 | paramdecl += externsyncattrib | 
|  | 2611 | paramdecl += ' must be externally synchronized\n' | 
|  | 2612 |  | 
|  | 2613 | # Find and add any "implicit" parameters that are thread unsafe | 
|  | 2614 | implicitexternsyncparams = cmd.find('implicitexternsyncparams') | 
|  | 2615 | if (implicitexternsyncparams is not None): | 
|  | 2616 | for elem in implicitexternsyncparams: | 
|  | 2617 | paramdecl += '    // ' | 
|  | 2618 | paramdecl += elem.text | 
|  | 2619 | paramdecl += ' must be externally synchronized between host accesses\n' | 
|  | 2620 |  | 
|  | 2621 | if (paramdecl == ''): | 
|  | 2622 | return None | 
|  | 2623 | else: | 
|  | 2624 | return paramdecl | 
|  | 2625 | def beginFile(self, genOpts): | 
|  | 2626 | OutputGenerator.beginFile(self, genOpts) | 
|  | 2627 | # C-specific | 
|  | 2628 | # | 
|  | 2629 | # Multiple inclusion protection & C++ wrappers. | 
|  | 2630 | if (genOpts.protectFile and self.genOpts.filename): | 
|  | 2631 | headerSym = '__' + re.sub('\.h', '_h_', os.path.basename(self.genOpts.filename)) | 
|  | 2632 | write('#ifndef', headerSym, file=self.outFile) | 
|  | 2633 | write('#define', headerSym, '1', file=self.outFile) | 
|  | 2634 | self.newline() | 
|  | 2635 | write('#ifdef __cplusplus', file=self.outFile) | 
|  | 2636 | write('extern "C" {', file=self.outFile) | 
|  | 2637 | write('#endif', file=self.outFile) | 
|  | 2638 | self.newline() | 
|  | 2639 | # | 
|  | 2640 | # User-supplied prefix text, if any (list of strings) | 
|  | 2641 | if (genOpts.prefixText): | 
|  | 2642 | for s in genOpts.prefixText: | 
|  | 2643 | write(s, file=self.outFile) | 
|  | 2644 | def endFile(self): | 
|  | 2645 | # C-specific | 
|  | 2646 | # Finish C++ wrapper and multiple inclusion protection | 
|  | 2647 | self.newline() | 
|  | 2648 | # record intercepted procedures | 
|  | 2649 | write('// intercepts', file=self.outFile) | 
|  | 2650 | write('struct { const char* name; PFN_vkVoidFunction pFunc;} procmap[] = {', file=self.outFile) | 
|  | 2651 | write('\n'.join(self.intercepts), file=self.outFile) | 
|  | 2652 | write('};\n', file=self.outFile) | 
|  | 2653 | self.newline() | 
|  | 2654 | write('#ifdef __cplusplus', file=self.outFile) | 
|  | 2655 | write('}', file=self.outFile) | 
|  | 2656 | write('#endif', file=self.outFile) | 
|  | 2657 | if (self.genOpts.protectFile and self.genOpts.filename): | 
|  | 2658 | self.newline() | 
|  | 2659 | write('#endif', file=self.outFile) | 
|  | 2660 | # Finish processing in superclass | 
|  | 2661 | OutputGenerator.endFile(self) | 
|  | 2662 | def beginFeature(self, interface, emit): | 
|  | 2663 | #write('// starting beginFeature', file=self.outFile) | 
|  | 2664 | # Start processing in superclass | 
|  | 2665 | OutputGenerator.beginFeature(self, interface, emit) | 
|  | 2666 | # C-specific | 
|  | 2667 | # Accumulate includes, defines, types, enums, function pointer typedefs, | 
|  | 2668 | # end function prototypes separately for this feature. They're only | 
|  | 2669 | # printed in endFeature(). | 
|  | 2670 | self.sections = dict([(section, []) for section in self.ALL_SECTIONS]) | 
|  | 2671 | #write('// ending beginFeature', file=self.outFile) | 
|  | 2672 | def endFeature(self): | 
|  | 2673 | # C-specific | 
|  | 2674 | # Actually write the interface to the output file. | 
|  | 2675 | #write('// starting endFeature', file=self.outFile) | 
|  | 2676 | if (self.emit): | 
|  | 2677 | self.newline() | 
|  | 2678 | if (self.genOpts.protectFeature): | 
|  | 2679 | write('#ifndef', self.featureName, file=self.outFile) | 
|  | 2680 | # If type declarations are needed by other features based on | 
|  | 2681 | # this one, it may be necessary to suppress the ExtraProtect, | 
|  | 2682 | # or move it below the 'for section...' loop. | 
|  | 2683 | #write('// endFeature looking at self.featureExtraProtect', file=self.outFile) | 
|  | 2684 | if (self.featureExtraProtect != None): | 
|  | 2685 | write('#ifdef', self.featureExtraProtect, file=self.outFile) | 
|  | 2686 | #write('#define', self.featureName, '1', file=self.outFile) | 
|  | 2687 | for section in self.TYPE_SECTIONS: | 
|  | 2688 | #write('// endFeature writing section'+section, file=self.outFile) | 
|  | 2689 | contents = self.sections[section] | 
|  | 2690 | if contents: | 
|  | 2691 | write('\n'.join(contents), file=self.outFile) | 
|  | 2692 | self.newline() | 
|  | 2693 | #write('// endFeature looking at self.sections[command]', file=self.outFile) | 
|  | 2694 | if (self.sections['command']): | 
|  | 2695 | write('\n'.join(self.sections['command']), end='', file=self.outFile) | 
|  | 2696 | self.newline() | 
|  | 2697 | if (self.featureExtraProtect != None): | 
|  | 2698 | write('#endif /*', self.featureExtraProtect, '*/', file=self.outFile) | 
|  | 2699 | if (self.genOpts.protectFeature): | 
|  | 2700 | write('#endif /*', self.featureName, '*/', file=self.outFile) | 
|  | 2701 | # Finish processing in superclass | 
|  | 2702 | OutputGenerator.endFeature(self) | 
|  | 2703 | #write('// ending endFeature', file=self.outFile) | 
|  | 2704 | # | 
|  | 2705 | # Append a definition to the specified section | 
|  | 2706 | def appendSection(self, section, text): | 
|  | 2707 | # self.sections[section].append('SECTION: ' + section + '\n') | 
|  | 2708 | self.sections[section].append(text) | 
|  | 2709 | # | 
|  | 2710 | # Type generation | 
|  | 2711 | def genType(self, typeinfo, name): | 
|  | 2712 | pass | 
|  | 2713 | # | 
|  | 2714 | # Struct (e.g. C "struct" type) generation. | 
|  | 2715 | # This is a special case of the <type> tag where the contents are | 
|  | 2716 | # interpreted as a set of <member> tags instead of freeform C | 
|  | 2717 | # C type declarations. The <member> tags are just like <param> | 
|  | 2718 | # tags - they are a declaration of a struct or union member. | 
|  | 2719 | # Only simple member declarations are supported (no nested | 
|  | 2720 | # structs etc.) | 
|  | 2721 | def genStruct(self, typeinfo, typeName): | 
|  | 2722 | OutputGenerator.genStruct(self, typeinfo, typeName) | 
|  | 2723 | body = 'typedef ' + typeinfo.elem.get('category') + ' ' + typeName + ' {\n' | 
|  | 2724 | # paramdecl = self.makeCParamDecl(typeinfo.elem, self.genOpts.alignFuncParam) | 
|  | 2725 | for member in typeinfo.elem.findall('.//member'): | 
|  | 2726 | body += self.makeCParamDecl(member, self.genOpts.alignFuncParam) | 
|  | 2727 | body += ';\n' | 
|  | 2728 | body += '} ' + typeName + ';\n' | 
|  | 2729 | self.appendSection('struct', body) | 
|  | 2730 | # | 
|  | 2731 | # Group (e.g. C "enum" type) generation. | 
|  | 2732 | # These are concatenated together with other types. | 
|  | 2733 | def genGroup(self, groupinfo, groupName): | 
|  | 2734 | pass | 
|  | 2735 | # Enumerant generation | 
|  | 2736 | # <enum> tags may specify their values in several ways, but are usually | 
|  | 2737 | # just integers. | 
|  | 2738 | def genEnum(self, enuminfo, name): | 
|  | 2739 | pass | 
|  | 2740 | # | 
|  | 2741 | # Command generation | 
|  | 2742 | def genCmd(self, cmdinfo, name): | 
|  | 2743 | special_functions = [ | 
|  | 2744 | 'vkGetDeviceProcAddr', | 
|  | 2745 | 'vkGetInstanceProcAddr', | 
|  | 2746 | 'vkCreateDevice', | 
|  | 2747 | 'vkDestroyDevice', | 
|  | 2748 | 'vkCreateInstance', | 
|  | 2749 | 'vkDestroyInstance', | 
|  | 2750 | 'vkEnumerateInstanceLayerProperties', | 
|  | 2751 | 'vkEnumerateInstanceExtensionProperties', | 
|  | 2752 | 'vkAllocateCommandBuffers', | 
|  | 2753 | 'vkFreeCommandBuffers', | 
|  | 2754 | 'vkCreateDebugReportCallbackEXT', | 
|  | 2755 | 'vkDestroyDebugReportCallbackEXT', | 
|  | 2756 | ] | 
|  | 2757 | if name in special_functions: | 
| Michael Lentine | 584ccab | 2016-02-03 16:51:46 -0600 | [diff] [blame] | 2758 | self.intercepts += [ '    {"%s", reinterpret_cast<PFN_vkVoidFunction>(%s)},' % (name,name) ] | 
| Mike Stroyan | 8849f9a | 2015-11-02 15:30:20 -0700 | [diff] [blame] | 2759 | return | 
|  | 2760 | if "KHR" in name: | 
|  | 2761 | self.appendSection('command', '// TODO - not wrapping KHR function ' + name) | 
|  | 2762 | return | 
|  | 2763 | # Determine first if this function needs to be intercepted | 
|  | 2764 | startthreadsafety = self.makeThreadUseBlock(cmdinfo.elem, 'start') | 
|  | 2765 | if startthreadsafety is None: | 
|  | 2766 | return | 
|  | 2767 | finishthreadsafety = self.makeThreadUseBlock(cmdinfo.elem, 'finish') | 
|  | 2768 | # record that the function will be intercepted | 
|  | 2769 | if (self.featureExtraProtect != None): | 
|  | 2770 | self.intercepts += [ '#ifdef %s' % self.featureExtraProtect ] | 
| Michael Lentine | 584ccab | 2016-02-03 16:51:46 -0600 | [diff] [blame] | 2771 | self.intercepts += [ '    {"%s", reinterpret_cast<PFN_vkVoidFunction>(%s)},' % (name,name) ] | 
| Mike Stroyan | 8849f9a | 2015-11-02 15:30:20 -0700 | [diff] [blame] | 2772 | if (self.featureExtraProtect != None): | 
|  | 2773 | self.intercepts += [ '#endif' ] | 
|  | 2774 |  | 
|  | 2775 | OutputGenerator.genCmd(self, cmdinfo, name) | 
|  | 2776 | # | 
|  | 2777 | decls = self.makeCDecls(cmdinfo.elem) | 
|  | 2778 | self.appendSection('command', '') | 
|  | 2779 | self.appendSection('command', decls[0][:-1]) | 
|  | 2780 | self.appendSection('command', '{') | 
|  | 2781 | # setup common to call wrappers | 
|  | 2782 | # first parameter is always dispatchable | 
|  | 2783 | dispatchable_type = cmdinfo.elem.find('param/type').text | 
|  | 2784 | dispatchable_name = cmdinfo.elem.find('param/name').text | 
|  | 2785 | self.appendSection('command', '    dispatch_key key = get_dispatch_key('+dispatchable_name+');') | 
|  | 2786 | self.appendSection('command', '    layer_data *my_data = get_my_data_ptr(key, layer_data_map);') | 
|  | 2787 | if dispatchable_type in ["VkPhysicalDevice", "VkInstance"]: | 
|  | 2788 | self.appendSection('command', '    VkLayerInstanceDispatchTable *pTable = my_data->instance_dispatch_table;') | 
|  | 2789 | else: | 
|  | 2790 | self.appendSection('command', '    VkLayerDispatchTable *pTable = my_data->device_dispatch_table;') | 
|  | 2791 | # Declare result variable, if any. | 
|  | 2792 | resulttype = cmdinfo.elem.find('proto/type') | 
|  | 2793 | if (resulttype != None and resulttype.text == 'void'): | 
|  | 2794 | resulttype = None | 
|  | 2795 | if (resulttype != None): | 
|  | 2796 | self.appendSection('command', '    ' + resulttype.text + ' result;') | 
|  | 2797 | assignresult = 'result = ' | 
|  | 2798 | else: | 
|  | 2799 | assignresult = '' | 
|  | 2800 |  | 
|  | 2801 | self.appendSection('command', str(startthreadsafety)) | 
|  | 2802 | params = cmdinfo.elem.findall('param/name') | 
|  | 2803 | paramstext = ','.join([str(param.text) for param in params]) | 
|  | 2804 | API = cmdinfo.elem.attrib.get('name').replace('vk','pTable->',1) | 
|  | 2805 | self.appendSection('command', '    ' + assignresult + API + '(' + paramstext + ');') | 
|  | 2806 | self.appendSection('command', str(finishthreadsafety)) | 
|  | 2807 | # Return result variable, if any. | 
|  | 2808 | if (resulttype != None): | 
|  | 2809 | self.appendSection('command', '    return result;') | 
|  | 2810 | self.appendSection('command', '}') | 
| Dustin Graves | f69e377 | 2016-02-11 10:10:14 -0700 | [diff] [blame] | 2811 |  | 
|  | 2812 | # ParamCheckerOutputGenerator - subclass of OutputGenerator. | 
|  | 2813 | # Generates param checker layer code. | 
|  | 2814 | # | 
|  | 2815 | # ---- methods ---- | 
|  | 2816 | # ParamCheckerOutputGenerator(errFile, warnFile, diagFile) - args as for | 
|  | 2817 | #   OutputGenerator. Defines additional internal state. | 
|  | 2818 | # ---- methods overriding base class ---- | 
|  | 2819 | # beginFile(genOpts) | 
|  | 2820 | # endFile() | 
|  | 2821 | # beginFeature(interface, emit) | 
|  | 2822 | # endFeature() | 
|  | 2823 | # genType(typeinfo,name) | 
|  | 2824 | # genStruct(typeinfo,name) | 
|  | 2825 | # genGroup(groupinfo,name) | 
|  | 2826 | # genEnum(enuminfo, name) | 
|  | 2827 | # genCmd(cmdinfo) | 
|  | 2828 | class ParamCheckerOutputGenerator(OutputGenerator): | 
|  | 2829 | """Generate ParamChecker code based on XML element attributes""" | 
|  | 2830 | # This is an ordered list of sections in the header file. | 
|  | 2831 | ALL_SECTIONS = ['command'] | 
|  | 2832 | def __init__(self, | 
|  | 2833 | errFile = sys.stderr, | 
|  | 2834 | warnFile = sys.stderr, | 
|  | 2835 | diagFile = sys.stdout): | 
|  | 2836 | OutputGenerator.__init__(self, errFile, warnFile, diagFile) | 
|  | 2837 | self.INDENT_SPACES = 4 | 
|  | 2838 | # Commands to ignore | 
| Dustin Graves | af0d6dc | 2016-03-02 18:23:29 -0700 | [diff] [blame] | 2839 | self.blacklist = [ | 
| Dustin Graves | 80c0dea | 2016-03-03 14:17:08 -0700 | [diff] [blame] | 2840 | 'vkGetInstanceProcAddr', | 
|  | 2841 | 'vkGetDeviceProcAddr', | 
| Dustin Graves | af0d6dc | 2016-03-02 18:23:29 -0700 | [diff] [blame] | 2842 | 'vkEnumerateInstanceLayerProperties', | 
|  | 2843 | 'vkEnumerateInstanceExtensionsProperties', | 
|  | 2844 | 'vkEnumerateDeviceLayerProperties', | 
|  | 2845 | 'vkEnumerateDeviceExtensionsProperties', | 
|  | 2846 | 'vkCreateDebugReportCallbackEXT', | 
|  | 2847 | 'vkDebugReportMessageEXT'] | 
| Dustin Graves | d59852f | 2016-04-15 18:06:14 -0600 | [diff] [blame^] | 2848 | # Validation conditions for some special case struct members that are conditionally validated | 
|  | 2849 | self.structMemberValidationConditions = { 'VkPipelineColorBlendStateCreateInfo' : { 'logicOp' : '{}logicOpEnable == VK_TRUE' } } | 
| Dustin Graves | f69e377 | 2016-02-11 10:10:14 -0700 | [diff] [blame] | 2850 | # Internal state - accumulators for different inner block text | 
|  | 2851 | self.sections = dict([(section, []) for section in self.ALL_SECTIONS]) | 
| Dustin Graves | af0d6dc | 2016-03-02 18:23:29 -0700 | [diff] [blame] | 2852 | self.structNames = []                             # List of Vulkan struct typenames | 
|  | 2853 | self.stypes = []                                  # Values from the VkStructureType enumeration | 
|  | 2854 | self.structTypes = dict()                         # Map of Vulkan struct typename to required VkStructureType | 
|  | 2855 | self.commands = []                                # List of CommandData records for all Vulkan commands | 
|  | 2856 | self.structMembers = []                           # List of StructMemberData records for all Vulkan structs | 
| Dustin Graves | 7633d69 | 2016-04-14 15:03:31 -0600 | [diff] [blame] | 2857 | self.validatedStructs = dict()                    # Map of structs type names to generated validation code for that struct type | 
| Dustin Graves | c3fc3d8 | 2016-03-23 19:44:00 -0600 | [diff] [blame] | 2858 | self.enumRanges = dict()                          # Map of enum name to BEGIN/END range values | 
| Dustin Graves | f69e377 | 2016-02-11 10:10:14 -0700 | [diff] [blame] | 2859 | # Named tuples to store struct and command data | 
|  | 2860 | self.StructType = namedtuple('StructType', ['name', 'value']) | 
| Dustin Graves | c3fc3d8 | 2016-03-23 19:44:00 -0600 | [diff] [blame] | 2861 | self.CommandParam = namedtuple('CommandParam', ['type', 'name', 'ispointer', 'isstaticarray', 'isbool', 'israngedenum', | 
| Dustin Graves | d59852f | 2016-04-15 18:06:14 -0600 | [diff] [blame^] | 2862 | 'isconst', 'isoptional', 'iscount', 'len', 'extstructs', 'condition', 'cdecl']) | 
| Dustin Graves | f69e377 | 2016-02-11 10:10:14 -0700 | [diff] [blame] | 2863 | self.CommandData = namedtuple('CommandData', ['name', 'params', 'cdecl']) | 
| Dustin Graves | af0d6dc | 2016-03-02 18:23:29 -0700 | [diff] [blame] | 2864 | self.StructMemberData = namedtuple('StructMemberData', ['name', 'members']) | 
| Dustin Graves | f69e377 | 2016-02-11 10:10:14 -0700 | [diff] [blame] | 2865 | # | 
|  | 2866 | def incIndent(self, indent): | 
|  | 2867 | inc = ' ' * self.INDENT_SPACES | 
|  | 2868 | if indent: | 
|  | 2869 | return indent + inc | 
|  | 2870 | return inc | 
| Dustin Graves | 80c0dea | 2016-03-03 14:17:08 -0700 | [diff] [blame] | 2871 | # | 
| Dustin Graves | f69e377 | 2016-02-11 10:10:14 -0700 | [diff] [blame] | 2872 | def decIndent(self, indent): | 
|  | 2873 | if indent and (len(indent) > self.INDENT_SPACES): | 
|  | 2874 | return indent[:-self.INDENT_SPACES] | 
|  | 2875 | return '' | 
|  | 2876 | # | 
|  | 2877 | def beginFile(self, genOpts): | 
|  | 2878 | OutputGenerator.beginFile(self, genOpts) | 
|  | 2879 | # C-specific | 
|  | 2880 | # | 
|  | 2881 | # User-supplied prefix text, if any (list of strings) | 
|  | 2882 | if (genOpts.prefixText): | 
|  | 2883 | for s in genOpts.prefixText: | 
|  | 2884 | write(s, file=self.outFile) | 
|  | 2885 | # | 
|  | 2886 | # Multiple inclusion protection & C++ wrappers. | 
|  | 2887 | if (genOpts.protectFile and self.genOpts.filename): | 
|  | 2888 | headerSym = re.sub('\.h', '_H', os.path.basename(self.genOpts.filename)).upper() | 
|  | 2889 | write('#ifndef', headerSym, file=self.outFile) | 
|  | 2890 | write('#define', headerSym, '1', file=self.outFile) | 
|  | 2891 | self.newline() | 
|  | 2892 | # | 
|  | 2893 | # Headers | 
| Dustin Graves | cd99e56 | 2016-03-31 09:50:42 -0600 | [diff] [blame] | 2894 | write('#include <string>', file=self.outFile) | 
|  | 2895 | self.newline() | 
| Dustin Graves | f69e377 | 2016-02-11 10:10:14 -0700 | [diff] [blame] | 2896 | write('#include "vulkan/vulkan.h"', file=self.outFile) | 
| Dustin Graves | 2949a5a | 2016-03-08 17:48:20 -0700 | [diff] [blame] | 2897 | write('#include "vk_layer_extension_utils.h"', file=self.outFile) | 
| Mark Lobodzinski | 1c33357 | 2016-03-17 15:08:18 -0600 | [diff] [blame] | 2898 | write('#include "parameter_validation_utils.h"', file=self.outFile) | 
| Dustin Graves | f69e377 | 2016-02-11 10:10:14 -0700 | [diff] [blame] | 2899 | # | 
|  | 2900 | # Macros | 
|  | 2901 | self.newline() | 
|  | 2902 | write('#ifndef UNUSED_PARAMETER', file=self.outFile) | 
|  | 2903 | write('#define UNUSED_PARAMETER(x) (void)(x)', file=self.outFile) | 
|  | 2904 | write('#endif // UNUSED_PARAMETER', file=self.outFile) | 
|  | 2905 | def endFile(self): | 
|  | 2906 | # C-specific | 
|  | 2907 | # Finish C++ wrapper and multiple inclusion protection | 
|  | 2908 | self.newline() | 
|  | 2909 | if (self.genOpts.protectFile and self.genOpts.filename): | 
|  | 2910 | self.newline() | 
|  | 2911 | write('#endif', file=self.outFile) | 
|  | 2912 | # Finish processing in superclass | 
|  | 2913 | OutputGenerator.endFile(self) | 
|  | 2914 | def beginFeature(self, interface, emit): | 
|  | 2915 | # Start processing in superclass | 
|  | 2916 | OutputGenerator.beginFeature(self, interface, emit) | 
|  | 2917 | # C-specific | 
|  | 2918 | # Accumulate includes, defines, types, enums, function pointer typedefs, | 
|  | 2919 | # end function prototypes separately for this feature. They're only | 
|  | 2920 | # printed in endFeature(). | 
|  | 2921 | self.sections = dict([(section, []) for section in self.ALL_SECTIONS]) | 
| Dustin Graves | af0d6dc | 2016-03-02 18:23:29 -0700 | [diff] [blame] | 2922 | self.structNames = [] | 
| Dustin Graves | f69e377 | 2016-02-11 10:10:14 -0700 | [diff] [blame] | 2923 | self.stypes = [] | 
|  | 2924 | self.structTypes = dict() | 
|  | 2925 | self.commands = [] | 
| Dustin Graves | af0d6dc | 2016-03-02 18:23:29 -0700 | [diff] [blame] | 2926 | self.structMembers = [] | 
| Dustin Graves | 7633d69 | 2016-04-14 15:03:31 -0600 | [diff] [blame] | 2927 | self.validatedStructs = dict() | 
| Dustin Graves | c3fc3d8 | 2016-03-23 19:44:00 -0600 | [diff] [blame] | 2928 | self.enumRanges = dict() | 
| Dustin Graves | f69e377 | 2016-02-11 10:10:14 -0700 | [diff] [blame] | 2929 | def endFeature(self): | 
|  | 2930 | # C-specific | 
|  | 2931 | # Actually write the interface to the output file. | 
|  | 2932 | if (self.emit): | 
|  | 2933 | self.newline() | 
|  | 2934 | # If type declarations are needed by other features based on | 
|  | 2935 | # this one, it may be necessary to suppress the ExtraProtect, | 
|  | 2936 | # or move it below the 'for section...' loop. | 
|  | 2937 | if (self.featureExtraProtect != None): | 
|  | 2938 | write('#ifdef', self.featureExtraProtect, file=self.outFile) | 
| Dustin Graves | af0d6dc | 2016-03-02 18:23:29 -0700 | [diff] [blame] | 2939 | # Generate the struct member checking code from the captured data | 
| Dustin Graves | af0d6dc | 2016-03-02 18:23:29 -0700 | [diff] [blame] | 2940 | self.processStructMemberData() | 
|  | 2941 | # Generate the command parameter checking code from the captured data | 
| Dustin Graves | f69e377 | 2016-02-11 10:10:14 -0700 | [diff] [blame] | 2942 | self.processCmdData() | 
|  | 2943 | if (self.sections['command']): | 
|  | 2944 | if (self.genOpts.protectProto): | 
|  | 2945 | write(self.genOpts.protectProto, | 
|  | 2946 | self.genOpts.protectProtoStr, file=self.outFile) | 
|  | 2947 | write('\n'.join(self.sections['command']), end='', file=self.outFile) | 
|  | 2948 | if (self.featureExtraProtect != None): | 
|  | 2949 | write('#endif /*', self.featureExtraProtect, '*/', file=self.outFile) | 
|  | 2950 | else: | 
|  | 2951 | self.newline() | 
|  | 2952 | # Finish processing in superclass | 
|  | 2953 | OutputGenerator.endFeature(self) | 
|  | 2954 | # | 
|  | 2955 | # Append a definition to the specified section | 
|  | 2956 | def appendSection(self, section, text): | 
|  | 2957 | # self.sections[section].append('SECTION: ' + section + '\n') | 
|  | 2958 | self.sections[section].append(text) | 
|  | 2959 | # | 
|  | 2960 | # Type generation | 
|  | 2961 | def genType(self, typeinfo, name): | 
|  | 2962 | OutputGenerator.genType(self, typeinfo, name) | 
|  | 2963 | typeElem = typeinfo.elem | 
|  | 2964 | # If the type is a struct type, traverse the imbedded <member> tags | 
|  | 2965 | # generating a structure. Otherwise, emit the tag text. | 
|  | 2966 | category = typeElem.get('category') | 
|  | 2967 | if (category == 'struct' or category == 'union'): | 
| Dustin Graves | af0d6dc | 2016-03-02 18:23:29 -0700 | [diff] [blame] | 2968 | self.structNames.append(name) | 
| Dustin Graves | f69e377 | 2016-02-11 10:10:14 -0700 | [diff] [blame] | 2969 | self.genStruct(typeinfo, name) | 
|  | 2970 | # | 
|  | 2971 | # Struct parameter check generation. | 
|  | 2972 | # This is a special case of the <type> tag where the contents are | 
|  | 2973 | # interpreted as a set of <member> tags instead of freeform C | 
|  | 2974 | # C type declarations. The <member> tags are just like <param> | 
|  | 2975 | # tags - they are a declaration of a struct or union member. | 
|  | 2976 | # Only simple member declarations are supported (no nested | 
|  | 2977 | # structs etc.) | 
|  | 2978 | def genStruct(self, typeinfo, typeName): | 
|  | 2979 | OutputGenerator.genStruct(self, typeinfo, typeName) | 
| Dustin Graves | d59852f | 2016-04-15 18:06:14 -0600 | [diff] [blame^] | 2980 | conditions = self.structMemberValidationConditions[typeName] if typeName in self.structMemberValidationConditions else None | 
| Dustin Graves | af0d6dc | 2016-03-02 18:23:29 -0700 | [diff] [blame] | 2981 | members = typeinfo.elem.findall('.//member') | 
|  | 2982 | # | 
|  | 2983 | # Iterate over members once to get length parameters for arrays | 
|  | 2984 | lens = set() | 
|  | 2985 | for member in members: | 
|  | 2986 | len = self.getLen(member) | 
|  | 2987 | if len: | 
|  | 2988 | lens.add(len) | 
|  | 2989 | # | 
|  | 2990 | # Generate member info | 
|  | 2991 | membersInfo = [] | 
|  | 2992 | for member in members: | 
| Dustin Graves | f69e377 | 2016-02-11 10:10:14 -0700 | [diff] [blame] | 2993 | # Get the member's type and name | 
| Dustin Graves | af0d6dc | 2016-03-02 18:23:29 -0700 | [diff] [blame] | 2994 | info = self.getTypeNameTuple(member) | 
|  | 2995 | type = info[0] | 
|  | 2996 | name = info[1] | 
|  | 2997 | stypeValue = '' | 
| Dustin Graves | c3fc3d8 | 2016-03-23 19:44:00 -0600 | [diff] [blame] | 2998 | cdecl = self.makeCParamDecl(member, 0) | 
| Dustin Graves | f69e377 | 2016-02-11 10:10:14 -0700 | [diff] [blame] | 2999 | # Process VkStructureType | 
|  | 3000 | if type == 'VkStructureType': | 
|  | 3001 | # Extract the required struct type value from the comments | 
|  | 3002 | # embedded in the original text defining the 'typeinfo' element | 
|  | 3003 | rawXml = etree.tostring(typeinfo.elem).decode('ascii') | 
| Dustin Graves | af0d6dc | 2016-03-02 18:23:29 -0700 | [diff] [blame] | 3004 | result = re.search(r'VK_STRUCTURE_TYPE_\w+', rawXml) | 
| Dustin Graves | f69e377 | 2016-02-11 10:10:14 -0700 | [diff] [blame] | 3005 | if result: | 
|  | 3006 | value = result.group(0) | 
|  | 3007 | # Make sure value is valid | 
|  | 3008 | #if value not in self.stypes: | 
|  | 3009 | #    print('WARNING: {} is not part of the VkStructureType enumeration [{}]'.format(value, typeName)) | 
|  | 3010 | else: | 
| Mike Stroyan | 0cf28a2 | 2016-04-05 16:40:30 -0600 | [diff] [blame] | 3011 | value = typeName | 
|  | 3012 | # Remove EXT | 
|  | 3013 | value = re.sub('EXT', '', value) | 
|  | 3014 | # Add underscore between lowercase then uppercase | 
|  | 3015 | value = re.sub('([a-z0-9])([A-Z])', r'\1_\2', value) | 
|  | 3016 | # Change to uppercase | 
|  | 3017 | value = value.upper() | 
|  | 3018 | # Add STRUCTURE_TYPE_ | 
|  | 3019 | value = re.sub('VK_', 'VK_STRUCTURE_TYPE_', value) | 
| Dustin Graves | af0d6dc | 2016-03-02 18:23:29 -0700 | [diff] [blame] | 3020 | # Store the required type value | 
| Dustin Graves | f69e377 | 2016-02-11 10:10:14 -0700 | [diff] [blame] | 3021 | self.structTypes[typeName] = self.StructType(name=name, value=value) | 
| Dustin Graves | af0d6dc | 2016-03-02 18:23:29 -0700 | [diff] [blame] | 3022 | # | 
|  | 3023 | # Store pointer/array/string info | 
|  | 3024 | # Check for parameter name in lens set | 
|  | 3025 | iscount = False | 
|  | 3026 | if name in lens: | 
|  | 3027 | iscount = True | 
|  | 3028 | # The pNext members are not tagged as optional, but are treated as | 
|  | 3029 | # optional for parameter NULL checks.  Static array members | 
|  | 3030 | # are also treated as optional to skip NULL pointer validation, as | 
|  | 3031 | # they won't be NULL. | 
|  | 3032 | isstaticarray = self.paramIsStaticArray(member) | 
|  | 3033 | isoptional = False | 
|  | 3034 | if self.paramIsOptional(member) or (name == 'pNext') or (isstaticarray): | 
|  | 3035 | isoptional = True | 
|  | 3036 | membersInfo.append(self.CommandParam(type=type, name=name, | 
|  | 3037 | ispointer=self.paramIsPointer(member), | 
|  | 3038 | isstaticarray=isstaticarray, | 
| Dustin Graves | c3fc3d8 | 2016-03-23 19:44:00 -0600 | [diff] [blame] | 3039 | isbool=True if type == 'VkBool32' else False, | 
|  | 3040 | israngedenum=True if type in self.enumRanges else False, | 
|  | 3041 | isconst=True if 'const' in cdecl else False, | 
| Dustin Graves | af0d6dc | 2016-03-02 18:23:29 -0700 | [diff] [blame] | 3042 | isoptional=isoptional, | 
|  | 3043 | iscount=iscount, | 
|  | 3044 | len=self.getLen(member), | 
| Dustin Graves | 2949a5a | 2016-03-08 17:48:20 -0700 | [diff] [blame] | 3045 | extstructs=member.attrib.get('validextensionstructs') if name == 'pNext' else None, | 
| Dustin Graves | d59852f | 2016-04-15 18:06:14 -0600 | [diff] [blame^] | 3046 | condition=conditions[name] if conditions and name in conditions else None, | 
| Dustin Graves | c3fc3d8 | 2016-03-23 19:44:00 -0600 | [diff] [blame] | 3047 | cdecl=cdecl)) | 
| Dustin Graves | af0d6dc | 2016-03-02 18:23:29 -0700 | [diff] [blame] | 3048 | self.structMembers.append(self.StructMemberData(name=typeName, members=membersInfo)) | 
| Dustin Graves | f69e377 | 2016-02-11 10:10:14 -0700 | [diff] [blame] | 3049 | # | 
| Dustin Graves | af0d6dc | 2016-03-02 18:23:29 -0700 | [diff] [blame] | 3050 | # Capture group (e.g. C "enum" type) info to be used for | 
|  | 3051 | # param check code generation. | 
| Dustin Graves | f69e377 | 2016-02-11 10:10:14 -0700 | [diff] [blame] | 3052 | # These are concatenated together with other types. | 
|  | 3053 | def genGroup(self, groupinfo, groupName): | 
|  | 3054 | OutputGenerator.genGroup(self, groupinfo, groupName) | 
| Dustin Graves | c3fc3d8 | 2016-03-23 19:44:00 -0600 | [diff] [blame] | 3055 | groupElem = groupinfo.elem | 
|  | 3056 | # | 
|  | 3057 | # Store the sType values | 
| Dustin Graves | f69e377 | 2016-02-11 10:10:14 -0700 | [diff] [blame] | 3058 | if groupName == 'VkStructureType': | 
| Dustin Graves | f69e377 | 2016-02-11 10:10:14 -0700 | [diff] [blame] | 3059 | for elem in groupElem.findall('enum'): | 
| Dustin Graves | c3fc3d8 | 2016-03-23 19:44:00 -0600 | [diff] [blame] | 3060 | self.stypes.append(elem.get('name')) | 
|  | 3061 | else: | 
|  | 3062 | # Determine if begin/end ranges are needed (we don't do this for VkStructureType, which has a more finely grained check) | 
|  | 3063 | expandName = re.sub(r'([0-9a-z_])([A-Z0-9][^A-Z0-9]?)',r'\1_\2',groupName).upper() | 
|  | 3064 | expandPrefix = expandName | 
|  | 3065 | expandSuffix = '' | 
|  | 3066 | expandSuffixMatch = re.search(r'[A-Z][A-Z]+$',groupName) | 
|  | 3067 | if expandSuffixMatch: | 
|  | 3068 | expandSuffix = '_' + expandSuffixMatch.group() | 
|  | 3069 | # Strip off the suffix from the prefix | 
|  | 3070 | expandPrefix = expandName.rsplit(expandSuffix, 1)[0] | 
|  | 3071 | isEnum = ('FLAG_BITS' not in expandPrefix) | 
|  | 3072 | if isEnum: | 
|  | 3073 | self.enumRanges[groupName] = (expandPrefix + '_BEGIN_RANGE' + expandSuffix, expandPrefix + '_END_RANGE' + expandSuffix) | 
| Dustin Graves | f69e377 | 2016-02-11 10:10:14 -0700 | [diff] [blame] | 3074 | # | 
| Dustin Graves | af0d6dc | 2016-03-02 18:23:29 -0700 | [diff] [blame] | 3075 | # Capture command parameter info to be used for param | 
|  | 3076 | # check code generation. | 
| Dustin Graves | f69e377 | 2016-02-11 10:10:14 -0700 | [diff] [blame] | 3077 | def genCmd(self, cmdinfo, name): | 
|  | 3078 | OutputGenerator.genCmd(self, cmdinfo, name) | 
|  | 3079 | if name not in self.blacklist: | 
| Dustin Graves | f69e377 | 2016-02-11 10:10:14 -0700 | [diff] [blame] | 3080 | params = cmdinfo.elem.findall('param') | 
| Dustin Graves | f69e377 | 2016-02-11 10:10:14 -0700 | [diff] [blame] | 3081 | # Get list of array lengths | 
|  | 3082 | lens = set() | 
|  | 3083 | for param in params: | 
|  | 3084 | len = self.getLen(param) | 
|  | 3085 | if len: | 
|  | 3086 | lens.add(len) | 
|  | 3087 | # Get param info | 
|  | 3088 | paramsInfo = [] | 
|  | 3089 | for param in params: | 
|  | 3090 | paramInfo = self.getTypeNameTuple(param) | 
| Dustin Graves | c3fc3d8 | 2016-03-23 19:44:00 -0600 | [diff] [blame] | 3091 | cdecl = self.makeCParamDecl(param, 0) | 
| Dustin Graves | f69e377 | 2016-02-11 10:10:14 -0700 | [diff] [blame] | 3092 | # Check for parameter name in lens set | 
|  | 3093 | iscount = False | 
|  | 3094 | if paramInfo[1] in lens: | 
|  | 3095 | iscount = True | 
|  | 3096 | paramsInfo.append(self.CommandParam(type=paramInfo[0], name=paramInfo[1], | 
|  | 3097 | ispointer=self.paramIsPointer(param), | 
|  | 3098 | isstaticarray=self.paramIsStaticArray(param), | 
| Dustin Graves | c3fc3d8 | 2016-03-23 19:44:00 -0600 | [diff] [blame] | 3099 | isbool=True if paramInfo[0] == 'VkBool32' else False, | 
|  | 3100 | israngedenum=True if paramInfo[0] in self.enumRanges else False, | 
|  | 3101 | isconst=True if 'const' in cdecl else False, | 
| Dustin Graves | f69e377 | 2016-02-11 10:10:14 -0700 | [diff] [blame] | 3102 | isoptional=self.paramIsOptional(param), | 
|  | 3103 | iscount=iscount, | 
|  | 3104 | len=self.getLen(param), | 
| Dustin Graves | 2949a5a | 2016-03-08 17:48:20 -0700 | [diff] [blame] | 3105 | extstructs=None, | 
| Dustin Graves | d59852f | 2016-04-15 18:06:14 -0600 | [diff] [blame^] | 3106 | condition=None, | 
| Dustin Graves | c3fc3d8 | 2016-03-23 19:44:00 -0600 | [diff] [blame] | 3107 | cdecl=cdecl)) | 
| Dustin Graves | f69e377 | 2016-02-11 10:10:14 -0700 | [diff] [blame] | 3108 | self.commands.append(self.CommandData(name=name, params=paramsInfo, cdecl=self.makeCDecls(cmdinfo.elem)[0])) | 
|  | 3109 | # | 
|  | 3110 | # Check if the parameter passed in is a pointer | 
|  | 3111 | def paramIsPointer(self, param): | 
| Dustin Graves | af0d6dc | 2016-03-02 18:23:29 -0700 | [diff] [blame] | 3112 | ispointer = 0 | 
| Dustin Graves | f69e377 | 2016-02-11 10:10:14 -0700 | [diff] [blame] | 3113 | paramtype = param.find('type') | 
| Dustin Graves | af0d6dc | 2016-03-02 18:23:29 -0700 | [diff] [blame] | 3114 | if (paramtype.tail is not None) and ('*' in paramtype.tail): | 
|  | 3115 | ispointer = paramtype.tail.count('*') | 
| Dustin Graves | 3e646e3 | 2016-03-07 17:52:14 -0700 | [diff] [blame] | 3116 | elif paramtype.text[:4] == 'PFN_': | 
|  | 3117 | # Treat function pointer typedefs as a pointer to a single value | 
|  | 3118 | ispointer = 1 | 
| Dustin Graves | f69e377 | 2016-02-11 10:10:14 -0700 | [diff] [blame] | 3119 | return ispointer | 
|  | 3120 | # | 
|  | 3121 | # Check if the parameter passed in is a static array | 
|  | 3122 | def paramIsStaticArray(self, param): | 
| Dustin Graves | af0d6dc | 2016-03-02 18:23:29 -0700 | [diff] [blame] | 3123 | isstaticarray = 0 | 
|  | 3124 | paramname = param.find('name') | 
|  | 3125 | if (paramname.tail is not None) and ('[' in paramname.tail): | 
|  | 3126 | isstaticarray = paramname.tail.count('[') | 
| Dustin Graves | f69e377 | 2016-02-11 10:10:14 -0700 | [diff] [blame] | 3127 | return isstaticarray | 
|  | 3128 | # | 
|  | 3129 | # Check if the parameter passed in is optional | 
|  | 3130 | # Returns a list of Boolean values for comma separated len attributes (len='false,true') | 
|  | 3131 | def paramIsOptional(self, param): | 
|  | 3132 | # See if the handle is optional | 
|  | 3133 | isoptional = False | 
|  | 3134 | # Simple, if it's optional, return true | 
|  | 3135 | optString = param.attrib.get('optional') | 
|  | 3136 | if optString: | 
|  | 3137 | if optString == 'true': | 
|  | 3138 | isoptional = True | 
|  | 3139 | elif ',' in optString: | 
|  | 3140 | opts = [] | 
|  | 3141 | for opt in optString.split(','): | 
|  | 3142 | val = opt.strip() | 
|  | 3143 | if val == 'true': | 
|  | 3144 | opts.append(True) | 
|  | 3145 | elif val == 'false': | 
|  | 3146 | opts.append(False) | 
|  | 3147 | else: | 
|  | 3148 | print('Unrecognized len attribute value',val) | 
|  | 3149 | isoptional = opts | 
|  | 3150 | return isoptional | 
|  | 3151 | # | 
|  | 3152 | # Retrieve the value of the len tag | 
|  | 3153 | def getLen(self, param): | 
| Dustin Graves | af0d6dc | 2016-03-02 18:23:29 -0700 | [diff] [blame] | 3154 | result = None | 
| Dustin Graves | f69e377 | 2016-02-11 10:10:14 -0700 | [diff] [blame] | 3155 | len = param.attrib.get('len') | 
|  | 3156 | if len and len != 'null-terminated': | 
| Dustin Graves | af0d6dc | 2016-03-02 18:23:29 -0700 | [diff] [blame] | 3157 | # For string arrays, 'len' can look like 'count,null-terminated', | 
|  | 3158 | # indicating that we have a null terminated array of strings.  We | 
|  | 3159 | # strip the null-terminated from the 'len' field and only return | 
|  | 3160 | # the parameter specifying the string count | 
|  | 3161 | if 'null-terminated' in len: | 
|  | 3162 | result = len.split(',')[0] | 
|  | 3163 | else: | 
|  | 3164 | result = len | 
|  | 3165 | return result | 
| Dustin Graves | f69e377 | 2016-02-11 10:10:14 -0700 | [diff] [blame] | 3166 | # | 
|  | 3167 | # Retrieve the type and name for a parameter | 
|  | 3168 | def getTypeNameTuple(self, param): | 
|  | 3169 | type = '' | 
|  | 3170 | name = '' | 
|  | 3171 | for elem in param: | 
|  | 3172 | if elem.tag == 'type': | 
|  | 3173 | type = noneStr(elem.text) | 
|  | 3174 | elif elem.tag == 'name': | 
|  | 3175 | name = noneStr(elem.text) | 
|  | 3176 | return (type, name) | 
|  | 3177 | # | 
|  | 3178 | # Find a named parameter in a parameter list | 
|  | 3179 | def getParamByName(self, params, name): | 
|  | 3180 | for param in params: | 
|  | 3181 | if param.name == name: | 
|  | 3182 | return param | 
|  | 3183 | return None | 
| Dustin Graves | af0d6dc | 2016-03-02 18:23:29 -0700 | [diff] [blame] | 3184 | # | 
| Dustin Graves | b8458d1 | 2016-03-28 16:17:38 -0600 | [diff] [blame] | 3185 | # Extract length values from latexmath.  Currently an inflexible solution that looks for specific | 
|  | 3186 | # patterns that are found in vk.xml.  Will need to be updated when new patterns are introduced. | 
|  | 3187 | def parseLateXMath(self, source): | 
|  | 3188 | name = 'ERROR' | 
|  | 3189 | decoratedName = 'ERROR' | 
|  | 3190 | if 'mathit' in source: | 
|  | 3191 | # Matches expressions similar to 'latexmath:[$\lceil{\mathit{rasterizationSamples} \over 32}\rceil$]' | 
|  | 3192 | match = re.match(r'latexmath\s*\:\s*\[\s*\$\\l(\w+)\s*\{\s*\\mathit\s*\{\s*(\w+)\s*\}\s*\\over\s*(\d+)\s*\}\s*\\r(\w+)\$\s*\]', source) | 
|  | 3193 | if not match or match.group(1) != match.group(4): | 
|  | 3194 | raise 'Unrecognized latexmath expression' | 
|  | 3195 | name = match.group(2) | 
|  | 3196 | decoratedName = '{}({}/{})'.format(*match.group(1, 2, 3)) | 
|  | 3197 | else: | 
|  | 3198 | # Matches expressions similar to 'latexmath : [$dataSize \over 4$]' | 
|  | 3199 | match = re.match(r'latexmath\s*\:\s*\[\s*\$\s*(\w+)\s*\\over\s*(\d+)\s*\$\s*\]', source) | 
|  | 3200 | name = match.group(1) | 
|  | 3201 | decoratedName = '{}/{}'.format(*match.group(1, 2)) | 
|  | 3202 | return name, decoratedName | 
|  | 3203 | # | 
| Dustin Graves | af0d6dc | 2016-03-02 18:23:29 -0700 | [diff] [blame] | 3204 | # Get the length paramater record for the specified parameter name | 
|  | 3205 | def getLenParam(self, params, name): | 
|  | 3206 | lenParam = None | 
|  | 3207 | if name: | 
|  | 3208 | if '->' in name: | 
|  | 3209 | # The count is obtained by dereferencing a member of a struct parameter | 
| Dustin Graves | c3fc3d8 | 2016-03-23 19:44:00 -0600 | [diff] [blame] | 3210 | lenParam = self.CommandParam(name=name, iscount=True, ispointer=False, isbool=False, israngedenum=False, isconst=False, | 
| Dustin Graves | d59852f | 2016-04-15 18:06:14 -0600 | [diff] [blame^] | 3211 | isstaticarray=None, isoptional=False, type=None, len=None, extstructs=None, condition=None, cdecl=None) | 
| Dustin Graves | af0d6dc | 2016-03-02 18:23:29 -0700 | [diff] [blame] | 3212 | elif 'latexmath' in name: | 
| Dustin Graves | b8458d1 | 2016-03-28 16:17:38 -0600 | [diff] [blame] | 3213 | lenName, decoratedName = self.parseLateXMath(name) | 
|  | 3214 | lenParam = self.getParamByName(params, lenName) | 
|  | 3215 | # TODO: Zero-check the result produced by the equation? | 
|  | 3216 | # Copy the stored len parameter entry and overwrite the name with the processed latexmath equation | 
|  | 3217 | #param = self.getParamByName(params, lenName) | 
|  | 3218 | #lenParam = self.CommandParam(name=decoratedName, iscount=param.iscount, ispointer=param.ispointer, | 
|  | 3219 | #                             isoptional=param.isoptional, type=param.type, len=param.len, | 
| Dustin Graves | d59852f | 2016-04-15 18:06:14 -0600 | [diff] [blame^] | 3220 | #                             isstaticarray=param.isstaticarray, extstructs=param.extstructs, | 
|  | 3221 | #                             condition=None, cdecl=param.cdecl) | 
| Dustin Graves | af0d6dc | 2016-03-02 18:23:29 -0700 | [diff] [blame] | 3222 | else: | 
|  | 3223 | lenParam = self.getParamByName(params, name) | 
|  | 3224 | return lenParam | 
|  | 3225 | # | 
| Mark Lobodzinski | 1c33357 | 2016-03-17 15:08:18 -0600 | [diff] [blame] | 3226 | # Convert a vulkan.h command declaration into a parameter_validation.h definition | 
| Dustin Graves | f69e377 | 2016-02-11 10:10:14 -0700 | [diff] [blame] | 3227 | def getCmdDef(self, cmd): | 
| Dustin Graves | f69e377 | 2016-02-11 10:10:14 -0700 | [diff] [blame] | 3228 | # | 
|  | 3229 | # Strip the trailing ';' and split into individual lines | 
|  | 3230 | lines = cmd.cdecl[:-1].split('\n') | 
|  | 3231 | # Replace Vulkan prototype | 
| Dustin Graves | bb84994 | 2016-04-05 13:48:15 -0600 | [diff] [blame] | 3232 | lines[0] = 'static bool parameter_validation_' + cmd.name + '(' | 
| Dustin Graves | 80c0dea | 2016-03-03 14:17:08 -0700 | [diff] [blame] | 3233 | # Replace the first argument with debug_report_data, when the first | 
|  | 3234 | # argument is a handle (not vkCreateInstance) | 
|  | 3235 | reportData = '    debug_report_data*'.ljust(self.genOpts.alignFuncParam) + 'report_data,' | 
|  | 3236 | if cmd.name != 'vkCreateInstance': | 
|  | 3237 | lines[1] = reportData | 
|  | 3238 | else: | 
|  | 3239 | lines.insert(1, reportData) | 
| Dustin Graves | f69e377 | 2016-02-11 10:10:14 -0700 | [diff] [blame] | 3240 | return '\n'.join(lines) | 
|  | 3241 | # | 
| Dustin Graves | af1f1b8 | 2016-02-29 13:35:07 -0700 | [diff] [blame] | 3242 | # Generate the code to check for a NULL dereference before calling the | 
|  | 3243 | # validation function | 
| Dustin Graves | 62448bf | 2016-04-11 16:06:25 -0600 | [diff] [blame] | 3244 | def genCheckedLengthCall(self, name, exprs): | 
| Dustin Graves | af1f1b8 | 2016-02-29 13:35:07 -0700 | [diff] [blame] | 3245 | count = name.count('->') | 
|  | 3246 | if count: | 
| Dustin Graves | 62448bf | 2016-04-11 16:06:25 -0600 | [diff] [blame] | 3247 | checkedExpr = [] | 
|  | 3248 | localIndent = '' | 
| Dustin Graves | af1f1b8 | 2016-02-29 13:35:07 -0700 | [diff] [blame] | 3249 | elements = name.split('->') | 
|  | 3250 | # Open the if expression blocks | 
|  | 3251 | for i in range(0, count): | 
| Dustin Graves | 62448bf | 2016-04-11 16:06:25 -0600 | [diff] [blame] | 3252 | checkedExpr.append(localIndent + 'if ({} != NULL) {{\n'.format('->'.join(elements[0:i+1]))) | 
| Dustin Graves | af1f1b8 | 2016-02-29 13:35:07 -0700 | [diff] [blame] | 3253 | localIndent = self.incIndent(localIndent) | 
|  | 3254 | # Add the validation expression | 
| Dustin Graves | 62448bf | 2016-04-11 16:06:25 -0600 | [diff] [blame] | 3255 | for expr in exprs: | 
|  | 3256 | checkedExpr.append(localIndent + expr) | 
| Dustin Graves | af1f1b8 | 2016-02-29 13:35:07 -0700 | [diff] [blame] | 3257 | # Close the if blocks | 
|  | 3258 | for i in range(0, count): | 
|  | 3259 | localIndent = self.decIndent(localIndent) | 
| Dustin Graves | 62448bf | 2016-04-11 16:06:25 -0600 | [diff] [blame] | 3260 | checkedExpr.append(localIndent + '}\n') | 
|  | 3261 | return [checkedExpr] | 
| Dustin Graves | af1f1b8 | 2016-02-29 13:35:07 -0700 | [diff] [blame] | 3262 | # No if statements were required | 
| Dustin Graves | 62448bf | 2016-04-11 16:06:25 -0600 | [diff] [blame] | 3263 | return exprs | 
|  | 3264 | # | 
| Dustin Graves | d59852f | 2016-04-15 18:06:14 -0600 | [diff] [blame^] | 3265 | # Generate code to check for a specific condition before executing validation code | 
|  | 3266 | def genConditionalCall(self, prefix, condition, exprs): | 
|  | 3267 | checkedExpr = [] | 
|  | 3268 | localIndent = '' | 
|  | 3269 | formattedCondition = condition.format(prefix) | 
|  | 3270 | checkedExpr.append(localIndent + 'if ({})\n'.format(formattedCondition)) | 
|  | 3271 | checkedExpr.append(localIndent + '{\n') | 
|  | 3272 | localIndent = self.incIndent(localIndent) | 
|  | 3273 | for expr in exprs: | 
|  | 3274 | checkedExpr.append(localIndent + expr) | 
|  | 3275 | localIndent = self.decIndent(localIndent) | 
|  | 3276 | checkedExpr.append(localIndent + '}\n') | 
|  | 3277 | return [checkedExpr] | 
|  | 3278 | # | 
| Dustin Graves | 62448bf | 2016-04-11 16:06:25 -0600 | [diff] [blame] | 3279 | # Generate the sType check string | 
|  | 3280 | def makeStructTypeCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, lenPtrRequired, funcPrintName, lenPrintName, valuePrintName): | 
|  | 3281 | checkExpr = [] | 
|  | 3282 | stype = self.structTypes[value.type] | 
|  | 3283 | if lenValue: | 
|  | 3284 | # This is an array with a pointer to a count value | 
|  | 3285 | if lenValue.ispointer: | 
|  | 3286 | # When the length parameter is a pointer, there is an extra Boolean parameter in the function call to indicate if it is required | 
| Dustin Graves | 7633d69 | 2016-04-14 15:03:31 -0600 | [diff] [blame] | 3287 | checkExpr.append('skipCall |= validate_struct_type_array(report_data, "{}", "{ldn}", "{dn}", "{sv}", {pf}{ln}, {pf}{vn}, {sv}, {}, {}, {});\n'.format( | 
| Dustin Graves | 62448bf | 2016-04-11 16:06:25 -0600 | [diff] [blame] | 3288 | funcPrintName, lenPtrRequired, lenValueRequired, valueRequired, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, sv=stype.value, pf=prefix)) | 
|  | 3289 | # This is an array with an integer count value | 
|  | 3290 | else: | 
| Dustin Graves | 7633d69 | 2016-04-14 15:03:31 -0600 | [diff] [blame] | 3291 | checkExpr.append('skipCall |= validate_struct_type_array(report_data, "{}", "{ldn}", "{dn}", "{sv}", {pf}{ln}, {pf}{vn}, {sv}, {}, {});\n'.format( | 
| Dustin Graves | 62448bf | 2016-04-11 16:06:25 -0600 | [diff] [blame] | 3292 | funcPrintName, lenValueRequired, valueRequired, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, sv=stype.value, pf=prefix)) | 
|  | 3293 | # This is an individual struct | 
|  | 3294 | else: | 
| Dustin Graves | 7633d69 | 2016-04-14 15:03:31 -0600 | [diff] [blame] | 3295 | checkExpr.append('skipCall |= validate_struct_type(report_data, "{}", "{}", "{sv}", {}{vn}, {sv}, {});\n'.format( | 
| Dustin Graves | 62448bf | 2016-04-11 16:06:25 -0600 | [diff] [blame] | 3296 | funcPrintName, valuePrintName, prefix, valueRequired, vn=value.name, sv=stype.value)) | 
|  | 3297 | return checkExpr | 
|  | 3298 | # | 
|  | 3299 | # Generate pNext check string | 
|  | 3300 | def makeStructNextCheck(self, prefix, value, funcPrintName, valuePrintName): | 
|  | 3301 | checkExpr = [] | 
|  | 3302 | # Generate an array of acceptable VkStructureType values for pNext | 
|  | 3303 | extStructCount = 0 | 
|  | 3304 | extStructVar = 'NULL' | 
|  | 3305 | extStructNames = 'NULL' | 
|  | 3306 | if value.extstructs: | 
|  | 3307 | structs = value.extstructs.split(',') | 
|  | 3308 | checkExpr.append('const VkStructureType allowedStructs[] = {' + ', '.join([self.structTypes[s].value for s in structs]) + '};\n') | 
|  | 3309 | extStructCount = 'ARRAY_SIZE(allowedStructs)' | 
|  | 3310 | extStructVar = 'allowedStructs' | 
|  | 3311 | extStructNames = '"' + ', '.join(structs) + '"' | 
| Dustin Graves | 7633d69 | 2016-04-14 15:03:31 -0600 | [diff] [blame] | 3312 | checkExpr.append('skipCall |= validate_struct_pnext(report_data, "{}", "{}", {}, {}{}, {}, {});\n'.format( | 
| Dustin Graves | 62448bf | 2016-04-11 16:06:25 -0600 | [diff] [blame] | 3313 | funcPrintName, valuePrintName, extStructNames, prefix, value.name, extStructCount, extStructVar)) | 
|  | 3314 | return checkExpr | 
|  | 3315 | # | 
|  | 3316 | # Generate the pointer check string | 
|  | 3317 | def makePointerCheck(self, prefix, value, lenValue, valueRequired, lenValueRequired, lenPtrRequired, funcPrintName, lenPrintName, valuePrintName): | 
|  | 3318 | checkExpr = [] | 
|  | 3319 | if lenValue: | 
|  | 3320 | # This is an array with a pointer to a count value | 
|  | 3321 | if lenValue.ispointer: | 
|  | 3322 | # If count and array parameters are optional, there will be no validation | 
|  | 3323 | if valueRequired == 'true' or lenPtrRequired == 'true' or lenValueRequired == 'true': | 
|  | 3324 | # When the length parameter is a pointer, there is an extra Boolean parameter in the function call to indicate if it is required | 
| Dustin Graves | 7633d69 | 2016-04-14 15:03:31 -0600 | [diff] [blame] | 3325 | checkExpr.append('skipCall |= validate_array(report_data, "{}", "{ldn}", "{dn}", {pf}{ln}, {pf}{vn}, {}, {}, {});\n'.format( | 
| Dustin Graves | 62448bf | 2016-04-11 16:06:25 -0600 | [diff] [blame] | 3326 | funcPrintName, lenPtrRequired, lenValueRequired, valueRequired, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, pf=prefix)) | 
|  | 3327 | # This is an array with an integer count value | 
|  | 3328 | else: | 
|  | 3329 | # If count and array parameters are optional, there will be no validation | 
|  | 3330 | if valueRequired == 'true' or lenValueRequired == 'true': | 
|  | 3331 | # Arrays of strings receive special processing | 
| Dustin Graves | 7633d69 | 2016-04-14 15:03:31 -0600 | [diff] [blame] | 3332 | validationFuncName = 'validate_array' if value.type != 'char' else 'validate_string_array' | 
|  | 3333 | checkExpr.append('skipCall |= {}(report_data, "{}", "{ldn}", "{dn}", {pf}{ln}, {pf}{vn}, {}, {});\n'.format( | 
|  | 3334 | validationFuncName, funcPrintName, lenValueRequired, valueRequired, ln=lenValue.name, ldn=lenPrintName, dn=valuePrintName, vn=value.name, pf=prefix)) | 
| Dustin Graves | 62448bf | 2016-04-11 16:06:25 -0600 | [diff] [blame] | 3335 | if checkExpr: | 
|  | 3336 | if lenValue and ('->' in lenValue.name): | 
|  | 3337 | # Add checks to ensure the validation call does not dereference a NULL pointer to obtain the count | 
|  | 3338 | checkExpr = self.genCheckedLengthCall(lenValue.name, checkExpr) | 
|  | 3339 | # This is an individual struct that is not allowed to be NULL | 
|  | 3340 | elif not value.isoptional: | 
|  | 3341 | # Function pointers need a reinterpret_cast to void* | 
|  | 3342 | if value.type[:4] == 'PFN_': | 
| Dustin Graves | 7633d69 | 2016-04-14 15:03:31 -0600 | [diff] [blame] | 3343 | checkExpr.append('skipCall |= validate_required_pointer(report_data, "{}", "{}", reinterpret_cast<const void*>({}{}));\n'.format(funcPrintName, valuePrintName, prefix, value.name)) | 
| Dustin Graves | 62448bf | 2016-04-11 16:06:25 -0600 | [diff] [blame] | 3344 | else: | 
| Dustin Graves | 7633d69 | 2016-04-14 15:03:31 -0600 | [diff] [blame] | 3345 | checkExpr.append('skipCall |= validate_required_pointer(report_data, "{}", "{}", {}{});\n'.format(funcPrintName, valuePrintName, prefix, value.name)) | 
| Dustin Graves | 62448bf | 2016-04-11 16:06:25 -0600 | [diff] [blame] | 3346 | return checkExpr | 
| Dustin Graves | af0d6dc | 2016-03-02 18:23:29 -0700 | [diff] [blame] | 3347 | # | 
| Dustin Graves | 7633d69 | 2016-04-14 15:03:31 -0600 | [diff] [blame] | 3348 | # Process struct member validation code, performing name suibstitution if required | 
|  | 3349 | def processStructMemberCode(self, line, funcName, memberNamePrefix, memberDisplayNamePrefix): | 
|  | 3350 | if any(token in line for token in ['{funcName}', '{valuePrefix}', '{displayNamePrefix}']): | 
|  | 3351 | return line.format(funcName=funcName, valuePrefix=memberNamePrefix, displayNamePrefix=memberDisplayNamePrefix) | 
|  | 3352 | return line | 
|  | 3353 | # | 
|  | 3354 | # Process struct validation code for inclusion in function or parent struct validation code | 
|  | 3355 | def expandStructCode(self, lines, funcName, memberNamePrefix, memberDisplayNamePrefix, indent, output): | 
|  | 3356 | for line in lines: | 
|  | 3357 | if output: | 
|  | 3358 | output[-1] += '\n' | 
|  | 3359 | if type(line) is list: | 
|  | 3360 | for sub in line: | 
|  | 3361 | output.append(self.processStructMemberCode(indent + sub, funcName, memberNamePrefix, memberDisplayNamePrefix)) | 
|  | 3362 | else: | 
|  | 3363 | output.append(self.processStructMemberCode(indent + line, funcName, memberNamePrefix, memberDisplayNamePrefix)) | 
|  | 3364 | return output | 
|  | 3365 | # | 
|  | 3366 | # Process struct pointer/array validation code, perfoeming name substitution if required | 
|  | 3367 | def expandStructPointerCode(self, prefix, value, lenValue, funcName, valueDisplayName): | 
|  | 3368 | expr = [] | 
|  | 3369 | expr.append('if ({}{} != NULL)\n'.format(prefix, value.name)) | 
|  | 3370 | expr.append('{') | 
|  | 3371 | indent = self.incIndent(None) | 
|  | 3372 | if lenValue: | 
|  | 3373 | # Need to process all elements in the array | 
|  | 3374 | indexName = lenValue.name.replace('Count', 'Index') | 
|  | 3375 | expr[-1] += '\n' | 
|  | 3376 | expr.append(indent + 'for (uint32_t {iname} = 0; {iname} < {}{}; ++{iname})\n'.format(prefix, lenValue.name, iname=indexName)) | 
|  | 3377 | expr.append(indent + '{') | 
|  | 3378 | indent = self.incIndent(indent) | 
|  | 3379 | # Prefix for value name to display in error message | 
|  | 3380 | memberNamePrefix = '{}{}[{}].'.format(prefix, value.name, indexName) | 
|  | 3381 | memberDisplayNamePrefix = '{}[i].'.format(valueDisplayName) | 
|  | 3382 | else: | 
|  | 3383 | memberNamePrefix = '{}{}->'.format(prefix, value.name) | 
|  | 3384 | memberDisplayNamePrefix = '{}->'.format(valueDisplayName) | 
|  | 3385 | # | 
|  | 3386 | # Expand the struct validation lines | 
|  | 3387 | expr = self.expandStructCode(self.validatedStructs[value.type], funcName, memberNamePrefix, memberDisplayNamePrefix, indent, expr) | 
|  | 3388 | # | 
|  | 3389 | if lenValue: | 
|  | 3390 | # Close if and for scopes | 
|  | 3391 | indent = self.decIndent(indent) | 
|  | 3392 | expr.append(indent + '}\n') | 
|  | 3393 | expr.append('}\n') | 
|  | 3394 | return expr | 
|  | 3395 | # | 
| Dustin Graves | af0d6dc | 2016-03-02 18:23:29 -0700 | [diff] [blame] | 3396 | # Generate the parameter checking code | 
| Dustin Graves | 62448bf | 2016-04-11 16:06:25 -0600 | [diff] [blame] | 3397 | def genFuncBody(self, funcName, values, valuePrefix, displayNamePrefix, structTypeName): | 
|  | 3398 | lines = []    # Generated lines of code | 
|  | 3399 | unused = []   # Unused variable names | 
| Dustin Graves | af0d6dc | 2016-03-02 18:23:29 -0700 | [diff] [blame] | 3400 | for value in values: | 
| Dustin Graves | 7633d69 | 2016-04-14 15:03:31 -0600 | [diff] [blame] | 3401 | usedLines = [] | 
| Dustin Graves | c3fc3d8 | 2016-03-23 19:44:00 -0600 | [diff] [blame] | 3402 | lenParam = None | 
|  | 3403 | # | 
| Dustin Graves | 62448bf | 2016-04-11 16:06:25 -0600 | [diff] [blame] | 3404 | # Generate the full name of the value, which will be printed in the error message, by adding the variable prefix to the value name | 
| Dustin Graves | 7633d69 | 2016-04-14 15:03:31 -0600 | [diff] [blame] | 3405 | valueDisplayName = '{}{}'.format(displayNamePrefix, value.name) | 
| Dustin Graves | af0d6dc | 2016-03-02 18:23:29 -0700 | [diff] [blame] | 3406 | # | 
|  | 3407 | # Check for NULL pointers, ignore the inout count parameters that | 
|  | 3408 | # will be validated with their associated array | 
|  | 3409 | if (value.ispointer or value.isstaticarray) and not value.iscount: | 
|  | 3410 | # | 
| Dustin Graves | af0d6dc | 2016-03-02 18:23:29 -0700 | [diff] [blame] | 3411 | # Parameters for function argument generation | 
| Dustin Graves | 62448bf | 2016-04-11 16:06:25 -0600 | [diff] [blame] | 3412 | req = 'true'    # Paramerter cannot be NULL | 
|  | 3413 | cpReq = 'true'  # Count pointer cannot be NULL | 
|  | 3414 | cvReq = 'true'  # Count value cannot be 0 | 
| Dustin Graves | c3fc3d8 | 2016-03-23 19:44:00 -0600 | [diff] [blame] | 3415 | lenDisplayName = None # Name of length parameter to print with validation messages; parameter name with prefix applied | 
| Dustin Graves | af0d6dc | 2016-03-02 18:23:29 -0700 | [diff] [blame] | 3416 | # | 
|  | 3417 | # Generate required/optional parameter strings for the pointer and count values | 
|  | 3418 | if value.isoptional: | 
| Dustin Graves | bb84994 | 2016-04-05 13:48:15 -0600 | [diff] [blame] | 3419 | req = 'false' | 
| Dustin Graves | af0d6dc | 2016-03-02 18:23:29 -0700 | [diff] [blame] | 3420 | if value.len: | 
|  | 3421 | # The parameter is an array with an explicit count parameter | 
|  | 3422 | lenParam = self.getLenParam(values, value.len) | 
| Dustin Graves | 7633d69 | 2016-04-14 15:03:31 -0600 | [diff] [blame] | 3423 | lenDisplayName = '{}{}'.format(displayNamePrefix, lenParam.name) | 
| Dustin Graves | af0d6dc | 2016-03-02 18:23:29 -0700 | [diff] [blame] | 3424 | if lenParam.ispointer: | 
|  | 3425 | # Count parameters that are pointers are inout | 
|  | 3426 | if type(lenParam.isoptional) is list: | 
|  | 3427 | if lenParam.isoptional[0]: | 
| Dustin Graves | bb84994 | 2016-04-05 13:48:15 -0600 | [diff] [blame] | 3428 | cpReq = 'false' | 
| Dustin Graves | af0d6dc | 2016-03-02 18:23:29 -0700 | [diff] [blame] | 3429 | if lenParam.isoptional[1]: | 
| Dustin Graves | bb84994 | 2016-04-05 13:48:15 -0600 | [diff] [blame] | 3430 | cvReq = 'false' | 
| Dustin Graves | af0d6dc | 2016-03-02 18:23:29 -0700 | [diff] [blame] | 3431 | else: | 
|  | 3432 | if lenParam.isoptional: | 
| Dustin Graves | bb84994 | 2016-04-05 13:48:15 -0600 | [diff] [blame] | 3433 | cpReq = 'false' | 
| Dustin Graves | af0d6dc | 2016-03-02 18:23:29 -0700 | [diff] [blame] | 3434 | else: | 
|  | 3435 | if lenParam.isoptional: | 
| Dustin Graves | bb84994 | 2016-04-05 13:48:15 -0600 | [diff] [blame] | 3436 | cvReq = 'false' | 
| Dustin Graves | af0d6dc | 2016-03-02 18:23:29 -0700 | [diff] [blame] | 3437 | # | 
|  | 3438 | # If this is a pointer to a struct with an sType field, verify the type | 
|  | 3439 | if value.type in self.structTypes: | 
| Dustin Graves | 7633d69 | 2016-04-14 15:03:31 -0600 | [diff] [blame] | 3440 | usedLines += self.makeStructTypeCheck(valuePrefix, value, lenParam, req, cvReq, cpReq, funcName, lenDisplayName, valueDisplayName) | 
| Dustin Graves | 2949a5a | 2016-03-08 17:48:20 -0700 | [diff] [blame] | 3441 | elif value.name == 'pNext': | 
|  | 3442 | # We need to ignore VkDeviceCreateInfo and VkInstanceCreateInfo, as the loader manipulates them in a way that is not documented in vk.xml | 
| Dustin Graves | 62448bf | 2016-04-11 16:06:25 -0600 | [diff] [blame] | 3443 | if not structTypeName in ['VkDeviceCreateInfo', 'VkInstanceCreateInfo']: | 
| Dustin Graves | 7633d69 | 2016-04-14 15:03:31 -0600 | [diff] [blame] | 3444 | usedLines += self.makeStructNextCheck(valuePrefix, value, funcName, valueDisplayName) | 
| Dustin Graves | af0d6dc | 2016-03-02 18:23:29 -0700 | [diff] [blame] | 3445 | else: | 
| Dustin Graves | 7633d69 | 2016-04-14 15:03:31 -0600 | [diff] [blame] | 3446 | usedLines += self.makePointerCheck(valuePrefix, value, lenParam, req, cvReq, cpReq, funcName, lenDisplayName, valueDisplayName) | 
| Dustin Graves | af0d6dc | 2016-03-02 18:23:29 -0700 | [diff] [blame] | 3447 | # | 
| Dustin Graves | cd99e56 | 2016-03-31 09:50:42 -0600 | [diff] [blame] | 3448 | # If this is a pointer to a struct (input), see if it contains members that need to be checked | 
|  | 3449 | if value.type in self.validatedStructs and value.isconst: | 
| Dustin Graves | 7633d69 | 2016-04-14 15:03:31 -0600 | [diff] [blame] | 3450 | usedLines.append(self.expandStructPointerCode(valuePrefix, value, lenParam, funcName, valueDisplayName)) | 
| Dustin Graves | c3fc3d8 | 2016-03-23 19:44:00 -0600 | [diff] [blame] | 3451 | elif value.isbool and value.isconst: | 
| Dustin Graves | 7633d69 | 2016-04-14 15:03:31 -0600 | [diff] [blame] | 3452 | usedLines.append('skipCall |= validate_bool32_array(report_data, "{}", "{}", {pf}{}, {pf}{});\n'.format(funcName, valueDisplayName, lenParam.name, value.name, pf=valuePrefix)) | 
| Dustin Graves | c3fc3d8 | 2016-03-23 19:44:00 -0600 | [diff] [blame] | 3453 | elif value.israngedenum and value.isconst: | 
|  | 3454 | enumRange = self.enumRanges[value.type] | 
| Dustin Graves | 7633d69 | 2016-04-14 15:03:31 -0600 | [diff] [blame] | 3455 | usedLines.append('skipCall |= validate_ranged_enum_array(report_data, "{}", "{}", "{}", {}, {}, {pf}{}, {pf}{});\n'.format(funcName, valueDisplayName, value.type, enumRange[0], enumRange[1], lenParam.name, value.name, pf=valuePrefix)) | 
|  | 3456 | # Non-pointer types | 
| Dustin Graves | af0d6dc | 2016-03-02 18:23:29 -0700 | [diff] [blame] | 3457 | elif value.type in self.validatedStructs: | 
| Dustin Graves | 7633d69 | 2016-04-14 15:03:31 -0600 | [diff] [blame] | 3458 | memberNamePrefix = '{}{}.'.format(valuePrefix, value.name) | 
|  | 3459 | memberDisplayNamePrefix = '{}.'.format(valueDisplayName) | 
|  | 3460 | usedLines.append(self.expandStructCode(self.validatedStructs[value.type], funcName, memberNamePrefix, memberDisplayNamePrefix, '', [])) | 
| Dustin Graves | c3fc3d8 | 2016-03-23 19:44:00 -0600 | [diff] [blame] | 3461 | elif value.isbool: | 
| Dustin Graves | 7633d69 | 2016-04-14 15:03:31 -0600 | [diff] [blame] | 3462 | usedLines.append('skipCall |= validate_bool32(report_data, "{}", "{}", {}{});\n'.format(funcName, valueDisplayName, valuePrefix, value.name)) | 
| Dustin Graves | c3fc3d8 | 2016-03-23 19:44:00 -0600 | [diff] [blame] | 3463 | elif value.israngedenum: | 
|  | 3464 | enumRange = self.enumRanges[value.type] | 
| Dustin Graves | 7633d69 | 2016-04-14 15:03:31 -0600 | [diff] [blame] | 3465 | usedLines.append('skipCall |= validate_ranged_enum(report_data, "{}", "{}", "{}", {}, {}, {}{});\n'.format(funcName, valueDisplayName, value.type, enumRange[0], enumRange[1], valuePrefix, value.name)) | 
| Dustin Graves | af0d6dc | 2016-03-02 18:23:29 -0700 | [diff] [blame] | 3466 | # | 
|  | 3467 | # Append the parameter check to the function body for the current command | 
| Dustin Graves | 7633d69 | 2016-04-14 15:03:31 -0600 | [diff] [blame] | 3468 | if usedLines: | 
| Dustin Graves | d59852f | 2016-04-15 18:06:14 -0600 | [diff] [blame^] | 3469 | # Apply special conditional checks | 
|  | 3470 | if value.condition: | 
|  | 3471 | usedLines = self.genConditionalCall(valuePrefix, value.condition, usedLines) | 
| Dustin Graves | 7633d69 | 2016-04-14 15:03:31 -0600 | [diff] [blame] | 3472 | lines += usedLines | 
| Dustin Graves | 80c0dea | 2016-03-03 14:17:08 -0700 | [diff] [blame] | 3473 | elif not value.iscount: | 
| Dustin Graves | c3fc3d8 | 2016-03-23 19:44:00 -0600 | [diff] [blame] | 3474 | # If no expression was generated for this value, it is unreferenced by the validation function, unless | 
|  | 3475 | # it is an array count, which is indirectly referenced for array valiadation. | 
| Dustin Graves | 80c0dea | 2016-03-03 14:17:08 -0700 | [diff] [blame] | 3476 | unused.append(value.name) | 
| Dustin Graves | 62448bf | 2016-04-11 16:06:25 -0600 | [diff] [blame] | 3477 | return lines, unused | 
| Dustin Graves | af0d6dc | 2016-03-02 18:23:29 -0700 | [diff] [blame] | 3478 | # | 
| Dustin Graves | af0d6dc | 2016-03-02 18:23:29 -0700 | [diff] [blame] | 3479 | # Generate the struct member check code from the captured data | 
|  | 3480 | def processStructMemberData(self): | 
|  | 3481 | indent = self.incIndent(None) | 
|  | 3482 | for struct in self.structMembers: | 
| Dustin Graves | c3fc3d8 | 2016-03-23 19:44:00 -0600 | [diff] [blame] | 3483 | # | 
|  | 3484 | # The string returned by genFuncBody will be nested in an if check for a NULL pointer, so needs its indent incremented | 
| Dustin Graves | 7633d69 | 2016-04-14 15:03:31 -0600 | [diff] [blame] | 3485 | lines, unused = self.genFuncBody('{funcName}', struct.members, '{valuePrefix}', '{displayNamePrefix}', struct.name) | 
| Dustin Graves | 62448bf | 2016-04-11 16:06:25 -0600 | [diff] [blame] | 3486 | if lines: | 
| Dustin Graves | 7633d69 | 2016-04-14 15:03:31 -0600 | [diff] [blame] | 3487 | self.validatedStructs[struct.name] = lines | 
| Dustin Graves | af0d6dc | 2016-03-02 18:23:29 -0700 | [diff] [blame] | 3488 | # | 
|  | 3489 | # Generate the command param check code from the captured data | 
|  | 3490 | def processCmdData(self): | 
|  | 3491 | indent = self.incIndent(None) | 
|  | 3492 | for command in self.commands: | 
| Dustin Graves | 7633d69 | 2016-04-14 15:03:31 -0600 | [diff] [blame] | 3493 | lines, unused = self.genFuncBody(command.name, command.params, '', '', None) | 
| Dustin Graves | 62448bf | 2016-04-11 16:06:25 -0600 | [diff] [blame] | 3494 | if lines: | 
| Dustin Graves | af0d6dc | 2016-03-02 18:23:29 -0700 | [diff] [blame] | 3495 | cmdDef = self.getCmdDef(command) + '\n' | 
|  | 3496 | cmdDef += '{\n' | 
| Dustin Graves | 62448bf | 2016-04-11 16:06:25 -0600 | [diff] [blame] | 3497 | # Process unused parameters, Ignoring the first dispatch handle parameter, which is not | 
|  | 3498 | # processed by parameter_validation (except for vkCreateInstance, which does not have a | 
|  | 3499 | # handle as its first parameter) | 
|  | 3500 | if unused: | 
|  | 3501 | startIndex = 0 if command.name == 'vkCreateInstance' else 1 | 
|  | 3502 | for name in unused[startIndex:]: | 
|  | 3503 | cmdDef += indent + 'UNUSED_PARAMETER({});\n'.format(name) | 
|  | 3504 | if len(unused) > startIndex: | 
|  | 3505 | cmdDef += '\n' | 
| Dustin Graves | bb84994 | 2016-04-05 13:48:15 -0600 | [diff] [blame] | 3506 | cmdDef += indent + 'bool skipCall = false;\n' | 
| Dustin Graves | 62448bf | 2016-04-11 16:06:25 -0600 | [diff] [blame] | 3507 | for line in lines: | 
|  | 3508 | cmdDef += '\n' | 
|  | 3509 | if type(line) is list: | 
|  | 3510 | for sub in line: | 
|  | 3511 | cmdDef += indent + sub | 
|  | 3512 | else: | 
|  | 3513 | cmdDef += indent + line | 
| Dustin Graves | af0d6dc | 2016-03-02 18:23:29 -0700 | [diff] [blame] | 3514 | cmdDef += '\n' | 
|  | 3515 | cmdDef += indent + 'return skipCall;\n' | 
|  | 3516 | cmdDef += '}\n' | 
|  | 3517 | self.appendSection('command', cmdDef) |