1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203
|
import functools
import os
import re
import sys
from collections import namedtuple, defaultdict
if sys.version_info >= (3, 0, 0):
basestring = str
Version = namedtuple('Version', ['major', 'minor'])
ExpandedName = namedtuple('ExpandedName', ['prefix', 'suffix'])
_API_NAMES = {
'egl': 'EGL',
'gl': 'OpenGL',
'gles1': 'OpenGL ES',
'gles2': 'OpenGL ES',
'glsc2': 'OpenGL SC',
'glx': 'GLX',
'wgl': 'WGL',
}
def api_name(api):
api = api.lower()
return _API_NAMES.get(api, api.upper())
def makefiledir(path):
dir = os.path.split(path)[0]
if not os.path.exists(dir):
os.makedirs(dir)
ApiInformation = namedtuple('ApiInformation', ('specification', 'version', 'profile'))
_API_SPEC_MAPPING = {
'gl': 'gl',
'gles1': 'gl',
'gles2': 'gl',
'glsc2': 'gl',
'egl': 'egl',
'glx': 'glx',
'wgl': 'wgl',
'vulkan': 'vk'
}
def parse_version(value):
if value is None:
return None
value = value.strip()
if not value:
return None
major, minor = (value + '.0').split('.')[:2]
return Version(int(major), int(minor))
def parse_apis(value, api_spec_mapping=_API_SPEC_MAPPING):
result = dict()
for api in value.split(','):
api = api.strip()
m = re.match(
r'^(?P<api>\w+)(:(?P<profile>\w+))?(/(?P<spec>\w+))?(=(?P<version>\d+(\.\d+)?)?)?$',
api
)
if m is None:
raise ValueError('Invalid API {}'.format(api))
spec = m.group('spec')
if spec is None:
try:
spec = api_spec_mapping[m.group('api')]
except KeyError:
raise ValueError('Can not resolve specification for API {}'.format(m.group('api')))
version = parse_version(m.group('version'))
result[m.group('api')] = ApiInformation(spec, version, m.group('profile'))
return result
# based on https://stackoverflow.com/a/11564323/969534
def topological_sort(items, key, dependencies):
pending = [(item, set(dependencies(item))) for item in items]
emitted = []
while pending:
next_pending = []
next_emitted = []
for entry in pending:
item, deps = entry
deps.difference_update(emitted)
if deps:
next_pending.append(entry)
else:
yield item
key_item = key(item)
emitted.append(key_item)
next_emitted.append(key_item)
if not next_emitted:
raise ValueError("cyclic or missing dependency detected: %r" % (next_pending,))
pending = next_pending
emitted = next_emitted
class _HashedSeq(list):
__slots__ = 'hashvalue'
# noinspection PyMissingConstructor
def __init__(self, tup, hash=hash):
self[:] = tup
self.hashvalue = hash(tup)
def __hash__(self):
return self.hashvalue
def _default_key_func(*args, **kwargs):
key = (tuple(args), tuple(kwargs.items()))
return _HashedSeq(key)
def memoize(key=None, method=False):
"""
Memoize decorator for functions and methods.
:param key: a cache-key transformation function
:param method: whether the cache should be attached to the `self` parameter
"""
key_func = key or _default_key_func
def memoize_decorator(func):
_cache = dict()
@functools.wraps(func)
def memoized(*args, **kwargs):
cache_args = args
if method:
# This is an attempt to bind the cache to the instance of the currently
# executed method. The idea is to not hoard references to the instance
# and other values (arguments) to not prevent the GC from collecting those.
# If we don't attach it this leaks memory all over the place,
# especially since this implementation currently has an uncapped cache.
self = args[0]
cache_args = args[1:]
try:
funcs_cache = self._memoize_cache
except AttributeError:
funcs_cache = defaultdict(dict)
self._memoize_cache = funcs_cache
cache = funcs_cache[func]
else:
cache = _cache
key = key_func(*cache_args, **kwargs)
if key not in cache:
cache[key] = func(*args, **kwargs)
return cache[key]
return memoized
return memoize_decorator
def itertext(element, ignore=()):
tag = element.tag
if not isinstance(tag, basestring) and tag is not None:
return
if element.text:
yield element.text
for e in element:
if not e.tag in ignore:
for s in itertext(e, ignore=ignore):
yield s
if e.tail:
yield e.tail
def expand_type_name(name):
"""
Transforms a type name into its expanded version, e.g.
expands the type `VkShaderInfoTypeAMD` to the tuple `('VK_SHADER_INFO_TYPE', '_AMD')`.
See: https://github.com/KhronosGroup/Vulkan-Docs/blob/main/scripts/generator.py#L60 buildEnumCDecl_Enum
"""
upper_name = re.sub(r'([0-9]+|[a-z_])([A-Z0-9])', r'\1_\2', name).upper()
(prefix, suffix) = (upper_name, '')
suffix_match = re.search(r'[A-Z][A-Z]+$', name)
if suffix_match:
suffix = '_' + suffix_match.group()
# Strip off the suffix from the prefix
prefix = upper_name.rsplit(suffix, 1)[0]
return ExpandedName(prefix, suffix)
|