blob: dab1d496b9ac6827f4e6246188a023c6017bc8ed [file] [log] [blame]
#!/usr/bin/python3
#
# Copyright (c) 2018 The Khronos Group Inc.
# Copyright (c) 2018 Google 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 xml.etree.ElementTree as ET
import argparse
import os
import sys
import subprocess
import importlib
currentheader = ""
baseextensions = {}
supersetextensions = []
sources = [
"cgenerator.py",
"generator.py",
"reg.py",
"vk.xml"
]
def get_current_header(header):
global currentheader
if os.path.exists(header):
currentheader = open(header).read()
def get_spec_ver(n):
specver = "?"
req = n.findall("require")
if len(req) > 0:
enum = req[0].findall("enum")
if len(enum) > 0:
for y in enum:
if "_SPEC_VERSION" in y.get("name"):
specver = y.get("value")
return specver
def get_base_extensions(xmlfile):
global baseextensions
if os.path.exists(xmlfile):
missing = []
tree = ET.parse(xmlfile)
root = tree.getroot()
extroot = root.findall("extensions")
ext = []
if len(extroot) > 0 :
ext = extroot[0].getchildren()
for x in ext:
name = x.get("name")
specver = get_spec_ver(x)
if specver not in "0:":
baseextensions[name] = specver
if name not in currentheader:
if specver not in "0?":
missing.append(name)
else:
if specver is "0":
print ("!! Warning: Current header contains extension with version 0:", name)
if specver is "?":
print ("!! Warning: Current header contains extension with unknown version:", name)
if len(missing) > 0:
print ("!! Warning: current header does not include following base extension(s)")
for x in missing:
print ("!! ", x)
print ("!! These will be included in generated header.")
def parse_superset_extensions(xmlfile):
global supersetextensions
global baseextensions
if os.path.exists(xmlfile):
tree = ET.parse(xmlfile)
root = tree.getroot()
extroot = root.findall("extensions")
ext = []
if len(extroot) > 0 :
ext = extroot[0].getchildren()
for x in ext:
name = x.get("name")
specver = get_spec_ver(x)
if name in baseextensions:
if baseextensions[name] != specver:
print ("!! Warning: base and superset versions for extension", name, "differ: ", baseextensions[name], "!=", specver)
print ("!! The superset version ", specver, " will be included in generated header.")
else:
if specver not in "0?":
supersetextensions.append([name, name in currentheader, specver])
def print_menu():
global supersetextensions
index = 0
print()
for x in supersetextensions:
print (index, ") Output:", x[1],"-", x[0], "(ver " + x[2] + ")")
index += 1
print ("q ) Quit without saving")
print ("go ) Generate new header with selected superset extensions")
def generate(args):
# Dynamically import generator functions (since we downloaded the scripts)
sys.path.insert(0, os.getcwd())
importlib.invalidate_caches()
reg_py = importlib.import_module("reg")
cgenerator_py = importlib.import_module("cgenerator")
reg = reg_py.Registry()
tree = ET.parse(args.supersetxml)
reg.loadElementTree(tree)
createGenerator = cgenerator_py.COutputGenerator
# 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.',
'** DO NOT EDIT MANUALLY. Use the following script to generate this file:',
'** external/vulkancts/scripts/gen_vulkan_header.py',
'*/',
''
]
# Emit everything except the extensions specifically disabled
removeExtensionsPat = ''
for x in supersetextensions:
if not x[1]:
if removeExtensionsPat != '':
removeExtensionsPat += '|'
removeExtensionsPat += x[0]
if removeExtensionsPat != '':
removeExtensionsPat = "^(" + removeExtensionsPat + ")$"
else:
removeExtensionsPat = None
options = cgenerator_py.CGeneratorOptions(
filename = args.header,
directory = '.',
apiname = 'vulkan',
profile = None,
versions = '.*',
emitversions = '.*',
defaultExtensions = 'vulkan',
addExtensions = None,
removeExtensions = removeExtensionsPat,
emitExtensions = '.*',
prefixText = prefixStrings + vkPrefixStrings,
genFuncPointers = True,
protectFile = True,
protectFeature = False,
protectProto = '#ifndef',
protectProtoStr = 'VK_NO_PROTOTYPES',
apicall = 'VKAPI_ATTR ',
apientry = 'VKAPI_CALL ',
apientryp = 'VKAPI_PTR *',
alignFuncParam = 48)
gen = createGenerator(diagFile=None)
reg.setGenerator(gen)
reg.apiGen(options)
print("Done")
def cleanup(args):
if args.nofetch or args.nocleanup:
print("Skipping cleanup")
else:
for x in sources:
if os.path.exists(x):
os.remove(x)
def fetch_sources(args):
if not args.nofetch:
for x in sources:
if os.path.exists(x):
os.remove(x)
command = ["wget", "https://raw.github.com/KhronosGroup/Vulkan-Docs/master/xml/" + x]
if not args.noquietwget:
command.append("--quiet")
print("Fetching", x)
subprocess.call(command)
if not os.path.exists(x):
print("!! Error: Could not fetch", x)
if args.noquietwget:
print("!! Re-run with -noquietwget for diagnostic information")
quit()
else:
for x in sources:
if not os.path.exists(x):
print("!! Error: Can't find the file",x)
print("!! please re-run without -skipfetch")
quit()
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('-go', action='store_true',
default=False,
help='Enable execution.')
parser.add_argument('-noquietwget', action='store_true',
default=False,
help='Let wget output diagnostic information.')
parser.add_argument('-nofetch', action='store_true',
default=False,
help='Skip fetching required sources from github.')
parser.add_argument('-nocleanup', action='store_true',
default=False,
help='Do not remove fetched files after run.')
parser.add_argument('-basexml', action='store',
default='vk.xml',
help='Specify the base xml file with vulkan API information. Defaults to vk.xml.')
parser.add_argument('-supersetxml', action='store',
default='vk.xml',
help='Specify the xml file with superset information. Defaults to vk.xml.')
parser.add_argument('-header', action='store',
default='external/vulkancts/scripts/src/vulkan.h.in',
help='Specify the current header file. Defaults to external/vulkancts/scripts/src/invulkan.h.in.')
args = parser.parse_args()
if not args.go:
print(
"""
This script is used to generate the Vulkan header file for the Vulkan CTS from
the vk.xml specification. It can optionally take a superset XML file and the
user can interactively select which of the superset extensions to use in
generation.
The script automatically fetches the current vk.xml as well as the header
generation scripts from github.
For help with options, run with -h. To execute the script, run with -go.
""")
quit()
fetch_sources(args)
if not os.path.exists(args.basexml):
print("!! Error: Can't find base xml file", args.basexml)
quit()
if not os.path.exists(args.supersetxml):
print("!! Error: Can't find superset xml file", args.supersetxml)
quit()
if not os.path.exists(args.header):
print("!! Error: Can't find header file", args.header)
quit()
get_current_header(args.header)
get_base_extensions(args.basexml)
parse_superset_extensions(args.supersetxml)
while True:
print_menu()
i = input("Option: ")
if i != "":
if i in "qQ":
print ("Quiting without changes")
cleanup(args)
quit()
if i == "go":
print ("Generating new header")
generate(args)
cleanup(args)
quit()
if not i.isdigit() or int(i) >= len(supersetextensions):
print ("Invalid input '"+i+"'")
else:
supersetextensions[int(i)][1] = not supersetextensions[int(i)][1]