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