Danny Robson
863cdf4a35
XSLT1 is too hard to work with, and Khronos have an annoying tendency to generate their spec such that it's not in dependency order. So we're switching to python, and performing basic depedency analysis within a new tool. This should allow us to do more extensive type modifications and annotations.
419 lines
12 KiB
Python
Executable File
419 lines
12 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
import logging
|
|
|
|
import xml.etree.ElementTree as ET
|
|
|
|
|
|
###############################################################################
|
|
class type(object):
|
|
def __init__(self, name):
|
|
self.name = name
|
|
|
|
def depends(self):
|
|
return []
|
|
|
|
def declare(self):
|
|
return ""
|
|
|
|
def define(self):
|
|
return ""
|
|
|
|
|
|
##-----------------------------------------------------------------------------
|
|
class basetype(type):
|
|
def __init__(self, node):
|
|
assert(node.tag == 'type')
|
|
assert(node.attrib['category'] == 'basetype')
|
|
|
|
super().__init__(node.find('name').text)
|
|
self._type = node.find('type').text
|
|
|
|
def depends(self):
|
|
return [self._type]
|
|
|
|
def define(self):
|
|
return "using %s = %s;" % (self.name, self._type)
|
|
|
|
|
|
##-----------------------------------------------------------------------------
|
|
class handle(type):
|
|
def __init__(self, node):
|
|
assert (node.tag == "type")
|
|
assert (node.attrib['category'] == 'handle')
|
|
|
|
super().__init__(node.find('name').text)
|
|
|
|
def declare(self):
|
|
return "using %s = uintptr_t;" % self.name
|
|
|
|
|
|
##-----------------------------------------------------------------------------
|
|
class constant(type):
|
|
def __init__(self,node):
|
|
assert(node.tag == 'enum')
|
|
|
|
super().__init__(node.attrib['name'])
|
|
|
|
self._value = node.attrib['value']
|
|
|
|
def define(self):
|
|
return "constexpr auto %s = %s;" % (self.name, self._value)
|
|
|
|
|
|
##-----------------------------------------------------------------------------
|
|
# a high level 'bitmask' enum that doesn't necessarily define anything.
|
|
# inherits the values from something else instead. super fucking weird..
|
|
class bitmask(type):
|
|
def __init__(self, node):
|
|
assert(node.tag == 'type')
|
|
assert(node.attrib['category'] == 'bitmask')
|
|
|
|
super().__init__(node.find('name').text)
|
|
|
|
self._requires = node.attrib.get('requires', None)
|
|
self._type = node.find('type').text
|
|
|
|
def declare(self):
|
|
return ""
|
|
|
|
def depends(self):
|
|
if self._requires:
|
|
return [self._type, self._requires]
|
|
else:
|
|
return [self._type]
|
|
|
|
def define(self):
|
|
return "using %s = %s;" % (self.name, self._type)
|
|
|
|
|
|
##-----------------------------------------------------------------------------
|
|
# an enum with bit values that will be used by a bitmask enum. weirdly...
|
|
class bitflag(type):
|
|
def __init__(self, node):
|
|
assert(node.tag == "enums")
|
|
assert(node.attrib['type'] == 'bitmask')
|
|
|
|
name = node.attrib['name']
|
|
super ().__init__(name)
|
|
|
|
self._depends = node.attrib.get('requires', None)
|
|
|
|
self.values = []
|
|
for v in node.findall("./enum"):
|
|
if 'value' in v.attrib:
|
|
self.values.append({'name': v.attrib['name'], 'value': v.attrib['value']})
|
|
elif 'bitpos' in v.attrib:
|
|
self.values.append({'name': v.attrib['name'], 'value': '1 << %s' % v.attrib['bitpos']})
|
|
else:
|
|
assert False, "unhandled bitmask type"
|
|
|
|
def depends(self):
|
|
if self._depends:
|
|
return [self._depends]
|
|
else:
|
|
return []
|
|
|
|
def declare(self):
|
|
return ""
|
|
|
|
def define(self):
|
|
values = map(lambda x: "%(name)s = %(value)s" % x, self.values)
|
|
return "enum %s { %s };" % (self.name, ", ".join(values))
|
|
|
|
|
|
##-----------------------------------------------------------------------------
|
|
class enum(type):
|
|
def __init__(self, node):
|
|
assert(node.tag == "enums")
|
|
|
|
name = node.attrib['name']
|
|
super().__init__(name)
|
|
|
|
self.values = map(
|
|
lambda x: {
|
|
'name' : x.attrib['name'],
|
|
'value': x.attrib['value']
|
|
},
|
|
node.findall('./enum')
|
|
)
|
|
|
|
def declare(self):
|
|
return ""
|
|
|
|
def define(self):
|
|
values = map(lambda x: "%(name)s = %(value)s" % x, self.values)
|
|
|
|
return "enum %s { %s };" % (
|
|
self.name,
|
|
", ".join(values)
|
|
)
|
|
|
|
|
|
##-----------------------------------------------------------------------------
|
|
class funcpointer(type):
|
|
def __init__(self, node):
|
|
assert(node.tag == 'type')
|
|
assert(node.attrib['category'] == "funcpointer")
|
|
|
|
name = node.find('name').text
|
|
super().__init__(name)
|
|
self.params = list(map(lambda x: x.text, node.findall('./type')))
|
|
self.text = "".join(node.itertext())
|
|
|
|
def depends(self):
|
|
return ['VkBool32'] + self.params
|
|
|
|
def declare(self):
|
|
return self.text
|
|
|
|
def define(self):
|
|
return "";
|
|
|
|
|
|
##-----------------------------------------------------------------------------
|
|
class pod(type):
|
|
def __init__(self,node):
|
|
assert(node.tag == 'type')
|
|
assert(node.attrib['category'] in ['struct', 'union'])
|
|
|
|
super().__init__(node.attrib['name'])
|
|
self._category = node.attrib['category']
|
|
|
|
# sometimes there are enums hiding in the member fields being used as array sizes
|
|
self._depends = []
|
|
self._depends += list(e.text for e in node.findall('.//enum'))
|
|
self._depends += list(t.text for t in node.findall('.//type'))
|
|
|
|
self._members = list(map(
|
|
lambda x: {
|
|
'type': x.find('type').text,
|
|
'name': x.find('name').text,
|
|
# we must include a space separator otherwise we get run-on
|
|
# types/names in the member definitions.
|
|
'code': " ".join(x.itertext())
|
|
},
|
|
node.findall('./member'))
|
|
)
|
|
|
|
def depends(self):
|
|
return self._depends
|
|
|
|
def declare(self):
|
|
return "%s %s;" % (self._category, self.name)
|
|
|
|
def define(self):
|
|
return "%(category)s %(name)s { %(members)s };" % {
|
|
'category': self._category,
|
|
'name': self.name,
|
|
'members': "\n".join(m['code'] + ';' for m in self._members)
|
|
}
|
|
|
|
|
|
##-----------------------------------------------------------------------------
|
|
class struct(pod):
|
|
def __init__(self,node):
|
|
super().__init__(node)
|
|
|
|
|
|
##-----------------------------------------------------------------------------
|
|
class union(pod):
|
|
def __init__(self,node):
|
|
super().__init__(node)
|
|
|
|
|
|
###############################################################################
|
|
def parse_types(nodes):
|
|
types = []
|
|
|
|
for n in nodes:
|
|
# we need to parse too much unstructured text to actually do anything
|
|
# useful with these declarations
|
|
for t in n.findall("./type[@category='handle']"):
|
|
types.append(handle(t))
|
|
|
|
for t in n.findall('./type[@category="bitmask"]'):
|
|
types.append(bitmask(t))
|
|
|
|
for t in n.findall('./type[@category="basetype"]'):
|
|
types.append(basetype(t))
|
|
|
|
for t in n.findall("./type[@category='funcpointer']"):
|
|
types.append(funcpointer(t))
|
|
|
|
for t in n.findall("./type[@category='union']"):
|
|
types.append(union(t))
|
|
|
|
for t in n.findall("./type[@category='struct']"):
|
|
types.append(struct(t))
|
|
|
|
return types
|
|
|
|
|
|
###############################################################################
|
|
def parse_enums(nodes):
|
|
enums = []
|
|
|
|
for n in nodes:
|
|
if n.attrib['name'] == "API Constants":
|
|
for c in n.findall('./enum'):
|
|
enums.append(constant(c))
|
|
elif n.attrib['type'] == 'bitmask':
|
|
enums.append(bitflag(n))
|
|
elif n.attrib['type'] == 'enum':
|
|
enums.append(enum(n))
|
|
else:
|
|
assert False, "unhandled enum type"
|
|
|
|
return enums
|
|
|
|
|
|
###############################################################################
|
|
class command(type):
|
|
def __init__(self, node):
|
|
assert(node.tag == "command")
|
|
proto = node.find('proto')
|
|
super().__init__(proto.find('name').text)
|
|
|
|
self._result = proto.find('type').text
|
|
self._params = []
|
|
self._depends = []
|
|
for p in node.findall('./param'):
|
|
self._depends.append(p.find('type').text)
|
|
self._params.append("".join(p.itertext()))
|
|
|
|
def depends(self):
|
|
return [self._result] + self._depends
|
|
|
|
def declare(self):
|
|
return "%(result)s %(name)s (%(params)s) noexcept;" % {
|
|
'name': self.name,
|
|
'result': self._result,
|
|
'params': ", ".join(self._params)
|
|
}
|
|
|
|
|
|
##-----------------------------------------------------------------------------
|
|
def parse_commands(nodes):
|
|
commands = []
|
|
|
|
for n in nodes:
|
|
for c in n.findall('./command'):
|
|
commands.append(command(c))
|
|
|
|
return commands
|
|
|
|
|
|
###############################################################################
|
|
def write_type(dst, types, t):
|
|
logging.info("writing: %s", t.name)
|
|
|
|
for d in t.depends():
|
|
if d in types:
|
|
write_type(dst, types, types[d])
|
|
|
|
if t.name in types:
|
|
dst.write(t.declare())
|
|
dst.write(t.define())
|
|
dst.write('\n')
|
|
del types[t.name]
|
|
|
|
|
|
##-----------------------------------------------------------------------------
|
|
def write_types(dst, types):
|
|
keys = list(types.keys())
|
|
|
|
for k in keys:
|
|
if k in types:
|
|
write_type(dst, types, types[k])
|
|
|
|
|
|
##-----------------------------------------------------------------------------
|
|
def write_root(dst, node):
|
|
dst.write ("""
|
|
#ifndef __VK_HPP
|
|
#define __VK_HPP
|
|
#include <cstdint>
|
|
#include <cstddef>
|
|
|
|
#if defined(__cplusplus)
|
|
extern "C" {
|
|
#endif
|
|
|
|
struct Display;
|
|
struct VisualID;
|
|
using Window = unsigned long;
|
|
|
|
struct ANativeWindow;
|
|
|
|
struct MirConnection;
|
|
struct MirSurface;
|
|
|
|
struct wl_display;
|
|
struct wl_surface;
|
|
|
|
using HANDLE = void*;
|
|
using HINSTANCE = HANDLE;
|
|
using HWND = HANDLE;
|
|
|
|
using SECURITY_ATTRIBUTES = HANDLE;
|
|
using DWORD = uint32_t;
|
|
using LPCWSTR = char16_t*;
|
|
|
|
struct xcb_connection_t;
|
|
struct xcb_visualid_t;
|
|
using xcb_window_t = uint32_t;
|
|
using XID = unsigned long;
|
|
using RROutput = XID;
|
|
|
|
#define VKAPI_PTR /*VKAPI_PTR*/
|
|
|
|
// because, in its wisdom, the spec doesn't actually allow us to
|
|
// extract the value for VK_NULL_HANDLE from the XML we'll just
|
|
// hard code it here.
|
|
#define VK_NULL_HANDLE uintptr_t(0)
|
|
""")
|
|
|
|
types = []
|
|
types += parse_types(node.findall('./types'))
|
|
types += parse_enums(node.findall('./enums'))
|
|
types += parse_commands(node.findall('./commands'))
|
|
|
|
types = dict((t.name,t) for t in types)
|
|
|
|
write_types(dst, types)
|
|
|
|
#dst.writelines("\n".join(map(lambda x: x.declare(), types)))
|
|
#dst.writelines("\n".join(map(lambda x: x.define(), types)))
|
|
#dst.writelines("\n".join(map(lambda x: x.declare(), commands)))
|
|
|
|
dst.write ("""
|
|
#if defined(__cplusplus)
|
|
}
|
|
#endif
|
|
#endif
|
|
""")
|
|
|
|
|
|
###############################################################################
|
|
import argparse
|
|
|
|
|
|
##-----------------------------------------------------------------------------
|
|
if __name__ == '__main__':
|
|
#logging.getLogger().setLevel(logging.INFO)
|
|
|
|
parser = argparse.ArgumentParser(description='Transform XML API specification into C++ headers')
|
|
parser.add_argument('src', type=str, help='the path to the XML file to transform')
|
|
parser.add_argument('dst', type=str, help='the output path for the result')
|
|
|
|
args = parser.parse_args()
|
|
|
|
src = open(args.src, 'r')
|
|
dst = open(args.dst, 'w')
|
|
|
|
tree = ET.parse(src)
|
|
root = tree.getroot()
|
|
|
|
write_root(dst, root)
|