generator.py (7837B)
1 from collections import defaultdict 2 from datetime import datetime 3 from itertools import chain 4 import os.path 5 import sys 6 7 from glad.lang.common.loader import NullLoader 8 from glad.opener import URLOpener 9 from glad.util import api_name 10 import glad 11 12 13 if sys.version_info >= (3, 0): 14 from urllib.parse import urlencode 15 else: 16 from urllib import urlencode 17 18 19 HEADER_TEMPLATE = ''' 20 {apis_named} loader generated by glad {version} on {date}. 21 22 Language/Generator: {language} 23 Specification: {specification} 24 APIs: {apis} 25 Profile: {profile} 26 Extensions: 27 {extensions} 28 Loader: {loader} 29 Local files: {local_files} 30 Omit khrplatform: {omit_khrplatform} 31 32 Commandline: 33 {commandline} 34 Online: 35 {online} 36 ''' 37 38 39 class Generator(object): 40 NAME = None 41 NAME_LONG = None 42 URL = 'http://glad.dav1d.de' 43 44 def __init__(self, path, spec, api, extension_names=None, loader=None, 45 opener=None, local_files=False, omit_khrplatform=False, 46 header_template=HEADER_TEMPLATE): 47 self.path = os.path.abspath(path) 48 49 self.spec = spec 50 for a in api: 51 if a not in self.spec.features: 52 raise ValueError( 53 'Unknown API "{0}" for specification "{1}"' 54 .format(a, self.spec.NAME) 55 ) 56 self.api = api 57 self.extension_names = extension_names 58 59 self.has_loader = not loader.disabled 60 self.loader = loader 61 if self.loader is None: 62 self.loader = NullLoader 63 64 self.opener = opener 65 if self.opener is None: 66 self.opener = URLOpener.default() 67 68 self.local_files = local_files 69 self.omit_khrplatform = omit_khrplatform 70 71 self._header_template = header_template 72 73 def open(self): 74 raise NotImplementedError 75 76 def close(self): 77 raise NotImplementedError 78 79 def __enter__(self): 80 self.open() 81 return self 82 83 def __exit__(self, exc_type, exc_value, traceback): 84 self.close() 85 86 def generate(self): 87 features = list() 88 for api, version in self.api.items(): 89 features.extend(self.spec.features[api]) 90 91 if version is None: 92 version = list(self.spec.features[api].keys())[-1] 93 self.api[api] = version 94 95 if version not in self.spec.features[api]: 96 raise ValueError( 97 'Unknown version "{0}" for specification "{1}"' 98 .format(version, self.spec.NAME) 99 ) 100 101 if self.extension_names is None: 102 self.extension_names = list(chain.from_iterable(self.spec.extensions[a] 103 for a in self.api)) 104 105 # sort and eliminate duplicates 106 self.extension_names = list(sorted(set(self.extension_names))) 107 108 e = list(chain.from_iterable(self.spec.extensions[a] for a in self.api)) 109 for ext in self.extension_names: 110 if ext not in e: 111 raise ValueError( 112 'Invalid extension "{0}" for specification "{1}"' 113 .format(ext, self.spec.NAME) 114 ) 115 116 self.generate_header() 117 118 types = [t for t in self.spec.types if t.api is None or t.api in self.api] 119 self.generate_types(types) 120 121 f = list() 122 for api, version in self.api.items(): 123 f.extend([value for key, value in self.spec.features[api].items() 124 if key <= version]) 125 enums, functions = merge(f) 126 self.generate_features(f) 127 128 extensions = list() 129 for api in self.api: 130 extensions.extend(self.spec.extensions[api][ext] 131 for ext in self.extension_names if ext 132 in self.spec.extensions[api]) 133 self.generate_extensions(extensions, enums, functions) 134 135 fs = defaultdict(list) 136 es = defaultdict(list) 137 for api, version in self.api.items(): 138 fs[api].extend( 139 [value for key, value in 140 self.spec.features[api].items() if key <= version] 141 ) 142 es[api].extend(self.spec.extensions[api][ext] 143 for ext in self.extension_names if ext 144 in self.spec.extensions[api]) 145 self.generate_loader(fs, es) 146 147 @property 148 def header(self): 149 apis_named = ', '.join(sorted(set(api_name(api) for api in self.api))) 150 date = datetime.now().strftime('%c') 151 language = self.NAME_LONG 152 specification = self.spec.NAME 153 apis = ', '.join('{}={}'.format(api, '.'.join(map(str, version))) for api, version in self.api.items()) 154 profile = getattr(self.spec, 'profile', '-') 155 extensions = ',\n '.join(self.extension_names) 156 online = self.online 157 if len(online) > 2000: 158 online = 'Too many extensions' 159 160 return self._header_template.format( 161 apis_named=apis_named, 162 version=glad.__version__, 163 date=date, 164 language=language, 165 specification=specification, 166 apis=apis, 167 profile=profile, 168 extensions=extensions, 169 loader=self.has_loader, 170 local_files=self.local_files, 171 omit_khrplatform=self.omit_khrplatform, 172 commandline=self.commandline, 173 online=online 174 ) 175 176 @property 177 def commandline(self): 178 profile = getattr(self.spec, 'profile', None) 179 if profile is not None: 180 profile = '--profile="{}"'.format(profile) 181 182 api = '--api="{}"'.format(','.join( 183 '{}={}'.format(api, '.'.join(map(str, version))) for api, version in self.api.items()) 184 ) 185 generator = '--generator="{}"'.format(self.NAME) 186 specification = '--spec="{}"'.format(self.spec.NAME) 187 loader = '' if self.has_loader else '--no-loader' 188 extensions = '--extensions="{}"'.format(','.join(self.extension_names)) 189 local_files = '--local-files' if self.local_files else '' 190 omit_khrplatform = '--omit-khrplatform' if self.omit_khrplatform else '' 191 192 return ' '.join(filter(None, [ 193 profile, api, generator, specification, 194 loader, local_files, omit_khrplatform, extensions 195 ])) 196 197 @property 198 def online(self): 199 profile = getattr(self.spec, 'profile', None) 200 if profile is not None: 201 profile = ('profile', profile) 202 203 api = [('api', s) for s in ('{}={}'.format(api, '.'.join(map(str, version))) for api, version in self.api.items())] 204 generator = ('language', self.NAME) 205 specification = ('specification', self.spec.NAME) 206 loader = ('loader', 'on') if self.has_loader else None 207 extensions = [('extensions', ext) for ext in self.extension_names] 208 209 data = [profile, generator, specification, loader] 210 data.extend(api) 211 data.extend(extensions) 212 data = list(filter(None, data)) 213 serialized = urlencode(data) 214 215 # TODO: --local-files, --omit-khrplatform 216 return '{}/#{}'.format(self.URL, serialized) 217 218 def generate_header(self): 219 raise NotImplementedError 220 221 def generate_loader(self, features, extensions): 222 raise NotImplementedError 223 224 def generate_types(self, types): 225 raise NotImplementedError 226 227 def generate_features(self, features): 228 raise NotImplementedError 229 230 def generate_extensions(self, extensions, enums, functions): 231 raise NotImplementedError 232 233 234 def merge(features): 235 enums = set() 236 functions = set() 237 238 for feature in features: 239 enums |= set(feature.enums) 240 functions |= set(feature.functions) 241 242 return enums, functions