1#!/usr/bin/python3 2# 3# Copyright (c) 2013-2019 The Khronos Group Inc. 4# Copyright (c) 2021-2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 5# Copyright (c) 2023-2023 RasterGrid Kft. 6# 7# Licensed under the Apache License, Version 2.0 (the "License"); 8# you may not use this file except in compliance with the License. 9# You may obtain a copy of the License at 10# 11# http://www.apache.org/licenses/LICENSE-2.0 12# 13# Unless required by applicable law or agreed to in writing, software 14# distributed under the License is distributed on an "AS IS" BASIS, 15# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16# See the License for the specific language governing permissions and 17# limitations under the License. 18 19import argparse, cProfile, pdb, string, sys, time, os 20 21# Simple timer functions 22startTime = None 23 24def startTimer(timeit): 25 global startTime 26 if timeit: 27 startTime = time.process_time() 28 29def endTimer(timeit, msg): 30 global startTime 31 if timeit: 32 endTime = time.process_time() 33 write(msg, endTime - startTime, file=sys.stderr) 34 startTime = None 35 36# Turn a list of strings into a regexp string matching exactly those strings 37def makeREstring(list, default = None): 38 if len(list) > 0 or default is None: 39 return '^(' + '|'.join(list) + ')$' 40 else: 41 return default 42 43# Returns a directory of [ generator function, generator options ] indexed 44# by specified short names. The generator options incorporate the following 45# parameters: 46# 47# args is an parsed argument object; see below for the fields that are used. 48def makeGenOpts(args): 49 global genOpts 50 genOpts = {} 51 52 # API to generate sources for 53 apiname = args.api 54 55 # Default class of extensions to include, or None 56 if args.defaultExtensions is not None: 57 defaultExtensions = args.defaultExtensions 58 else: 59 defaultExtensions = apiname 60 61 # Additional extensions to include (list of extensions) 62 extensions = args.extension 63 64 # Extensions to remove (list of extensions) 65 removeExtensions = args.removeExtensions 66 67 # Extensions to emit (list of extensions) 68 emitExtensions = args.emitExtensions 69 70 # Features to include (list of features) 71 features = args.feature 72 73 # Whether to disable inclusion protect in headers 74 protect = args.protect 75 76 # Output target directory 77 directory = args.directory 78 79 # Path to generated files, particularly api.py 80 genpath = args.genpath 81 82 # Descriptive names for various regexp patterns used to select 83 # versions and extensions 84 allFeatures = allExtensions = '.*' 85 noFeatures = noExtensions = None 86 87 # Turn lists of names/patterns into matching regular expressions 88 addExtensionsPat = makeREstring(extensions, None) 89 removeExtensionsPat = makeREstring(removeExtensions, None) 90 emitExtensionsPat = makeREstring(emitExtensions, allExtensions) 91 featuresPat = makeREstring(features, allFeatures) 92 93 # Copyright text prefixing all headers (list of strings). 94 prefixStrings = [ 95 '/*', 96 '** Copyright (c) 2015-2019 The Khronos Group Inc.', 97 '**', 98 '** Licensed under the Apache License, Version 2.0 (the "License");', 99 '** you may not use this file except in compliance with the License.', 100 '** You may obtain a copy of the License at', 101 '**', 102 '** http://www.apache.org/licenses/LICENSE-2.0', 103 '**', 104 '** Unless required by applicable law or agreed to in writing, software', 105 '** distributed under the License is distributed on an "AS IS" BASIS,', 106 '** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.', 107 '** See the License for the specific language governing permissions and', 108 '** limitations under the License.', 109 '*/', 110 '' 111 ] 112 113 # Text specific to Vulkan headers 114 vkPrefixStrings = [ 115 '/*', 116 '** This header is generated from the Khronos Vulkan XML API Registry.', 117 '**', 118 '*/', 119 '' 120 ] 121 122 # Defaults for generating re-inclusion protection wrappers (or not) 123 protectFeature = protect 124 125 # An API style conventions object 126 conventions = VulkanConventions() 127 128 # Loader Generators 129 # Options for dispatch table helper generator 130 genOpts['vk_dispatch_table_helper.h'] = [ 131 DispatchTableHelperOutputGenerator, 132 DispatchTableHelperOutputGeneratorOptions( 133 conventions = conventions, 134 filename = 'vk_dispatch_table_helper.h', 135 directory = directory, 136 genpath = None, 137 apiname = apiname, 138 profile = None, 139 versions = featuresPat, 140 emitversions = featuresPat, 141 defaultExtensions = defaultExtensions, 142 addExtensions = addExtensionsPat, 143 removeExtensions = removeExtensionsPat, 144 emitExtensions = emitExtensionsPat, 145 prefixText = prefixStrings + vkPrefixStrings, 146 apicall = 'VKAPI_ATTR ', 147 apientry = 'VKAPI_CALL ', 148 apientryp = 'VKAPI_PTR *', 149 alignFuncParam = 48, 150 expandEnumerants = False) 151 ] 152 153 # Options for Layer dispatch table generator 154 genOpts['vk_layer_dispatch_table.h'] = [ 155 LoaderExtensionOutputGenerator, 156 LoaderExtensionGeneratorOptions( 157 conventions = conventions, 158 filename = 'vk_layer_dispatch_table.h', 159 directory = directory, 160 genpath = None, 161 apiname = apiname, 162 profile = None, 163 versions = featuresPat, 164 emitversions = featuresPat, 165 defaultExtensions = defaultExtensions, 166 addExtensions = addExtensionsPat, 167 removeExtensions = removeExtensionsPat, 168 emitExtensions = emitExtensionsPat, 169 prefixText = prefixStrings + vkPrefixStrings, 170 apicall = 'VKAPI_ATTR ', 171 apientry = 'VKAPI_CALL ', 172 apientryp = 'VKAPI_PTR *', 173 alignFuncParam = 48, 174 expandEnumerants = False) 175 ] 176 177 # Options for loader extension source generator 178 genOpts['vk_loader_extensions.h'] = [ 179 LoaderExtensionOutputGenerator, 180 LoaderExtensionGeneratorOptions( 181 conventions = conventions, 182 filename = 'vk_loader_extensions.h', 183 directory = directory, 184 genpath = None, 185 apiname = apiname, 186 profile = None, 187 versions = featuresPat, 188 emitversions = featuresPat, 189 defaultExtensions = defaultExtensions, 190 addExtensions = addExtensionsPat, 191 removeExtensions = removeExtensionsPat, 192 emitExtensions = emitExtensionsPat, 193 prefixText = prefixStrings + vkPrefixStrings, 194 apicall = 'VKAPI_ATTR ', 195 apientry = 'VKAPI_CALL ', 196 apientryp = 'VKAPI_PTR *', 197 alignFuncParam = 48, 198 expandEnumerants = False) 199 ] 200 201 # Options for loader extension source generator 202 genOpts['vk_loader_extensions.c'] = [ 203 LoaderExtensionOutputGenerator, 204 LoaderExtensionGeneratorOptions( 205 conventions = conventions, 206 filename = 'vk_loader_extensions.c', 207 directory = directory, 208 genpath = None, 209 apiname = apiname, 210 profile = None, 211 versions = featuresPat, 212 emitversions = featuresPat, 213 defaultExtensions = defaultExtensions, 214 addExtensions = addExtensionsPat, 215 removeExtensions = removeExtensionsPat, 216 emitExtensions = emitExtensionsPat, 217 prefixText = prefixStrings + vkPrefixStrings, 218 apicall = 'VKAPI_ATTR ', 219 apientry = 'VKAPI_CALL ', 220 apientryp = 'VKAPI_PTR *', 221 alignFuncParam = 48, 222 expandEnumerants = False) 223 ] 224 225 # Helper file generator options for vk_object_types.h 226 genOpts['vk_object_types.h'] = [ 227 HelperFileOutputGenerator, 228 HelperFileOutputGeneratorOptions( 229 conventions = conventions, 230 filename = 'vk_object_types.h', 231 directory = directory, 232 genpath = None, 233 apiname = apiname, 234 profile = None, 235 versions = featuresPat, 236 emitversions = featuresPat, 237 defaultExtensions = defaultExtensions, 238 addExtensions = addExtensionsPat, 239 removeExtensions = removeExtensionsPat, 240 emitExtensions = emitExtensionsPat, 241 prefixText = prefixStrings + vkPrefixStrings, 242 apicall = 'VKAPI_ATTR ', 243 apientry = 'VKAPI_CALL ', 244 apientryp = 'VKAPI_PTR *', 245 alignFuncParam = 48, 246 expandEnumerants = False, 247 helper_file_type = 'object_types_header') 248 ] 249 250# Create an API generator and corresponding generator options based on 251# the requested target and command line options. 252# This is encapsulated in a function so it can be profiled and/or timed. 253# The args parameter is an parsed argument object containing the following 254# fields that are used: 255# target - target to generate 256# directory - directory to generate it in 257# protect - True if re-inclusion wrappers should be created 258# extensions - list of additional extensions to include in generated 259# interfaces 260def genTarget(args): 261 global genOpts 262 263 # Create generator options with parameters specified on command line 264 makeGenOpts(args) 265 266 # Select a generator matching the requested target 267 if (args.target in genOpts.keys()): 268 createGenerator = genOpts[args.target][0] 269 options = genOpts[args.target][1] 270 271 if not args.quiet: 272 write('* Building', options.filename, file=sys.stderr) 273 write('* options.apiname =', options.apiname, file=sys.stderr) 274 write('* options.versions =', options.versions, file=sys.stderr) 275 write('* options.emitversions =', options.emitversions, file=sys.stderr) 276 write('* options.defaultExtensions =', options.defaultExtensions, file=sys.stderr) 277 write('* options.addExtensions =', options.addExtensions, file=sys.stderr) 278 write('* options.removeExtensions =', options.removeExtensions, file=sys.stderr) 279 write('* options.emitExtensions =', options.emitExtensions, file=sys.stderr) 280 281 gen = createGenerator(errFile=errWarn, 282 warnFile=errWarn, 283 diagFile=diag) 284 if not args.quiet: 285 write('* Generated', options.filename, file=sys.stderr) 286 return (gen, options) 287 else: 288 write('No generator options for unknown target:', args.target, file=sys.stderr) 289 return none 290 291# -feature name 292# -extension name 293# For both, "name" may be a single name, or a space-separated list 294# of names, or a regular expression. 295if __name__ == '__main__': 296 parser = argparse.ArgumentParser() 297 298 parser.add_argument('-api', action='store', 299 default='vulkan', 300 choices=['vulkan'], 301 help='Specify API name to generate') 302 parser.add_argument('-defaultExtensions', action='store', 303 default=None, 304 help='Specify a single class of extensions to add to targets') 305 parser.add_argument('-extension', action='append', 306 default=[], 307 help='Specify an extension or extensions to add to targets') 308 parser.add_argument('-removeExtensions', action='append', 309 default=[], 310 help='Specify an extension or extensions to remove from targets') 311 parser.add_argument('-emitExtensions', action='append', 312 default=[], 313 help='Specify an extension or extensions to emit in targets') 314 parser.add_argument('-feature', action='append', 315 default=[], 316 help='Specify a core API feature name or names to add to targets') 317 parser.add_argument('-debug', action='store_true', 318 help='Enable debugging') 319 parser.add_argument('-dump', action='store_true', 320 help='Enable dump to stderr') 321 parser.add_argument('-diagfile', action='store', 322 default=None, 323 help='Write diagnostics to specified file') 324 parser.add_argument('-errfile', action='store', 325 default=None, 326 help='Write errors and warnings to specified file instead of stderr') 327 parser.add_argument('-noprotect', dest='protect', action='store_false', 328 help='Disable inclusion protection in output headers') 329 parser.add_argument('-profile', action='store_true', 330 help='Enable profiling') 331 parser.add_argument('-registry', action='store', 332 default='vk.xml', 333 help='Use specified registry file instead of vk.xml') 334 parser.add_argument('-time', action='store_true', 335 help='Enable timing') 336 parser.add_argument('-validate', action='store_true', 337 help='Enable XML group validation') 338 parser.add_argument('-genpath', action='store', default='gen', 339 help='Path to generated files') 340 parser.add_argument('-o', action='store', dest='directory', 341 default='.', 342 help='Create target and related files in specified directory') 343 parser.add_argument('target', metavar='target', nargs='?', 344 help='Specify target') 345 parser.add_argument('-quiet', action='store_true', default=True, 346 help='Suppress script output during normal execution.') 347 parser.add_argument('-verbose', action='store_false', dest='quiet', default=True, 348 help='Enable script output during normal execution.') 349 350 # This argument tells us where to load the script from the Vulkan-Headers registry 351 parser.add_argument('-scripts', action='store', 352 help='Find additional scripts in this directory') 353 354 args = parser.parse_args() 355 356 # default scripts path to be same as registry 357 if not args.scripts: 358 args.scripts = os.path.dirname(args.registry) 359 print(args.scripts) 360 361 scripts_dir = os.path.dirname(os.path.abspath(__file__)) 362 registry_dir = os.path.join(scripts_dir, args.scripts) 363 sys.path.insert(0, registry_dir) 364 365 # The imports need to be done here so that they can be picked up from Vulkan-Headers 366 from reg import * 367 from generator import write 368 from cgenerator import CGeneratorOptions, COutputGenerator 369 370 from dispatch_table_helper_generator import DispatchTableHelperOutputGenerator, DispatchTableHelperOutputGeneratorOptions 371 from helper_file_generator import HelperFileOutputGenerator, HelperFileOutputGeneratorOptions 372 from loader_extension_generator import LoaderExtensionOutputGenerator, LoaderExtensionGeneratorOptions 373 374 # Temporary workaround for vkconventions python2 compatibility 375 import abc; abc.ABC = abc.ABCMeta('ABC', (object,), {}) 376 from vkconventions import VulkanConventions 377 378 # This splits arguments which are space-separated lists 379 args.feature = [name for arg in args.feature for name in arg.split()] 380 args.extension = [name for arg in args.extension for name in arg.split()] 381 382 # create error/warning & diagnostic files 383 if args.errfile: 384 errWarn = open(args.errfile, 'w', encoding='utf-8') 385 else: 386 errWarn = sys.stderr 387 388 if args.diagfile: 389 diag = open(args.diagfile, 'w', encoding='utf-8') 390 else: 391 diag = None 392 393 # Create the API generator & generator options 394 (gen, options) = genTarget(args) 395 396 # Create the registry object with the specified generator and generator 397 # options. The options are set before XML loading as they may affect it. 398 reg = Registry(gen, options) 399 400 # Parse the specified registry XML into an ElementTree objec 401 startTimer(args.time) 402 tree = etree.parse(args.registry) 403 endTimer(args.time, '* Time to make ElementTree =') 404 405 # Load the XML tree into the registry object 406 startTimer(args.time) 407 reg.loadElementTree(tree) 408 endTimer(args.time, '* Time to parse ElementTree =') 409 410 if (args.validate): 411 reg.validateGroups() 412 413 if (args.dump): 414 write('* Dumping registry to regdump.txt', file=sys.stderr) 415 reg.dumpReg(filehandle = open('regdump.txt', 'w', encoding='utf-8')) 416 417 # Finally, use the output generator to create the requested targe 418 if (args.debug): 419 pdb.run('reg.apiGen()') 420 else: 421 startTimer(args.time) 422 reg.apiGen() 423 endTimer(args.time, '* Time to generate ' + options.filename + ' =') 424 425 if not args.quiet: 426 write('* Generated', options.filename, file=sys.stderr) 427