blob: b8b24c9aeb6d1d192db96148d6ff8dd75624fd61 [file] [log] [blame]
#!/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()