1#!/usr/bin/python3 2# 3# Copyright (c) 2013-2020 The Khronos Group Inc. 4# 5# SPDX-License-Identifier: Apache-2.0 6 7import argparse 8import pdb 9import re 10import sys 11import time 12import xml.etree.ElementTree as etree 13 14from cgenerator import CGeneratorOptions, COutputGenerator 15from cerealgenerator import CerealGenerator 16from docgenerator import DocGeneratorOptions, DocOutputGenerator 17from extensionmetadocgenerator import (ExtensionMetaDocGeneratorOptions, 18 ExtensionMetaDocOutputGenerator) 19from interfacedocgenerator import InterfaceDocGenerator 20from generator import write 21from spirvcapgenerator import SpirvCapabilityOutputGenerator 22from hostsyncgenerator import HostSynchronizationOutputGenerator 23from pygenerator import PyOutputGenerator 24from reflib import logDiag, logWarn, setLogFile 25from reg import Registry 26from validitygenerator import ValidityOutputGenerator 27from vkconventions import VulkanConventions 28 29 30# Simple timer functions 31startTime = None 32 33 34def startTimer(timeit): 35 global startTime 36 if timeit: 37 startTime = time.process_time() 38 39 40def endTimer(timeit, msg): 41 global startTime 42 if timeit: 43 endTime = time.process_time() 44 logDiag(msg, endTime - startTime) 45 startTime = None 46 47 48def makeREstring(strings, default=None, strings_are_regex=False): 49 """Turn a list of strings into a regexp string matching exactly those strings.""" 50 if strings or default is None: 51 if not strings_are_regex: 52 strings = (re.escape(s) for s in strings) 53 return '^(' + '|'.join(strings) + ')$' 54 return default 55 56 57def makeGenOpts(args): 58 """Returns a directory of [ generator function, generator options ] indexed 59 by specified short names. The generator options incorporate the following 60 parameters: 61 62 args is an parsed argument object; see below for the fields that are used.""" 63 global genOpts 64 genOpts = {} 65 66 # Default class of extensions to include, or None 67 defaultExtensions = args.defaultExtensions 68 69 # Additional extensions to include (list of extensions) 70 extensions = args.extension 71 72 # Extensions to remove (list of extensions) 73 removeExtensions = args.removeExtensions 74 75 # Extensions to emit (list of extensions) 76 emitExtensions = args.emitExtensions 77 78 # SPIR-V capabilities / features to emit (list of extensions & capabilities) 79 emitSpirv = args.emitSpirv 80 81 # Features to include (list of features) 82 features = args.feature 83 84 # Whether to disable inclusion protect in headers 85 protect = args.protect 86 87 # Output target directory 88 directory = args.directory 89 90 # Path to generated files, particularly api.py 91 genpath = args.genpath 92 93 # Descriptive names for various regexp patterns used to select 94 # versions and extensions 95 allSpirv = allFeatures = allExtensions = r'.*' 96 97 # Turn lists of names/patterns into matching regular expressions 98 addExtensionsPat = makeREstring(extensions, None) 99 removeExtensionsPat = makeREstring(removeExtensions, None) 100 emitExtensionsPat = makeREstring(emitExtensions, allExtensions) 101 emitSpirvPat = makeREstring(emitSpirv, allSpirv) 102 featuresPat = makeREstring(features, allFeatures) 103 104 # Copyright text prefixing all headers (list of strings). 105 # The SPDX formatting below works around constraints of the 'reuse' tool 106 prefixStrings = [ 107 '/*', 108 '** Copyright (c) 2015-2020 The Khronos Group Inc.', 109 '**', 110 '** SPDX' + '-License-Identifier: Apache-2.0', 111 '*/', 112 '' 113 ] 114 115 # Text specific to Vulkan headers 116 vkPrefixStrings = [ 117 '/*', 118 '** This header is generated from the Khronos Vulkan XML API Registry.', 119 '**', 120 '*/', 121 '' 122 ] 123 124 # Defaults for generating re-inclusion protection wrappers (or not) 125 protectFile = protect 126 127 # An API style conventions object 128 conventions = VulkanConventions() 129 130 # API include files for spec and ref pages 131 # Overwrites include subdirectories in spec source tree 132 # The generated include files do not include the calling convention 133 # macros (apientry etc.), unlike the header files. 134 # Because the 1.0 core branch includes ref pages for extensions, 135 # all the extension interfaces need to be generated, even though 136 # none are used by the core spec itself. 137 genOpts['apiinc'] = [ 138 DocOutputGenerator, 139 DocGeneratorOptions( 140 conventions = conventions, 141 filename = 'timeMarker', 142 directory = directory, 143 genpath = genpath, 144 apiname = 'vulkan', 145 profile = None, 146 versions = featuresPat, 147 emitversions = featuresPat, 148 defaultExtensions = None, 149 addExtensions = addExtensionsPat, 150 removeExtensions = removeExtensionsPat, 151 emitExtensions = emitExtensionsPat, 152 prefixText = prefixStrings + vkPrefixStrings, 153 apicall = '', 154 apientry = '', 155 apientryp = '*', 156 alignFuncParam = 48, 157 expandEnumerants = False) 158 ] 159 160 # Python representation of API information, used by scripts that 161 # don't need to load the full XML. 162 genOpts['api.py'] = [ 163 PyOutputGenerator, 164 DocGeneratorOptions( 165 conventions = conventions, 166 filename = 'api.py', 167 directory = directory, 168 genpath = None, 169 apiname = 'vulkan', 170 profile = None, 171 versions = featuresPat, 172 emitversions = featuresPat, 173 defaultExtensions = None, 174 addExtensions = addExtensionsPat, 175 removeExtensions = removeExtensionsPat, 176 emitExtensions = emitExtensionsPat, 177 reparentEnums = False) 178 ] 179 180 # API validity files for spec 181 genOpts['validinc'] = [ 182 ValidityOutputGenerator, 183 DocGeneratorOptions( 184 conventions = conventions, 185 filename = 'timeMarker', 186 directory = directory, 187 genpath = None, 188 apiname = 'vulkan', 189 profile = None, 190 versions = featuresPat, 191 emitversions = featuresPat, 192 defaultExtensions = None, 193 addExtensions = addExtensionsPat, 194 removeExtensions = removeExtensionsPat, 195 emitExtensions = emitExtensionsPat) 196 ] 197 198 # Serializer for spec 199 genOpts['cereal'] = [ 200 CerealGenerator, 201 CGeneratorOptions( 202 conventions = conventions, 203 filename = "CMakeLists.txt", 204 directory = directory, 205 apiname = 'vulkan', 206 profile = None, 207 versions = featuresPat, 208 emitversions = featuresPat, 209 defaultExtensions = defaultExtensions, 210 addExtensions = None, 211 removeExtensions = removeExtensionsPat, 212 emitExtensions = emitExtensionsPat, 213 prefixText = prefixStrings + vkPrefixStrings, 214 genFuncPointers = True, 215 protectFile = protectFile, 216 protectFeature = False, 217 protectProto = '#ifndef', 218 protectProtoStr = 'VK_NO_PROTOTYPES', 219 apicall = 'VKAPI_ATTR ', 220 apientry = 'VKAPI_CALL ', 221 apientryp = 'VKAPI_PTR *', 222 alignFuncParam = 48) 223 ] 224 225 # API host sync table files for spec 226 genOpts['hostsyncinc'] = [ 227 HostSynchronizationOutputGenerator, 228 DocGeneratorOptions( 229 conventions = conventions, 230 filename = 'timeMarker', 231 directory = directory, 232 genpath = None, 233 apiname = 'vulkan', 234 profile = None, 235 versions = featuresPat, 236 emitversions = featuresPat, 237 defaultExtensions = None, 238 addExtensions = addExtensionsPat, 239 removeExtensions = removeExtensionsPat, 240 emitExtensions = emitExtensionsPat, 241 reparentEnums = False) 242 ] 243 244 # Extension metainformation for spec extension appendices 245 # Includes all extensions by default, but only so that the generated 246 # 'promoted_extensions_*' files refer to all extensions that were 247 # promoted to a core version. 248 genOpts['extinc'] = [ 249 ExtensionMetaDocOutputGenerator, 250 ExtensionMetaDocGeneratorOptions( 251 conventions = conventions, 252 filename = 'timeMarker', 253 directory = directory, 254 genpath = None, 255 apiname = 'vulkan', 256 profile = None, 257 versions = featuresPat, 258 emitversions = None, 259 defaultExtensions = defaultExtensions, 260 addExtensions = addExtensionsPat, 261 removeExtensions = None, 262 emitExtensions = emitExtensionsPat) 263 ] 264 265 # Version and extension interface docs for version/extension appendices 266 # Includes all extensions by default. 267 genOpts['interfaceinc'] = [ 268 InterfaceDocGenerator, 269 DocGeneratorOptions( 270 conventions = conventions, 271 filename = 'timeMarker', 272 directory = directory, 273 genpath = None, 274 apiname = 'vulkan', 275 profile = None, 276 versions = featuresPat, 277 emitversions = featuresPat, 278 defaultExtensions = None, 279 addExtensions = addExtensionsPat, 280 removeExtensions = removeExtensionsPat, 281 emitExtensions = emitExtensionsPat, 282 reparentEnums = False) 283 ] 284 285 genOpts['spirvcapinc'] = [ 286 SpirvCapabilityOutputGenerator, 287 DocGeneratorOptions( 288 conventions = conventions, 289 filename = 'timeMarker', 290 directory = directory, 291 genpath = None, 292 apiname = 'vulkan', 293 profile = None, 294 versions = featuresPat, 295 emitversions = featuresPat, 296 defaultExtensions = None, 297 addExtensions = addExtensionsPat, 298 removeExtensions = removeExtensionsPat, 299 emitExtensions = emitExtensionsPat, 300 emitSpirv = emitSpirvPat, 301 reparentEnums = False) 302 ] 303 304 # Platform extensions, in their own header files 305 # Each element of the platforms[] array defines information for 306 # generating a single platform: 307 # [0] is the generated header file name 308 # [1] is the set of platform extensions to generate 309 # [2] is additional extensions whose interfaces should be considered, 310 # but suppressed in the output, to avoid duplicate definitions of 311 # dependent types like VkDisplayKHR and VkSurfaceKHR which come from 312 # non-platform extensions. 313 314 # Track all platform extensions, for exclusion from vulkan_core.h 315 allPlatformExtensions = [] 316 317 # Extensions suppressed for all WSI platforms (WSI extensions required 318 # by all platforms) 319 commonSuppressExtensions = [ 'VK_KHR_display', 'VK_KHR_swapchain' ] 320 321 # Extensions required and suppressed for beta "platform". This can 322 # probably eventually be derived from the requires= attributes of 323 # the extension blocks. 324 betaRequireExtensions = [ 'VK_KHR_portability_subset' ] 325 betaSuppressExtensions = [] 326 327 platforms = [ 328 [ 'vulkan_android.h', [ 'VK_KHR_android_surface', 329 'VK_ANDROID_external_memory_android_hardware_buffer' 330 ], commonSuppressExtensions ], 331 [ 'vulkan_fuchsia.h', [ 'VK_FUCHSIA_imagepipe_surface'], commonSuppressExtensions ], 332 [ 'vulkan_ggp.h', [ 'VK_GGP_stream_descriptor_surface', 333 'VK_GGP_frame_token' ], commonSuppressExtensions ], 334 [ 'vulkan_ios.h', [ 'VK_MVK_ios_surface' ], commonSuppressExtensions ], 335 [ 'vulkan_macos.h', [ 'VK_MVK_macos_surface' ], commonSuppressExtensions ], 336 [ 'vulkan_vi.h', [ 'VK_NN_vi_surface' ], commonSuppressExtensions ], 337 [ 'vulkan_wayland.h', [ 'VK_KHR_wayland_surface' ], commonSuppressExtensions ], 338 [ 'vulkan_win32.h', [ 'VK_.*_win32(|_.*)', 'VK_EXT_full_screen_exclusive' ], 339 commonSuppressExtensions + 340 [ 'VK_KHR_external_semaphore', 341 'VK_KHR_external_memory_capabilities', 342 'VK_KHR_external_fence', 343 'VK_KHR_external_fence_capabilities', 344 'VK_KHR_get_surface_capabilities2', 345 'VK_NV_external_memory_capabilities', 346 ] ], 347 [ 'vulkan_xcb.h', [ 'VK_KHR_xcb_surface' ], commonSuppressExtensions ], 348 [ 'vulkan_xlib.h', [ 'VK_KHR_xlib_surface' ], commonSuppressExtensions ], 349 [ 'vulkan_directfb.h', [ 'VK_EXT_directfb_surface' ], commonSuppressExtensions ], 350 [ 'vulkan_xlib_xrandr.h', [ 'VK_EXT_acquire_xlib_display' ], commonSuppressExtensions ], 351 [ 'vulkan_metal.h', [ 'VK_EXT_metal_surface' ], commonSuppressExtensions ], 352 [ 'vulkan_beta.h', betaRequireExtensions, betaSuppressExtensions ], 353 ] 354 355 for platform in platforms: 356 headername = platform[0] 357 358 allPlatformExtensions += platform[1] 359 360 addPlatformExtensionsRE = makeREstring( 361 platform[1] + platform[2], strings_are_regex=True) 362 emitPlatformExtensionsRE = makeREstring( 363 platform[1], strings_are_regex=True) 364 365 opts = CGeneratorOptions( 366 conventions = conventions, 367 filename = headername, 368 directory = directory, 369 genpath = None, 370 apiname = 'vulkan', 371 profile = None, 372 versions = featuresPat, 373 emitversions = None, 374 defaultExtensions = None, 375 addExtensions = addPlatformExtensionsRE, 376 removeExtensions = None, 377 emitExtensions = emitPlatformExtensionsRE, 378 prefixText = prefixStrings + vkPrefixStrings, 379 genFuncPointers = True, 380 protectFile = protectFile, 381 protectFeature = False, 382 protectProto = '#ifndef', 383 protectProtoStr = 'VK_NO_PROTOTYPES', 384 apicall = 'VKAPI_ATTR ', 385 apientry = 'VKAPI_CALL ', 386 apientryp = 'VKAPI_PTR *', 387 alignFuncParam = 48) 388 389 genOpts[headername] = [ COutputGenerator, opts ] 390 391 # Header for core API + extensions. 392 # To generate just the core API, 393 # change to 'defaultExtensions = None' below. 394 # 395 # By default this adds all enabled, non-platform extensions. 396 # It removes all platform extensions (from the platform headers options 397 # constructed above) as well as any explicitly specified removals. 398 399 removeExtensionsPat = makeREstring( 400 allPlatformExtensions + removeExtensions, None, strings_are_regex=True) 401 402 genOpts['vulkan_core.h'] = [ 403 COutputGenerator, 404 CGeneratorOptions( 405 conventions = conventions, 406 filename = 'vulkan_core.h', 407 directory = directory, 408 genpath = None, 409 apiname = 'vulkan', 410 profile = None, 411 versions = featuresPat, 412 emitversions = featuresPat, 413 defaultExtensions = defaultExtensions, 414 addExtensions = addExtensionsPat, 415 removeExtensions = removeExtensionsPat, 416 emitExtensions = emitExtensionsPat, 417 prefixText = prefixStrings + vkPrefixStrings, 418 genFuncPointers = True, 419 protectFile = protectFile, 420 protectFeature = False, 421 protectProto = '#ifndef', 422 protectProtoStr = 'VK_NO_PROTOTYPES', 423 apicall = 'VKAPI_ATTR ', 424 apientry = 'VKAPI_CALL ', 425 apientryp = 'VKAPI_PTR *', 426 alignFuncParam = 48) 427 ] 428 429 # Unused - vulkan10.h target. 430 # It is possible to generate a header with just the Vulkan 1.0 + 431 # extension interfaces defined, but since the promoted KHR extensions 432 # are now defined in terms of the 1.1 interfaces, such a header is very 433 # similar to vulkan_core.h. 434 genOpts['vulkan10.h'] = [ 435 COutputGenerator, 436 CGeneratorOptions( 437 conventions = conventions, 438 filename = 'vulkan10.h', 439 directory = directory, 440 genpath = None, 441 apiname = 'vulkan', 442 profile = None, 443 versions = 'VK_VERSION_1_0', 444 emitversions = 'VK_VERSION_1_0', 445 defaultExtensions = None, 446 addExtensions = None, 447 removeExtensions = None, 448 emitExtensions = None, 449 prefixText = prefixStrings + vkPrefixStrings, 450 genFuncPointers = True, 451 protectFile = protectFile, 452 protectFeature = False, 453 protectProto = '#ifndef', 454 protectProtoStr = 'VK_NO_PROTOTYPES', 455 apicall = 'VKAPI_ATTR ', 456 apientry = 'VKAPI_CALL ', 457 apientryp = 'VKAPI_PTR *', 458 alignFuncParam = 48) 459 ] 460 461 # Unused - vulkan11.h target. 462 # It is possible to generate a header with just the Vulkan 1.0 + 463 # extension interfaces defined, but since the promoted KHR extensions 464 # are now defined in terms of the 1.1 interfaces, such a header is very 465 # similar to vulkan_core.h. 466 genOpts['vulkan11.h'] = [ 467 COutputGenerator, 468 CGeneratorOptions( 469 conventions = conventions, 470 filename = 'vulkan11.h', 471 directory = directory, 472 genpath = None, 473 apiname = 'vulkan', 474 profile = None, 475 versions = '^VK_VERSION_1_[01]$', 476 emitversions = '^VK_VERSION_1_[01]$', 477 defaultExtensions = None, 478 addExtensions = None, 479 removeExtensions = None, 480 emitExtensions = None, 481 prefixText = prefixStrings + vkPrefixStrings, 482 genFuncPointers = True, 483 protectFile = protectFile, 484 protectFeature = False, 485 protectProto = '#ifndef', 486 protectProtoStr = 'VK_NO_PROTOTYPES', 487 apicall = 'VKAPI_ATTR ', 488 apientry = 'VKAPI_CALL ', 489 apientryp = 'VKAPI_PTR *', 490 alignFuncParam = 48) 491 ] 492 493 genOpts['alias.h'] = [ 494 COutputGenerator, 495 CGeneratorOptions( 496 conventions = conventions, 497 filename = 'alias.h', 498 directory = directory, 499 genpath = None, 500 apiname = 'vulkan', 501 profile = None, 502 versions = featuresPat, 503 emitversions = featuresPat, 504 defaultExtensions = defaultExtensions, 505 addExtensions = None, 506 removeExtensions = removeExtensionsPat, 507 emitExtensions = emitExtensionsPat, 508 prefixText = None, 509 genFuncPointers = False, 510 protectFile = False, 511 protectFeature = False, 512 protectProto = '', 513 protectProtoStr = '', 514 apicall = '', 515 apientry = '', 516 apientryp = '', 517 alignFuncParam = 36) 518 ] 519 520 521def genTarget(args): 522 """Create an API generator and corresponding generator options based on 523 the requested target and command line options. 524 525 This is encapsulated in a function so it can be profiled and/or timed. 526 The args parameter is an parsed argument object containing the following 527 fields that are used: 528 529 - target - target to generate 530 - directory - directory to generate it in 531 - protect - True if re-inclusion wrappers should be created 532 - extensions - list of additional extensions to include in generated interfaces""" 533 534 # Create generator options with parameters specified on command line 535 makeGenOpts(args) 536 537 # pdb.set_trace() 538 539 # Select a generator matching the requested target 540 if args.target in genOpts: 541 createGenerator = genOpts[args.target][0] 542 options = genOpts[args.target][1] 543 544 logDiag('* Building', options.filename) 545 logDiag('* options.versions =', options.versions) 546 logDiag('* options.emitversions =', options.emitversions) 547 logDiag('* options.defaultExtensions =', options.defaultExtensions) 548 logDiag('* options.addExtensions =', options.addExtensions) 549 logDiag('* options.removeExtensions =', options.removeExtensions) 550 logDiag('* options.emitExtensions =', options.emitExtensions) 551 552 gen = createGenerator(errFile=errWarn, 553 warnFile=errWarn, 554 diagFile=diag) 555 return (gen, options) 556 else: 557 logErr('No generator options for unknown target:', args.target) 558 return None 559 560 561# -feature name 562# -extension name 563# For both, "name" may be a single name, or a space-separated list 564# of names, or a regular expression. 565if __name__ == '__main__': 566 parser = argparse.ArgumentParser() 567 568 parser.add_argument('-defaultExtensions', action='store', 569 default='vulkan', 570 help='Specify a single class of extensions to add to targets') 571 parser.add_argument('-extension', action='append', 572 default=[], 573 help='Specify an extension or extensions to add to targets') 574 parser.add_argument('-removeExtensions', action='append', 575 default=[], 576 help='Specify an extension or extensions to remove from targets') 577 parser.add_argument('-emitExtensions', action='append', 578 default=[], 579 help='Specify an extension or extensions to emit in targets') 580 parser.add_argument('-emitSpirv', action='append', 581 default=[], 582 help='Specify a SPIR-V extension or capability to emit in targets') 583 parser.add_argument('-feature', action='append', 584 default=[], 585 help='Specify a core API feature name or names to add to targets') 586 parser.add_argument('-debug', action='store_true', 587 help='Enable debugging') 588 parser.add_argument('-dump', action='store_true', 589 help='Enable dump to stderr') 590 parser.add_argument('-diagfile', action='store', 591 default=None, 592 help='Write diagnostics to specified file') 593 parser.add_argument('-errfile', action='store', 594 default=None, 595 help='Write errors and warnings to specified file instead of stderr') 596 parser.add_argument('-noprotect', dest='protect', action='store_false', 597 help='Disable inclusion protection in output headers') 598 parser.add_argument('-profile', action='store_true', 599 help='Enable profiling') 600 parser.add_argument('-registry', action='store', 601 default='vk.xml', 602 help='Use specified registry file instead of vk.xml') 603 parser.add_argument('-time', action='store_true', 604 help='Enable timing') 605 parser.add_argument('-validate', action='store_true', 606 help='Enable XML group validation') 607 parser.add_argument('-genpath', action='store', default='gen', 608 help='Path to generated files') 609 parser.add_argument('-o', action='store', dest='directory', 610 default='.', 611 help='Create target and related files in specified directory') 612 parser.add_argument('target', metavar='target', nargs='?', 613 help='Specify target') 614 parser.add_argument('-quiet', action='store_true', default=True, 615 help='Suppress script output during normal execution.') 616 parser.add_argument('-verbose', action='store_false', dest='quiet', default=True, 617 help='Enable script output during normal execution.') 618 619 args = parser.parse_args() 620 621 # This splits arguments which are space-separated lists 622 args.feature = [name for arg in args.feature for name in arg.split()] 623 args.extension = [name for arg in args.extension for name in arg.split()] 624 625 # create error/warning & diagnostic files 626 if args.errfile: 627 errWarn = open(args.errfile, 'w', encoding='utf-8') 628 else: 629 errWarn = sys.stderr 630 631 if args.diagfile: 632 diag = open(args.diagfile, 'w', encoding='utf-8') 633 else: 634 diag = None 635 636 # Create the API generator & generator options 637 (gen, options) = genTarget(args) 638 639 # Create the registry object with the specified generator and generator 640 # options. The options are set before XML loading as they may affect it. 641 reg = Registry(gen, options) 642 643 # Parse the specified registry XML into an ElementTree object 644 startTimer(args.time) 645 tree = etree.parse(args.registry) 646 endTimer(args.time, '* Time to make ElementTree =') 647 648 # Load the XML tree into the registry object 649 startTimer(args.time) 650 reg.loadElementTree(tree) 651 endTimer(args.time, '* Time to parse ElementTree =') 652 653 if args.validate: 654 reg.validateGroups() 655 656 if args.dump: 657 logDiag('* Dumping registry to regdump.txt') 658 reg.dumpReg(filehandle=open('regdump.txt', 'w', encoding='utf-8')) 659 660 # Finally, use the output generator to create the requested target 661 if args.debug: 662 pdb.run('reg.apiGen()') 663 else: 664 startTimer(args.time) 665 reg.apiGen() 666 endTimer(args.time, '* Time to generate ' + options.filename + ' =') 667 668 if not args.quiet: 669 logDiag('* Generated', options.filename) 670