242 lines
7.7 KiB
Python
242 lines
7.7 KiB
Python
from collections import defaultdict
|
|
from datetime import datetime
|
|
from itertools import chain
|
|
import os.path
|
|
import sys
|
|
|
|
from glad.lang.common.loader import NullLoader
|
|
from glad.opener import URLOpener
|
|
from glad.util import api_name
|
|
import glad
|
|
|
|
|
|
if sys.version_info >= (3, 0):
|
|
from urllib.parse import urlencode
|
|
else:
|
|
from urllib import urlencode
|
|
|
|
|
|
HEADER_TEMPLATE = '''
|
|
{apis_named} loader generated by glad {version} on {date}.
|
|
|
|
Language/Generator: {language}
|
|
Specification: {specification}
|
|
APIs: {apis}
|
|
Profile: {profile}
|
|
Extensions:
|
|
{extensions}
|
|
Loader: {loader}
|
|
Local files: {local_files}
|
|
Omit khrplatform: {omit_khrplatform}
|
|
|
|
Commandline:
|
|
{commandline}
|
|
Online:
|
|
{online}
|
|
'''
|
|
|
|
|
|
class Generator(object):
|
|
NAME = None
|
|
NAME_LONG = None
|
|
URL = 'http://glad.dav1d.de'
|
|
|
|
def __init__(self, path, spec, api, extension_names=None, loader=None,
|
|
opener=None, local_files=False, omit_khrplatform=False,
|
|
header_template=HEADER_TEMPLATE):
|
|
self.path = os.path.abspath(path)
|
|
|
|
self.spec = spec
|
|
for a in api:
|
|
if a not in self.spec.features:
|
|
raise ValueError(
|
|
'Unknown API "{0}" for specification "{1}"'
|
|
.format(a, self.spec.NAME)
|
|
)
|
|
self.api = api
|
|
self.extension_names = extension_names
|
|
|
|
self.has_loader = not loader.disabled
|
|
self.loader = loader
|
|
if self.loader is None:
|
|
self.loader = NullLoader
|
|
|
|
self.opener = opener
|
|
if self.opener is None:
|
|
self.opener = URLOpener.default()
|
|
|
|
self.local_files = local_files
|
|
self.omit_khrplatform = omit_khrplatform
|
|
|
|
self._header_template = header_template
|
|
|
|
def open(self):
|
|
raise NotImplementedError
|
|
|
|
def close(self):
|
|
raise NotImplementedError
|
|
|
|
def __enter__(self):
|
|
self.open()
|
|
return self
|
|
|
|
def __exit__(self, exc_type, exc_value, traceback):
|
|
self.close()
|
|
|
|
def generate(self):
|
|
features = list()
|
|
for api, version in self.api.items():
|
|
features.extend(self.spec.features[api])
|
|
|
|
if version is None:
|
|
version = list(self.spec.features[api].keys())[-1]
|
|
self.api[api] = version
|
|
|
|
if version not in self.spec.features[api]:
|
|
raise ValueError(
|
|
'Unknown version "{0}" for specification "{1}"'
|
|
.format(version, self.spec.NAME)
|
|
)
|
|
|
|
if self.extension_names is None:
|
|
self.extension_names = list(chain.from_iterable(self.spec.extensions[a]
|
|
for a in self.api))
|
|
|
|
# sort and eliminate duplicates
|
|
self.extension_names = list(sorted(set(self.extension_names)))
|
|
|
|
e = list(chain.from_iterable(self.spec.extensions[a] for a in self.api))
|
|
for ext in self.extension_names:
|
|
if ext not in e:
|
|
raise ValueError(
|
|
'Invalid extension "{0}" for specification "{1}"'
|
|
.format(ext, self.spec.NAME)
|
|
)
|
|
|
|
self.generate_header()
|
|
|
|
types = [t for t in self.spec.types if t.api is None or t.api in self.api]
|
|
self.generate_types(types)
|
|
|
|
f = list()
|
|
for api, version in self.api.items():
|
|
f.extend([value for key, value in self.spec.features[api].items()
|
|
if key <= version])
|
|
enums, functions = merge(f)
|
|
self.generate_features(f)
|
|
|
|
extensions = list()
|
|
for api in self.api:
|
|
extensions.extend(self.spec.extensions[api][ext]
|
|
for ext in self.extension_names if ext
|
|
in self.spec.extensions[api])
|
|
self.generate_extensions(extensions, enums, functions)
|
|
|
|
fs = defaultdict(list)
|
|
es = defaultdict(list)
|
|
for api, version in self.api.items():
|
|
fs[api].extend(
|
|
[value for key, value in
|
|
self.spec.features[api].items() if key <= version]
|
|
)
|
|
es[api].extend(self.spec.extensions[api][ext]
|
|
for ext in self.extension_names if ext
|
|
in self.spec.extensions[api])
|
|
self.generate_loader(fs, es)
|
|
|
|
@property
|
|
def header(self):
|
|
apis_named = ', '.join(sorted(set(api_name(api) for api in self.api)))
|
|
date = datetime.now().strftime('%c')
|
|
language = self.NAME_LONG
|
|
specification = self.spec.NAME
|
|
apis = ', '.join('{}={}'.format(api, '.'.join(map(str, version))) for api, version in self.api.items())
|
|
profile = getattr(self.spec, 'profile', '-')
|
|
extensions = ',\n '.join(self.extension_names)
|
|
online = self.online
|
|
if len(online) > 2000:
|
|
online = 'Too many extensions'
|
|
|
|
return self._header_template.format(
|
|
apis_named=apis_named,
|
|
version=glad.__version__,
|
|
date=date,
|
|
language=language,
|
|
specification=specification,
|
|
apis=apis,
|
|
profile=profile,
|
|
extensions=extensions,
|
|
loader=self.has_loader,
|
|
local_files=self.local_files,
|
|
omit_khrplatform=self.omit_khrplatform,
|
|
commandline=self.commandline,
|
|
online=online
|
|
)
|
|
|
|
@property
|
|
def commandline(self):
|
|
profile = getattr(self.spec, 'profile', None)
|
|
if profile is not None:
|
|
profile = '--profile="{}"'.format(profile)
|
|
|
|
api = '--api="{}"'.format(','.join(
|
|
'{}={}'.format(api, '.'.join(map(str, version))) for api, version in self.api.items())
|
|
)
|
|
generator = '--generator="{}"'.format(self.NAME)
|
|
specification = '--spec="{}"'.format(self.spec.NAME)
|
|
loader = '' if self.has_loader else '--no-loader'
|
|
extensions = '--extensions="{}"'.format(','.join(self.extension_names))
|
|
local_files = '--local-files' if self.local_files else ''
|
|
omit_khrplatform = '--omit-khrplatform' if self.omit_khrplatform else ''
|
|
|
|
return ' '.join(filter(None, [
|
|
profile, api, generator, specification,
|
|
loader, local_files, omit_khrplatform, extensions
|
|
]))
|
|
|
|
@property
|
|
def online(self):
|
|
profile = getattr(self.spec, 'profile', None)
|
|
if profile is not None:
|
|
profile = ('profile', profile)
|
|
|
|
api = [('api', s) for s in ('{}={}'.format(api, '.'.join(map(str, version))) for api, version in self.api.items())]
|
|
generator = ('language', self.NAME)
|
|
specification = ('specification', self.spec.NAME)
|
|
loader = ('loader', 'on') if self.has_loader else None
|
|
extensions = [('extensions', ext) for ext in self.extension_names]
|
|
|
|
data = [profile, generator, specification, loader]
|
|
data.extend(api)
|
|
data.extend(extensions)
|
|
data = list(filter(None, data))
|
|
serialized = urlencode(data)
|
|
|
|
# TODO: --local-files, --omit-khrplatform
|
|
return '{}/#{}'.format(self.URL, serialized)
|
|
|
|
def generate_header(self):
|
|
raise NotImplementedError
|
|
|
|
def generate_loader(self, features, extensions):
|
|
raise NotImplementedError
|
|
|
|
def generate_types(self, types):
|
|
raise NotImplementedError
|
|
|
|
def generate_features(self, features):
|
|
raise NotImplementedError
|
|
|
|
def generate_extensions(self, extensions, enums, functions):
|
|
raise NotImplementedError
|
|
|
|
|
|
def merge(features):
|
|
enums = set()
|
|
functions = set()
|
|
|
|
for feature in features:
|
|
enums |= set(feature.enums)
|
|
functions |= set(feature.functions)
|
|
|
|
return enums, functions
|