• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/python3
2#
3# Copyright (c) 2018 The Khronos Group Inc.
4# Copyright (c) 2018 Google Inc.
5#
6# Licensed under the Apache License, Version 2.0 (the "License");
7# you may not use this file except in compliance with the License.
8# You may obtain a copy of the License at
9#
10#     http://www.apache.org/licenses/LICENSE-2.0
11#
12# Unless required by applicable law or agreed to in writing, software
13# distributed under the License is distributed on an "AS IS" BASIS,
14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15# See the License for the specific language governing permissions and
16# limitations under the License.
17
18import xml.etree.ElementTree as ET
19import argparse
20import os
21import sys
22import subprocess
23import importlib
24
25currentheader = ""
26baseextensions = {}
27supersetextensions = []
28sources = [
29	"cgenerator.py",
30	"generator.py",
31	"reg.py",
32	"vk.xml"
33	]
34
35def get_current_header(header):
36	global currentheader
37	if os.path.exists(header):
38		currentheader = open(header).read()
39
40def get_spec_ver(n):
41	specver = "?"
42	req = n.findall("require")
43	if len(req) > 0:
44		enum = req[0].findall("enum")
45		if len(enum) > 0:
46			for y in enum:
47				if "_SPEC_VERSION" in y.get("name"):
48					specver = y.get("value")
49	return specver
50
51def get_base_extensions(xmlfile):
52	global baseextensions
53	if os.path.exists(xmlfile):
54		missing = []
55		tree = ET.parse(xmlfile)
56		root = tree.getroot()
57		extroot = root.findall("extensions")
58		ext = []
59		if len(extroot) > 0 :
60			ext = extroot[0].getchildren()
61		for x in ext:
62			name = x.get("name")
63			specver = get_spec_ver(x)
64			if specver not in "0:":
65				baseextensions[name] = specver
66
67			if name not in currentheader:
68				if specver not in "0?":
69					missing.append(name)
70			else:
71				if specver is "0":
72					print ("!! Warning: Current header contains extension with version 0:", name)
73				if specver is "?":
74					print ("!! Warning: Current header contains extension with unknown version:", name)
75		if len(missing) > 0:
76			print ("!! Warning: current header does not include following base extension(s)")
77			for x in missing:
78				print ("!!  ", x)
79			print ("!! These will be included in generated header.")
80
81def parse_superset_extensions(xmlfile):
82	global supersetextensions
83	global baseextensions
84	if os.path.exists(xmlfile):
85		tree = ET.parse(xmlfile)
86		root = tree.getroot()
87		extroot = root.findall("extensions")
88		ext = []
89		if len(extroot) > 0 :
90			ext = extroot[0].getchildren()
91		for x in ext:
92			name = x.get("name")
93			specver = get_spec_ver(x)
94			if name in baseextensions:
95				if baseextensions[name] != specver:
96					print ("!! Warning: base and superset versions for extension", name, "differ: ", baseextensions[name], "!=", specver)
97					print ("!! The superset version ", specver, " will be included in generated header.")
98			else:
99				if specver not in "0?":
100					supersetextensions.append([name, name in currentheader, specver])
101
102def print_menu():
103	global supersetextensions
104	index = 0
105	print()
106	for x in supersetextensions:
107		print (index, ") Output:", x[1],"-", x[0], "(ver " + x[2] + ")")
108		index += 1
109	print ("q ) Quit without saving")
110	print ("go ) Generate new header with selected superset extensions")
111
112
113def generate(args):
114	# Dynamically import generator functions (since we downloaded the scripts)
115	sys.path.insert(0, os.getcwd())
116	importlib.invalidate_caches()
117	reg_py = importlib.import_module("reg")
118	cgenerator_py = importlib.import_module("cgenerator")
119
120	reg = reg_py.Registry()
121	tree = ET.parse(args.supersetxml)
122	reg.loadElementTree(tree)
123	createGenerator = cgenerator_py.COutputGenerator
124
125	# Copyright text prefixing all headers (list of strings).
126	prefixStrings = [
127		'/*',
128		'** Copyright (c) 2015-2018 The Khronos Group Inc.',
129		'**',
130		'** Licensed under the Apache License, Version 2.0 (the "License");',
131		'** you may not use this file except in compliance with the License.',
132		'** You may obtain a copy of the License at',
133		'**',
134		'**     http://www.apache.org/licenses/LICENSE-2.0',
135		'**',
136		'** Unless required by applicable law or agreed to in writing, software',
137		'** distributed under the License is distributed on an "AS IS" BASIS,',
138		'** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.',
139		'** See the License for the specific language governing permissions and',
140		'** limitations under the License.',
141		'*/',
142		''
143	]
144
145	# Text specific to Vulkan headers
146	vkPrefixStrings = [
147		'/*',
148		'** This header is generated from the Khronos Vulkan XML API Registry.',
149		'** DO NOT EDIT MANUALLY. Use the following script to generate this file:',
150		'** external/vulkancts/scripts/gen_vulkan_header.py',
151		'*/',
152		''
153	]
154
155	# Emit everything except the extensions specifically disabled
156	removeExtensionsPat = ''
157	for x in supersetextensions:
158		if not x[1]:
159			if removeExtensionsPat != '':
160				removeExtensionsPat += '|'
161			removeExtensionsPat += x[0]
162	if removeExtensionsPat != '':
163		removeExtensionsPat = "^(" + removeExtensionsPat + ")$"
164	else:
165		removeExtensionsPat = None
166
167	options = cgenerator_py.CGeneratorOptions(
168		filename          = args.header,
169		directory         = '.',
170		apiname           = 'vulkan',
171		profile           = None,
172		versions          = '.*',
173		emitversions      = '.*',
174		defaultExtensions = 'vulkan',
175		addExtensions     = None,
176		removeExtensions  = removeExtensionsPat,
177		emitExtensions    = '.*',
178		prefixText        = prefixStrings + vkPrefixStrings,
179		genFuncPointers   = True,
180		protectFile       = True,
181		protectFeature    = False,
182		protectProto      = '#ifndef',
183		protectProtoStr   = 'VK_NO_PROTOTYPES',
184		apicall           = 'VKAPI_ATTR ',
185		apientry          = 'VKAPI_CALL ',
186		apientryp         = 'VKAPI_PTR *',
187		alignFuncParam    = 48)
188	gen = createGenerator(diagFile=None)
189	reg.setGenerator(gen)
190	reg.apiGen(options)
191	print("Done")
192
193def cleanup(args):
194	if args.nofetch or args.nocleanup:
195		print("Skipping cleanup")
196	else:
197		for x in sources:
198			if os.path.exists(x):
199				os.remove(x)
200
201def fetch_sources(args):
202	if not args.nofetch:
203		for x in sources:
204			if os.path.exists(x):
205				os.remove(x)
206			command = ["wget", "https://raw.github.com/KhronosGroup/Vulkan-Docs/master/xml/" + x]
207			if not args.noquietwget:
208				command.append("--quiet")
209			print("Fetching", x)
210			subprocess.call(command)
211			if not os.path.exists(x):
212				print("!! Error: Could not fetch", x)
213				if args.noquietwget:
214					print("!! Re-run with -noquietwget for diagnostic information")
215				quit()
216	else:
217		for x in sources:
218			if not os.path.exists(x):
219				print("!! Error: Can't find the file",x)
220				print("!! please re-run without -skipfetch")
221				quit()
222
223if __name__ == '__main__':
224	parser = argparse.ArgumentParser()
225	parser.add_argument('-go', action='store_true',
226						default=False,
227						help='Enable execution.')
228	parser.add_argument('-noquietwget', action='store_true',
229						default=False,
230						help='Let wget output diagnostic information.')
231	parser.add_argument('-nofetch', action='store_true',
232						default=False,
233						help='Skip fetching required sources from github.')
234	parser.add_argument('-nocleanup', action='store_true',
235						default=False,
236						help='Do not remove fetched files after run.')
237	parser.add_argument('-basexml', action='store',
238						default='vk.xml',
239						help='Specify the base xml file with vulkan API information. Defaults to vk.xml.')
240	parser.add_argument('-supersetxml', action='store',
241						default='vk.xml',
242						help='Specify the xml file with superset information. Defaults to vk.xml.')
243	parser.add_argument('-header', action='store',
244						default='external/vulkancts/scripts/src/vulkan.h.in',
245						help='Specify the current header file. Defaults to external/vulkancts/scripts/src/invulkan.h.in.')
246	args = parser.parse_args()
247
248	if not args.go:
249		print(
250"""
251This script is used to generate the Vulkan header file for the Vulkan CTS from
252the vk.xml specification. It can optionally take a superset XML file and the
253user can interactively select which of the superset extensions to use in
254generation.
255
256The script automatically fetches the current vk.xml as well as the header
257generation scripts from github.
258
259For help with options, run with -h. To execute the script, run with -go.
260""")
261		quit()
262
263	fetch_sources(args)
264
265	if not os.path.exists(args.basexml):
266		print("!! Error: Can't find base xml file", args.basexml)
267		quit()
268	if not os.path.exists(args.supersetxml):
269		print("!! Error: Can't find superset xml file", args.supersetxml)
270		quit()
271	if not os.path.exists(args.header):
272		print("!! Error: Can't find header file", args.header)
273		quit()
274
275	get_current_header(args.header)
276	get_base_extensions(args.basexml)
277	parse_superset_extensions(args.supersetxml)
278
279	while True:
280		print_menu()
281		i = input("Option: ")
282		if i != "":
283			if i in "qQ":
284				print ("Quiting without changes")
285				cleanup(args)
286				quit()
287			if i == "go":
288				print ("Generating new header")
289				generate(args)
290				cleanup(args)
291				quit()
292			if not i.isdigit() or int(i) >= len(supersetextensions):
293				print ("Invalid input '"+i+"'")
294			else:
295				supersetextensions[int(i)][1] = not supersetextensions[int(i)][1]
296