1#!/usr/bin/python3 2# 3# Copyright (c) 2013-2019 The Khronos Group Inc. 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16 17import argparse, cProfile, pdb, string, sys, time 18from reg import * 19from generator import write 20from cgenerator import CGeneratorOptions, COutputGenerator 21from docgenerator import DocGeneratorOptions, DocOutputGenerator 22from extensionmetadocgenerator import ExtensionMetaDocGeneratorOptions, ExtensionMetaDocOutputGenerator 23from pygenerator import PyOutputGenerator 24from validitygenerator import ValidityOutputGenerator 25from hostsyncgenerator import HostSynchronizationOutputGenerator 26from extensionStubSource import ExtensionStubSourceOutputGenerator 27 28# Simple timer functions 29startTime = None 30 31def startTimer(timeit): 32 global startTime 33 if timeit: 34 startTime = time.process_time() 35 36def endTimer(timeit, msg): 37 global startTime 38 if timeit: 39 endTime = time.process_time() 40 write(msg, endTime - startTime, file=sys.stderr) 41 startTime = None 42 43# Turn a list of strings into a regexp string matching exactly those strings 44def makeREstring(list, default = None): 45 if len(list) > 0 or default == None: 46 return '^(' + '|'.join(list) + ')$' 47 else: 48 return default 49 50# Returns a directory of [ generator function, generator options ] indexed 51# by specified short names. The generator options incorporate the following 52# parameters: 53# 54# args is an parsed argument object; see below for the fields that are used. 55def makeGenOpts(args): 56 global genOpts 57 genOpts = {} 58 59 # Default class of extensions to include, or None 60 defaultExtensions = args.defaultExtensions 61 62 # Additional extensions to include (list of extensions) 63 extensions = args.extension 64 65 # Extensions to remove (list of extensions) 66 removeExtensions = args.removeExtensions 67 68 # Extensions to emit (list of extensions) 69 emitExtensions = args.emitExtensions 70 71 # Features to include (list of features) 72 features = args.feature 73 74 # Whether to disable inclusion protect in headers 75 protect = args.protect 76 77 # Output target directory 78 directory = args.directory 79 80 # Descriptive names for various regexp patterns used to select 81 # versions and extensions 82 allFeatures = allExtensions = '.*' 83 noFeatures = noExtensions = None 84 85 # Turn lists of names/patterns into matching regular expressions 86 addExtensionsPat = makeREstring(extensions, None) 87 removeExtensionsPat = makeREstring(removeExtensions, None) 88 emitExtensionsPat = makeREstring(emitExtensions, allExtensions) 89 featuresPat = makeREstring(features, allFeatures) 90 91 # Copyright text prefixing all headers (list of strings). 92 prefixStrings = [ 93 '/*', 94 '** Copyright (c) 2015-2019 The Khronos Group Inc.', 95 '**', 96 '** Licensed under the Apache License, Version 2.0 (the "License");', 97 '** you may not use this file except in compliance with the License.', 98 '** You may obtain a copy of the License at', 99 '**', 100 '** http://www.apache.org/licenses/LICENSE-2.0', 101 '**', 102 '** Unless required by applicable law or agreed to in writing, software', 103 '** distributed under the License is distributed on an "AS IS" BASIS,', 104 '** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.', 105 '** See the License for the specific language governing permissions and', 106 '** limitations under the License.', 107 '*/', 108 '' 109 ] 110 111 # Text specific to Vulkan headers 112 vkPrefixStrings = [ 113 '/*', 114 '** This header is generated from the Khronos Vulkan XML API Registry.', 115 '**', 116 '*/', 117 '' 118 ] 119 120 # Defaults for generating re-inclusion protection wrappers (or not) 121 protectFile = protect 122 protectFeature = protect 123 protectProto = protect 124 125 # API include files for spec and ref pages 126 # Overwrites include subdirectories in spec source tree 127 # The generated include files do not include the calling convention 128 # macros (apientry etc.), unlike the header files. 129 # Because the 1.0 core branch includes ref pages for extensions, 130 # all the extension interfaces need to be generated, even though 131 # none are used by the core spec itself. 132 genOpts['apiinc'] = [ 133 DocOutputGenerator, 134 DocGeneratorOptions( 135 filename = 'timeMarker', 136 directory = directory, 137 apiname = 'vulkan', 138 profile = None, 139 versions = featuresPat, 140 emitversions = featuresPat, 141 defaultExtensions = None, 142 addExtensions = addExtensionsPat, 143 removeExtensions = removeExtensionsPat, 144 emitExtensions = emitExtensionsPat, 145 prefixText = prefixStrings + vkPrefixStrings, 146 apicall = '', 147 apientry = '', 148 apientryp = '*', 149 alignFuncParam = 48, 150 expandEnumerants = False) 151 ] 152 153 # API names to validate man/api spec includes & links 154 genOpts['vkapi.py'] = [ 155 PyOutputGenerator, 156 DocGeneratorOptions( 157 filename = 'vkapi.py', 158 directory = directory, 159 apiname = 'vulkan', 160 profile = None, 161 versions = featuresPat, 162 emitversions = featuresPat, 163 defaultExtensions = None, 164 addExtensions = addExtensionsPat, 165 removeExtensions = removeExtensionsPat, 166 emitExtensions = emitExtensionsPat) 167 ] 168 169 # API validity files for spec 170 genOpts['validinc'] = [ 171 ValidityOutputGenerator, 172 DocGeneratorOptions( 173 filename = 'timeMarker', 174 directory = directory, 175 apiname = 'vulkan', 176 profile = None, 177 versions = featuresPat, 178 emitversions = featuresPat, 179 defaultExtensions = None, 180 addExtensions = addExtensionsPat, 181 removeExtensions = removeExtensionsPat, 182 emitExtensions = emitExtensionsPat) 183 ] 184 185 # API host sync table files for spec 186 genOpts['hostsyncinc'] = [ 187 HostSynchronizationOutputGenerator, 188 DocGeneratorOptions( 189 filename = 'timeMarker', 190 directory = directory, 191 apiname = 'vulkan', 192 profile = None, 193 versions = featuresPat, 194 emitversions = featuresPat, 195 defaultExtensions = None, 196 addExtensions = addExtensionsPat, 197 removeExtensions = removeExtensionsPat, 198 emitExtensions = emitExtensionsPat) 199 ] 200 201 # Extension stub source dispatcher 202 # This target is no longer maintained and supported. 203 # See README.adoc for discussion. 204 genOpts['vulkan_ext.c'] = [ 205 ExtensionStubSourceOutputGenerator, 206 CGeneratorOptions( 207 filename = 'vulkan_ext.c', 208 directory = directory, 209 apiname = 'vulkan', 210 profile = None, 211 versions = featuresPat, 212 emitversions = None, 213 defaultExtensions = None, 214 addExtensions = '.*', 215 removeExtensions = removeExtensionsPat, 216 emitExtensions = emitExtensionsPat, 217 prefixText = prefixStrings + vkPrefixStrings, 218 alignFuncParam = 48) 219 ] 220 221 # Extension metainformation for spec extension appendices 222 genOpts['extinc'] = [ 223 ExtensionMetaDocOutputGenerator, 224 ExtensionMetaDocGeneratorOptions( 225 filename = 'timeMarker', 226 directory = directory, 227 apiname = 'vulkan', 228 profile = None, 229 versions = featuresPat, 230 emitversions = None, 231 defaultExtensions = defaultExtensions, 232 addExtensions = None, 233 removeExtensions = None, 234 emitExtensions = emitExtensionsPat) 235 ] 236 237 # Platform extensions, in their own header files 238 # Each element of the platforms[] array defines information for 239 # generating a single platform: 240 # [0] is the generated header file name 241 # [1] is the set of platform extensions to generate 242 # [2] is additional extensions whose interfaces should be considered, 243 # but suppressed in the output, to avoid duplicate definitions of 244 # dependent types like VkDisplayKHR and VkSurfaceKHR which come from 245 # non-platform extensions. 246 247 # Track all platform extensions, for exclusion from vulkan_core.h 248 allPlatformExtensions = [] 249 250 # Extensions suppressed for all platforms. 251 # Covers common WSI extension types. 252 commonSuppressExtensions = [ 'VK_KHR_display', 'VK_KHR_swapchain' ] 253 254 platforms = [ 255 [ 'vulkan_android.h', [ 'VK_KHR_android_surface', 256 'VK_ANDROID_external_memory_android_hardware_buffer' 257 ], commonSuppressExtensions ], 258 [ 'vulkan_fuchsia.h', [ 'VK_FUCHSIA_imagepipe_surface'], commonSuppressExtensions ], 259 [ 'vulkan_ios.h', [ 'VK_MVK_ios_surface' ], commonSuppressExtensions ], 260 [ 'vulkan_macos.h', [ 'VK_MVK_macos_surface' ], commonSuppressExtensions ], 261 [ 'vulkan_vi.h', [ 'VK_NN_vi_surface' ], commonSuppressExtensions ], 262 [ 'vulkan_wayland.h', [ 'VK_KHR_wayland_surface' ], commonSuppressExtensions ], 263 [ 'vulkan_win32.h', [ 'VK_.*_win32(|_.*)' ], commonSuppressExtensions + [ 'VK_KHR_external_semaphore', 'VK_KHR_external_memory_capabilities', 'VK_KHR_external_fence', 'VK_KHR_external_fence_capabilities', 'VK_NV_external_memory_capabilities' ] ], 264 [ 'vulkan_xcb.h', [ 'VK_KHR_xcb_surface' ], commonSuppressExtensions ], 265 [ 'vulkan_xlib.h', [ 'VK_KHR_xlib_surface' ], commonSuppressExtensions ], 266 [ 'vulkan_xlib_xrandr.h', [ 'VK_EXT_acquire_xlib_display' ], commonSuppressExtensions ], 267 [ 'vulkan_metal.h', [ 'VK_EXT_metal_surface' ], commonSuppressExtensions ], 268 ] 269 270 for platform in platforms: 271 headername = platform[0] 272 273 allPlatformExtensions += platform[1] 274 275 addPlatformExtensionsRE = makeREstring(platform[1] + platform[2]) 276 emitPlatformExtensionsRE = makeREstring(platform[1]) 277 278 opts = CGeneratorOptions( 279 filename = headername, 280 directory = directory, 281 apiname = 'vulkan', 282 profile = None, 283 versions = featuresPat, 284 emitversions = None, 285 defaultExtensions = None, 286 addExtensions = addPlatformExtensionsRE, 287 removeExtensions = None, 288 emitExtensions = emitPlatformExtensionsRE, 289 prefixText = prefixStrings + vkPrefixStrings, 290 genFuncPointers = True, 291 protectFile = protectFile, 292 protectFeature = False, 293 protectProto = '#ifndef', 294 protectProtoStr = 'VK_NO_PROTOTYPES', 295 apicall = 'VKAPI_ATTR ', 296 apientry = 'VKAPI_CALL ', 297 apientryp = 'VKAPI_PTR *', 298 alignFuncParam = 48) 299 300 genOpts[headername] = [ COutputGenerator, opts ] 301 302 # Header for core API + extensions. 303 # To generate just the core API, 304 # change to 'defaultExtensions = None' below. 305 # 306 # By default this adds all enabled, non-platform extensions. 307 # It removes all platform extensions (from the platform headers options 308 # constructed above) as well as any explicitly specified removals. 309 310 removeExtensionsPat = makeREstring(allPlatformExtensions + removeExtensions, None) 311 312 genOpts['vulkan_core.h'] = [ 313 COutputGenerator, 314 CGeneratorOptions( 315 filename = 'vulkan_core.h', 316 directory = directory, 317 apiname = 'vulkan', 318 profile = None, 319 versions = featuresPat, 320 emitversions = featuresPat, 321 defaultExtensions = defaultExtensions, 322 addExtensions = None, 323 removeExtensions = removeExtensionsPat, 324 emitExtensions = emitExtensionsPat, 325 prefixText = prefixStrings + vkPrefixStrings, 326 genFuncPointers = True, 327 protectFile = protectFile, 328 protectFeature = False, 329 protectProto = '#ifndef', 330 protectProtoStr = 'VK_NO_PROTOTYPES', 331 apicall = 'VKAPI_ATTR ', 332 apientry = 'VKAPI_CALL ', 333 apientryp = 'VKAPI_PTR *', 334 alignFuncParam = 48) 335 ] 336 337 # Unused - vulkan10.h target. 338 # It is possible to generate a header with just the Vulkan 1.0 + 339 # extension interfaces defined, but since the promoted KHR extensions 340 # are now defined in terms of the 1.1 interfaces, such a header is very 341 # similar to vulkan_core.h. 342 genOpts['vulkan10.h'] = [ 343 COutputGenerator, 344 CGeneratorOptions( 345 filename = 'vulkan10.h', 346 directory = directory, 347 apiname = 'vulkan', 348 profile = None, 349 versions = 'VK_VERSION_1_0', 350 emitversions = 'VK_VERSION_1_0', 351 defaultExtensions = defaultExtensions, 352 addExtensions = None, 353 removeExtensions = removeExtensionsPat, 354 emitExtensions = emitExtensionsPat, 355 prefixText = prefixStrings + vkPrefixStrings, 356 genFuncPointers = True, 357 protectFile = protectFile, 358 protectFeature = False, 359 protectProto = '#ifndef', 360 protectProtoStr = 'VK_NO_PROTOTYPES', 361 apicall = 'VKAPI_ATTR ', 362 apientry = 'VKAPI_CALL ', 363 apientryp = 'VKAPI_PTR *', 364 alignFuncParam = 48) 365 ] 366 367 genOpts['alias.h'] = [ 368 COutputGenerator, 369 CGeneratorOptions( 370 filename = 'alias.h', 371 directory = directory, 372 apiname = 'vulkan', 373 profile = None, 374 versions = featuresPat, 375 emitversions = featuresPat, 376 defaultExtensions = defaultExtensions, 377 addExtensions = None, 378 removeExtensions = removeExtensionsPat, 379 emitExtensions = emitExtensionsPat, 380 prefixText = None, 381 genFuncPointers = False, 382 protectFile = False, 383 protectFeature = False, 384 protectProto = '', 385 protectProtoStr = '', 386 apicall = '', 387 apientry = '', 388 apientryp = '', 389 alignFuncParam = 36) 390 ] 391 392# Generate a target based on the options in the matching genOpts{} object. 393# This is encapsulated in a function so it can be profiled and/or timed. 394# The args parameter is an parsed argument object containing the following 395# fields that are used: 396# target - target to generate 397# directory - directory to generate it in 398# protect - True if re-inclusion wrappers should be created 399# extensions - list of additional extensions to include in generated 400# interfaces 401def genTarget(args): 402 global genOpts 403 404 # Create generator options with specified parameters 405 makeGenOpts(args) 406 407 if (args.target in genOpts.keys()): 408 createGenerator = genOpts[args.target][0] 409 options = genOpts[args.target][1] 410 411 if not args.quiet: 412 write('* Building', options.filename, file=sys.stderr) 413 write('* options.versions =', options.versions, file=sys.stderr) 414 write('* options.emitversions =', options.emitversions, file=sys.stderr) 415 write('* options.defaultExtensions =', options.defaultExtensions, file=sys.stderr) 416 write('* options.addExtensions =', options.addExtensions, file=sys.stderr) 417 write('* options.removeExtensions =', options.removeExtensions, file=sys.stderr) 418 write('* options.emitExtensions =', options.emitExtensions, file=sys.stderr) 419 420 startTimer(args.time) 421 gen = createGenerator(errFile=errWarn, 422 warnFile=errWarn, 423 diagFile=diag) 424 reg.setGenerator(gen) 425 reg.apiGen(options) 426 427 if not args.quiet: 428 write('* Generated', options.filename, file=sys.stderr) 429 endTimer(args.time, '* Time to generate ' + options.filename + ' =') 430 else: 431 write('No generator options for unknown target:', 432 args.target, file=sys.stderr) 433 434# -feature name 435# -extension name 436# For both, "name" may be a single name, or a space-separated list 437# of names, or a regular expression. 438if __name__ == '__main__': 439 parser = argparse.ArgumentParser() 440 441 parser.add_argument('-defaultExtensions', action='store', 442 default='vulkan', 443 help='Specify a single class of extensions to add to targets') 444 parser.add_argument('-extension', action='append', 445 default=[], 446 help='Specify an extension or extensions to add to targets') 447 parser.add_argument('-removeExtensions', action='append', 448 default=[], 449 help='Specify an extension or extensions to remove from targets') 450 parser.add_argument('-emitExtensions', action='append', 451 default=[], 452 help='Specify an extension or extensions to emit in targets') 453 parser.add_argument('-feature', action='append', 454 default=[], 455 help='Specify a core API feature name or names to add to targets') 456 parser.add_argument('-debug', action='store_true', 457 help='Enable debugging') 458 parser.add_argument('-dump', action='store_true', 459 help='Enable dump to stderr') 460 parser.add_argument('-diagfile', action='store', 461 default=None, 462 help='Write diagnostics to specified file') 463 parser.add_argument('-errfile', action='store', 464 default=None, 465 help='Write errors and warnings to specified file instead of stderr') 466 parser.add_argument('-noprotect', dest='protect', action='store_false', 467 help='Disable inclusion protection in output headers') 468 parser.add_argument('-profile', action='store_true', 469 help='Enable profiling') 470 parser.add_argument('-registry', action='store', 471 default='vk.xml', 472 help='Use specified registry file instead of vk.xml') 473 parser.add_argument('-time', action='store_true', 474 help='Enable timing') 475 parser.add_argument('-validate', action='store_true', 476 help='Enable group validation') 477 parser.add_argument('-o', action='store', dest='directory', 478 default='.', 479 help='Create target and related files in specified directory') 480 parser.add_argument('target', metavar='target', nargs='?', 481 help='Specify target') 482 parser.add_argument('-quiet', action='store_true', default=True, 483 help='Suppress script output during normal execution.') 484 parser.add_argument('-verbose', action='store_false', dest='quiet', default=True, 485 help='Enable script output during normal execution.') 486 487 args = parser.parse_args() 488 489 # This splits arguments which are space-separated lists 490 args.feature = [name for arg in args.feature for name in arg.split()] 491 args.extension = [name for arg in args.extension for name in arg.split()] 492 493 # Load & parse registry 494 reg = Registry() 495 496 startTimer(args.time) 497 tree = etree.parse(args.registry) 498 endTimer(args.time, '* Time to make ElementTree =') 499 500 if args.debug: 501 pdb.run('reg.loadElementTree(tree)') 502 else: 503 startTimer(args.time) 504 reg.loadElementTree(tree) 505 endTimer(args.time, '* Time to parse ElementTree =') 506 507 if (args.validate): 508 reg.validateGroups() 509 510 if (args.dump): 511 write('* Dumping registry to regdump.txt', file=sys.stderr) 512 reg.dumpReg(filehandle = open('regdump.txt', 'w', encoding='utf-8')) 513 514 # create error/warning & diagnostic files 515 if (args.errfile): 516 errWarn = open(args.errfile, 'w', encoding='utf-8') 517 else: 518 errWarn = sys.stderr 519 520 if (args.diagfile): 521 diag = open(args.diagfile, 'w', encoding='utf-8') 522 else: 523 diag = None 524 525 if (args.debug): 526 pdb.run('genTarget(args)') 527 elif (args.profile): 528 import cProfile, pstats 529 cProfile.run('genTarget(args)', 'profile.txt') 530 p = pstats.Stats('profile.txt') 531 p.strip_dirs().sort_stats('time').print_stats(50) 532 else: 533 genTarget(args) 534