Added script for vulkan.h.in generation
authorJari Komppa <jari.komppa@siru.fi>
Tue, 2 Oct 2018 13:02:59 +0000 (16:02 +0300)
committerAlexander Galazin <Alexander.Galazin@arm.com>
Tue, 4 Dec 2018 08:28:52 +0000 (03:28 -0500)
In order to avoid manual editing of vulkan.h.in, this script can be used
to perform all the operations that usually need manual editing, but
instead performs the operations from source XML files instead.

The script can read a base XML file, a superset XML file and the current
header file. These three files are analysed for the extensions they
contain, and the user can interactively select which extensions from the
superset to include in the generated header. (All extensions from the
base XML are included).

The script also automatically fetches the current vk.xml and header
genration scripts from github using wget.

This script is not automatically run by the build process, and requires
manual interaction to run. It depends on python3, since it uses the
header generation scripts that are part of the vk-docs project. Only the
generated header files should be committed to the git repository.

Affects:

None

Components: Framework

VK-GL-CTS issue: 1322

Change-Id: I26557d672483325924dc32febadd283669a4bc82

external/vulkancts/scripts/gen_vulkan_header.py [new file with mode: 0644]

diff --git a/external/vulkancts/scripts/gen_vulkan_header.py b/external/vulkancts/scripts/gen_vulkan_header.py
new file mode 100644 (file)
index 0000000..bb5ea97
--- /dev/null
@@ -0,0 +1,295 @@
+#!/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]