1#!/usr/bin/python3 2# 3# Copyright 2013-2023 The Khronos Group Inc. 4# Copyright 2023-2024 Google Inc. 5# 6# SPDX-License-Identifier: Apache-2.0 7 8import argparse 9import os 10import re 11import sys 12import xml.etree.ElementTree as etree 13 14sys.path.append(os.path.abspath(os.path.dirname(__file__))) 15 16from cgenerator import CGeneratorOptions, COutputGenerator 17 18from generator import write 19from reg import Registry 20 21# gfxstream + cereal modules 22from cerealgenerator import CerealGenerator 23 24from typing import Optional 25 26def makeREstring(strings, default=None, strings_are_regex=False): 27 """Turn a list of strings into a regexp string matching exactly those strings.""" 28 if strings or default is None: 29 if not strings_are_regex: 30 strings = (re.escape(s) for s in strings) 31 return '^(' + '|'.join(strings) + ')$' 32 return default 33 34 35def makeGenOpts(args): 36 """Returns a directory of [ generator function, generator options ] indexed 37 by specified short names. The generator options incorporate the following 38 parameters: 39 40 args is an parsed argument object; see below for the fields that are used.""" 41 global genOpts 42 genOpts = {} 43 44 # Output target directory 45 directory = args.directory 46 47 # Descriptive names for various regexp patterns used to select 48 # versions and extensions 49 allFormats = allFeatures = allExtensions = r'.*' 50 51 # Turn lists of names/patterns into matching regular expressions 52 emitExtensionsPat = makeREstring([], allExtensions) 53 emitFormatsPat = makeREstring([], allFormats) 54 featuresPat = makeREstring([], allFeatures) 55 56 # Copyright text prefixing all headers (list of strings). 57 # The SPDX formatting below works around constraints of the 'reuse' tool 58 prefixStrings = [ 59 '/*', 60 '** Copyright 2015-2023 The Khronos Group Inc.', 61 '**', 62 '** SPDX-License-Identifier' + ': Apache-2.0', 63 '*/', 64 '' 65 ] 66 67 # Text specific to Vulkan headers 68 vkPrefixStrings = [ 69 '/*', 70 '** This header is generated from the Khronos Vulkan XML API Registry.', 71 '**', 72 '*/', 73 '' 74 ] 75 76 genOpts['cereal'] = [ 77 CerealGenerator, 78 CGeneratorOptions( 79 directory = directory, 80 versions = featuresPat, 81 emitversions = featuresPat, 82 addExtensions = None, 83 emitExtensions = emitExtensionsPat, 84 prefixText = prefixStrings + vkPrefixStrings, 85 apientry = 'VKAPI_CALL ', 86 apientryp = 'VKAPI_PTR *', 87 alignFuncParam = 48) 88 ] 89 90 gfxstreamPrefixStrings = [ 91 '#pragma once', 92 '#ifdef VK_GFXSTREAM_STRUCTURE_TYPE_EXT', 93 '#include "vulkan_gfxstream_structure_type.h"', 94 '#endif', 95 ] 96 97 # gfxstream specific header 98 genOpts['vulkan_gfxstream.h'] = [ 99 COutputGenerator, 100 CGeneratorOptions( 101 filename = 'vulkan_gfxstream.h', 102 directory = directory, 103 versions = featuresPat, 104 emitversions = None, 105 addExtensions = makeREstring(['VK_GOOGLE_gfxstream'], None), 106 emitExtensions = makeREstring(['VK_GOOGLE_gfxstream'], None), 107 prefixText = prefixStrings + vkPrefixStrings + gfxstreamPrefixStrings, 108 # Use #pragma once in the prefixText instead, so that we can put the copyright comments 109 # at the beginning of the file. 110 apientry = 'VKAPI_CALL ', 111 apientryp = 'VKAPI_PTR *', 112 alignFuncParam = 48) 113 ] 114 115def genTarget(args): 116 """Create an API generator and corresponding generator options based on 117 the requested target and command line options. 118 119 This is encapsulated in a function so it can be profiled and/or timed. 120 The args parameter is an parsed argument object containing the following 121 fields that are used: 122 123 - target - target to generate 124 - directory - directory to generate it in 125 - extensions - list of additional extensions to include in generated interfaces""" 126 127 # Create generator options with parameters specified on command line 128 makeGenOpts(args) 129 130 # Select a generator matching the requested target 131 if args.target in genOpts: 132 createGenerator = genOpts[args.target][0] 133 options = genOpts[args.target][1] 134 135 gen = createGenerator(errFile=errWarn, 136 warnFile=errWarn, 137 diagFile=diag) 138 return (gen, options) 139 else: 140 return None 141 142 143# -feature name 144# -extension name 145# For both, "name" may be a single name, or a space-separated list 146# of names, or a regular expression. 147if __name__ == '__main__': 148 parser = argparse.ArgumentParser() 149 parser.add_argument('-registry', action='store', 150 default='vk.xml', 151 help='Use specified registry file instead of vk.xml') 152 parser.add_argument('-registryGfxstream', action='store', 153 default=None, 154 help='Use specified gfxstream registry file') 155 parser.add_argument('-o', action='store', dest='directory', 156 default='.', 157 help='Create target and related files in specified directory') 158 parser.add_argument('target', metavar='target', nargs='?', 159 help='Specify target') 160 161 args = parser.parse_args() 162 163 errWarn = sys.stderr 164 diag = None 165 166 # Create the API generator & generator options 167 (gen, options) = genTarget(args) 168 169 # Create the registry object with the specified generator and generator 170 # options. The options are set before XML loading as they may affect it. 171 reg = Registry(gen, options) 172 173 # Parse the specified registry XML into an ElementTree object 174 tree = etree.parse(args.registry) 175 176 # Merge the gfxstream registry with the official Vulkan registry if the 177 # target is the cereal generator 178 if args.registryGfxstream is not None and args.target == 'cereal': 179 treeGfxstream = etree.parse(args.registryGfxstream) 180 treeRoot = tree.getroot() 181 treeGfxstreamRoot = treeGfxstream.getroot() 182 183 def getEntryName(entry) -> Optional[str]: 184 name = entry.get("name") 185 if name is not None: 186 return name 187 try: 188 return entry.find("proto").find("name").text 189 except AttributeError: 190 return None 191 192 for entriesName in ['types', 'commands', 'extensions']: 193 treeEntries = treeRoot.find(entriesName) 194 195 originalEntryDict = {} 196 for entry in treeEntries: 197 name = getEntryName(entry) 198 if name is not None: 199 originalEntryDict[name] = entry 200 201 for entry in treeGfxstreamRoot.find(entriesName): 202 name = getEntryName(entry) 203 # New entry, just append to entry list 204 if name not in originalEntryDict.keys(): 205 treeEntries.append(entry) 206 continue 207 208 originalEntry = originalEntryDict[name] 209 210 # Extending an existing entry. This happens for MVK. 211 if entriesName == "extensions": 212 for key, value in entry.attrib.items(): 213 originalEntry.set(key, value) 214 require = entry.find("require") 215 if require is not None: 216 for child in require: 217 originalEntry.find("require").append(child) 218 continue 219 220 # Overwriting an existing entry. This happen for 221 # VkNativeBufferANDROID 222 if entriesName == "types" or entriesName == "commands": 223 originalEntry.clear() 224 originalEntry.attrib = entry.attrib 225 for child in entry: 226 originalEntry.append(child) 227 228 # Load the XML tree into the registry object 229 reg.loadElementTree(tree) 230 reg.apiGen() 231