#!/usr/bin/python3 # # Copyright (c) 2013-2019 The Khronos Group Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import argparse import cProfile import pdb import string import sys import time import os # Simple timer functions startTime = None def startTimer(timeit): global startTime if timeit: startTime = time.process_time() def endTimer(timeit, msg): global startTime if timeit: endTime = time.process_time() write(msg, endTime - startTime, file=sys.stderr) startTime = None # Turn a list of strings into a regexp string matching exactly those strings def makeREstring(list, default=None): if len(list) > 0 or default is None: return '^(' + '|'.join(list) + ')$' else: return default # Returns a directory of [ generator function, generator options ] indexed # by specified short names. The generator options incorporate the following # parameters: # # args is an parsed argument object; see below for the fields that are used. def makeGenOpts(args): global genOpts genOpts = {} # Default class of extensions to include, or None defaultExtensions = args.defaultExtensions # Additional extensions to include (list of extensions) extensions = args.extension # Extensions to remove (list of extensions) removeExtensions = args.removeExtensions # Extensions to emit (list of extensions) emitExtensions = args.emitExtensions # Features to include (list of features) features = args.feature # Whether to disable inclusion protect in headers protect = args.protect # Output target directory directory = args.directory # Descriptive names for various regexp patterns used to select # versions and extensions allFeatures = allExtensions = '.*' noFeatures = noExtensions = None # Turn lists of names/patterns into matching regular expressions addExtensionsPat = makeREstring(extensions, None) removeExtensionsPat = makeREstring(removeExtensions, None) emitExtensionsPat = makeREstring(emitExtensions, allExtensions) featuresPat = makeREstring(features, allFeatures) # Copyright text prefixing all headers (list of strings). prefixStrings = [ '/*', '** Copyright (c) 2015-2018 The Khronos Group Inc.', '**', '** Licensed under the Apache License, Version 2.0 (the "License");', '** you may not use this file except in compliance with the License.', '** You may obtain a copy of the License at', '**', '** http://www.apache.org/licenses/LICENSE-2.0', '**', '** Unless required by applicable law or agreed to in writing, software', '** distributed under the License is distributed on an "AS IS" BASIS,', '** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.', '** See the License for the specific language governing permissions and', '** limitations under the License.', '*/', '' ] # Text specific to Vulkan headers vkPrefixStrings = [ '/*', '** This header is generated from the Khronos Vulkan XML API Registry.', '**', '*/', '' ] # Defaults for generating re-inclusion protection wrappers (or not) protectFeature = protect # An API style conventions object conventions = VulkanConventions() # Helper file generator options for typemap_helper.h genOpts['vk_typemap_helper.h'] = [ HelperFileOutputGenerator, HelperFileOutputGeneratorOptions( conventions=conventions, filename='vk_typemap_helper.h', directory=directory, apiname='vulkan', profile=None, versions=featuresPat, emitversions=featuresPat, defaultExtensions='vulkan', addExtensions=addExtensionsPat, removeExtensions=removeExtensionsPat, emitExtensions=emitExtensionsPat, prefixText=prefixStrings + vkPrefixStrings, protectFeature=False, apicall='VKAPI_ATTR ', apientry='VKAPI_CALL ', apientryp='VKAPI_PTR *', alignFuncParam=48, expandEnumerants=False, helper_file_type='typemap_helper_header') ] # Options for mock ICD header genOpts['mock_icd.h'] = [ MockICDOutputGenerator, MockICDGeneratorOptions( conventions=conventions, filename='mock_icd.h', directory=directory, apiname='vulkan', profile=None, versions=featuresPat, emitversions=featuresPat, defaultExtensions='vulkan', addExtensions=addExtensionsPat, removeExtensions=removeExtensionsPat, emitExtensions=emitExtensionsPat, prefixText=prefixStrings + vkPrefixStrings, protectFeature=False, apicall='VKAPI_ATTR ', apientry='VKAPI_CALL ', apientryp='VKAPI_PTR *', alignFuncParam=48, expandEnumerants=False, helper_file_type='mock_icd_header') ] # Options for mock ICD cpp genOpts['mock_icd.cpp'] = [ MockICDOutputGenerator, MockICDGeneratorOptions( conventions=conventions, filename='mock_icd.cpp', directory=directory, apiname='vulkan', profile=None, versions=featuresPat, emitversions=featuresPat, defaultExtensions='vulkan', addExtensions=addExtensionsPat, removeExtensions=removeExtensionsPat, emitExtensions=emitExtensionsPat, prefixText=prefixStrings + vkPrefixStrings, protectFeature=False, apicall='VKAPI_ATTR ', apientry='VKAPI_CALL ', apientryp='VKAPI_PTR *', alignFuncParam=48, expandEnumerants=False, helper_file_type='mock_icd_source') ] # Options for vulkaninfo.hpp genOpts['vulkaninfo.hpp'] = [ VulkanInfoGenerator, VulkanInfoGeneratorOptions( conventions=conventions, filename='vulkaninfo.hpp', directory=directory, apiname='vulkan', profile=None, versions=featuresPat, emitversions=featuresPat, defaultExtensions='vulkan', addExtensions=addExtensionsPat, removeExtensions=removeExtensionsPat, emitExtensions=emitExtensionsPat, prefixText=prefixStrings + vkPrefixStrings, protectFeature=False, apicall='VKAPI_ATTR ', apientry='VKAPI_CALL ', apientryp='VKAPI_PTR *', alignFuncParam=48, expandEnumerants=False) ] # Generate a target based on the options in the matching genOpts{} object. # This is encapsulated in a function so it can be profiled and/or timed. # The args parameter is an parsed argument object containing the following # fields that are used: # target - target to generate # directory - directory to generate it in # protect - True if re-inclusion wrappers should be created # extensions - list of additional extensions to include in generated # interfaces def genTarget(args): global genOpts # Create generator options with specified parameters makeGenOpts(args) if (args.target in genOpts.keys()): createGenerator = genOpts[args.target][0] options = genOpts[args.target][1] if not args.quiet: write('* Building', options.filename, file=sys.stderr) write('* options.versions =', options.versions, file=sys.stderr) write('* options.emitversions =', options.emitversions, file=sys.stderr) write('* options.defaultExtensions =', options.defaultExtensions, file=sys.stderr) write('* options.addExtensions =', options.addExtensions, file=sys.stderr) write('* options.removeExtensions =', options.removeExtensions, file=sys.stderr) write('* options.emitExtensions =', options.emitExtensions, file=sys.stderr) gen = createGenerator(errFile=errWarn, warnFile=errWarn, diagFile=diag) if not args.quiet: write('* Generated', options.filename, file=sys.stderr) return (gen, options) else: write('No generator options for unknown target:', args.target, file=sys.stderr) return none # -feature name # -extension name # For both, "name" may be a single name, or a space-separated list # of names, or a regular expression. if __name__ == '__main__': parser = argparse.ArgumentParser() parser.add_argument('-defaultExtensions', action='store', default='vulkan', help='Specify a single class of extensions to add to targets') parser.add_argument('-extension', action='append', default=[], help='Specify an extension or extensions to add to targets') parser.add_argument('-removeExtensions', action='append', default=[], help='Specify an extension or extensions to remove from targets') parser.add_argument('-emitExtensions', action='append', default=[], help='Specify an extension or extensions to emit in targets') parser.add_argument('-feature', action='append', default=[], help='Specify a core API feature name or names to add to targets') parser.add_argument('-debug', action='store_true', help='Enable debugging') parser.add_argument('-dump', action='store_true', help='Enable dump to stderr') parser.add_argument('-diagfile', action='store', default=None, help='Write diagnostics to specified file') parser.add_argument('-errfile', action='store', default=None, help='Write errors and warnings to specified file instead of stderr') parser.add_argument('-noprotect', dest='protect', action='store_false', help='Disable inclusion protection in output headers') parser.add_argument('-profile', action='store_true', help='Enable profiling') parser.add_argument('-registry', action='store', default='vk.xml', help='Use specified registry file instead of vk.xml') parser.add_argument('-time', action='store_true', help='Enable timing') parser.add_argument('-validate', action='store_true', help='Enable group validation') parser.add_argument('-o', action='store', dest='directory', default='.', help='Create target and related files in specified directory') parser.add_argument('target', metavar='target', nargs='?', help='Specify target') parser.add_argument('-quiet', action='store_true', default=True, help='Suppress script output during normal execution.') parser.add_argument('-verbose', action='store_false', dest='quiet', default=True, help='Enable script output during normal execution.') # This argument tells us where to load the script from the Vulkan-Headers registry parser.add_argument('-scripts', action='store', help='Find additional scripts in this directory') args = parser.parse_args() # default scripts path to be same as registry if not args.scripts: args.scripts = os.path.dirname(args.registry) scripts_directory_path = os.path.dirname(os.path.abspath(__file__)) registry_headers_path = os.path.join(scripts_directory_path, args.scripts) sys.path.insert(0, registry_headers_path) from reg import * from generator import write from cgenerator import CGeneratorOptions, COutputGenerator # Generator Modifications from mock_icd_generator import MockICDGeneratorOptions, MockICDOutputGenerator from vulkan_tools_helper_file_generator import HelperFileOutputGenerator, HelperFileOutputGeneratorOptions from vulkaninfo_generator import VulkanInfoGenerator, VulkanInfoGeneratorOptions # Temporary workaround for vkconventions python2 compatibility import abc abc.ABC = abc.ABCMeta('ABC', (object,), {}) from vkconventions import VulkanConventions # This splits arguments which are space-separated lists args.feature = [name for arg in args.feature for name in arg.split()] args.extension = [name for arg in args.extension for name in arg.split()] # create error/warning & diagnostic files if (args.errfile): errWarn = open(args.errfile, 'w', encoding='utf-8') else: errWarn = sys.stderr if (args.diagfile): diag = open(args.diagfile, 'w', encoding='utf-8') else: diag = None # Create the API generator & generator options (gen, options) = genTarget(args) # Create the registry object with the specified generator and generator # options. The options are set before XML loading as they may affect it. reg = Registry(gen, options) # Parse the specified registry XML into an ElementTree object startTimer(args.time) tree = etree.parse(args.registry) endTimer(args.time, '* Time to make ElementTree =') # Load the XML tree into the registry object startTimer(args.time) reg.loadElementTree(tree) endTimer(args.time, '* Time to parse ElementTree =') if (args.validate): reg.validateGroups() if (args.dump): write('* Dumping registry to regdump.txt', file=sys.stderr) reg.dumpReg(filehandle = open('regdump.txt', 'w', encoding='utf-8')) # Finally, use the output generator to create the requested target if (args.debug): pdb.run('reg.apiGen()') else: startTimer(args.time) reg.apiGen() endTimer(args.time, '* Time to generate ' + options.filename + ' =') genTarget(args)