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