| #!/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] |