#!/usr/bin/python3 # # Copyright 2013-2023 The Khronos Group Inc. # Copyright 2023-2024 Google Inc. # # SPDX-License-Identifier: Apache-2.0 import argparse import os import re import sys import xml.etree.ElementTree as etree sys.path.append(os.path.abspath(os.path.dirname(__file__))) from cgenerator import CGeneratorOptions, COutputGenerator from generator import write from reg import Registry # gfxstream + cereal modules from cerealgenerator import CerealGenerator from typing import Optional def makeREstring(strings, default=None, strings_are_regex=False): """Turn a list of strings into a regexp string matching exactly those strings.""" if strings or default is None: if not strings_are_regex: strings = (re.escape(s) for s in strings) return '^(' + '|'.join(strings) + ')$' return default def makeGenOpts(args): """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.""" global genOpts genOpts = {} # Output target directory directory = args.directory # Descriptive names for various regexp patterns used to select # versions and extensions allFormats = allFeatures = allExtensions = r'.*' # Turn lists of names/patterns into matching regular expressions emitExtensionsPat = makeREstring([], allExtensions) emitFormatsPat = makeREstring([], allFormats) featuresPat = makeREstring([], allFeatures) # Copyright text prefixing all headers (list of strings). # The SPDX formatting below works around constraints of the 'reuse' tool prefixStrings = [ '/*', '** Copyright 2015-2023 The Khronos Group Inc.', '**', '** SPDX-License-Identifier' + ': Apache-2.0', '*/', '' ] # Text specific to Vulkan headers vkPrefixStrings = [ '/*', '** This header is generated from the Khronos Vulkan XML API Registry.', '**', '*/', '' ] genOpts['cereal'] = [ CerealGenerator, CGeneratorOptions( directory = directory, versions = featuresPat, emitversions = featuresPat, addExtensions = None, emitExtensions = emitExtensionsPat, prefixText = prefixStrings + vkPrefixStrings, apientry = 'VKAPI_CALL ', apientryp = 'VKAPI_PTR *', alignFuncParam = 48) ] gfxstreamPrefixStrings = [ '#pragma once', '#ifdef VK_GFXSTREAM_STRUCTURE_TYPE_EXT', '#include "vulkan_gfxstream_structure_type.h"', '#endif', ] # gfxstream specific header genOpts['vulkan_gfxstream.h'] = [ COutputGenerator, CGeneratorOptions( filename = 'vulkan_gfxstream.h', directory = directory, versions = featuresPat, emitversions = None, addExtensions = makeREstring(['VK_GOOGLE_gfxstream'], None), emitExtensions = makeREstring(['VK_GOOGLE_gfxstream'], None), prefixText = prefixStrings + vkPrefixStrings + gfxstreamPrefixStrings, # Use #pragma once in the prefixText instead, so that we can put the copyright comments # at the beginning of the file. apientry = 'VKAPI_CALL ', apientryp = 'VKAPI_PTR *', alignFuncParam = 48) ] def genTarget(args): """Create an API generator and corresponding generator options based on the requested target and command line options. 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 - extensions - list of additional extensions to include in generated interfaces""" # Create generator options with parameters specified on command line makeGenOpts(args) # Select a generator matching the requested target if args.target in genOpts: createGenerator = genOpts[args.target][0] options = genOpts[args.target][1] gen = createGenerator(errFile=errWarn, warnFile=errWarn, diagFile=diag) return (gen, options) else: 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('-registry', action='store', default='vk.xml', help='Use specified registry file instead of vk.xml') parser.add_argument('-registryGfxstream', action='store', default=None, help='Use specified gfxstream registry file') 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') args = parser.parse_args() errWarn = sys.stderr 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 tree = etree.parse(args.registry) # Merge the gfxstream registry with the official Vulkan registry if the # target is the cereal generator if args.registryGfxstream is not None and args.target == 'cereal': treeGfxstream = etree.parse(args.registryGfxstream) treeRoot = tree.getroot() treeGfxstreamRoot = treeGfxstream.getroot() def getEntryName(entry) -> Optional[str]: name = entry.get("name") if name is not None: return name try: return entry.find("proto").find("name").text except AttributeError: return None for entriesName in ['types', 'commands', 'extensions']: treeEntries = treeRoot.find(entriesName) originalEntryDict = {} for entry in treeEntries: name = getEntryName(entry) if name is not None: originalEntryDict[name] = entry for entry in treeGfxstreamRoot.find(entriesName): name = getEntryName(entry) # New entry, just append to entry list if name not in originalEntryDict.keys(): treeEntries.append(entry) continue originalEntry = originalEntryDict[name] # Extending an existing entry. This happens for MVK. if entriesName == "extensions": for key, value in entry.attrib.items(): originalEntry.set(key, value) require = entry.find("require") if require is not None: for child in require: originalEntry.find("require").append(child) continue # Overwriting an existing entry. This happen for # VkNativeBufferANDROID if entriesName == "types" or entriesName == "commands": originalEntry.clear() originalEntry.attrib = entry.attrib for child in entry: originalEntry.append(child) # Load the XML tree into the registry object reg.loadElementTree(tree) reg.apiGen()