| ... | ... |
@@ -1,9 +1,155 @@ |
| 1 | 1 |
# [`glregistry`][] |
| 2 | 2 |
|
| 3 |
-Cache and query the [OpenGL registry][] locally. |
|
| 3 |
+Cache and query the [OpenGL][] [registry][] locally. |
|
| 4 |
+ |
|
| 5 |
+`glregistry` is a Python program that glues together [HTTPS][] downloading, |
|
| 6 |
+[XPath][] querying, data accumulation/sorting, [`grep`][]ing, [color][] output, |
|
| 7 |
+and launching external [editor][]/[pager][] programs. |
|
| 8 |
+ |
|
| 9 |
+It tries to be a good Unix citizen by obeying relevant environment variables |
|
| 10 |
+and being [pipe][]-friendly, outputting newline-separated entries. Where |
|
| 11 |
+appropriate each line is in a widely supported format, usable with e.g. |
|
| 12 |
+[Vim][]'s [QuickFix][] ([`:cexpr`][]` system('glregistry audit')` or [`vim
|
|
| 13 |
+-q`][]` <(glregistry audit)`) and [GNU Emacs][]' [Compilation Mode][] ([`M-x |
|
| 14 |
+compile`][]` glregistry audit`). |
|
| 15 |
+ |
|
| 16 |
+Note that only the OpenGL (not OpenGL ES) API, and only the core (not |
|
| 17 |
+compatibility) [profile][] is considered. |
|
| 4 | 18 |
|
| 5 | 19 |
[`glregistry`]: https://git.rcrnstn.net/rcrnstn/glregistry |
| 6 |
-[OpenGL registry]: https://www.khronos.org/registry/OpenGL/ |
|
| 20 |
+[OpenGL]: https://en.wikipedia.org/wiki/OpenGL |
|
| 21 |
+[registry]: https://registry.khronos.org/OpenGL/ |
|
| 22 |
+[HTTPS]: https://en.wikipedia.org/wiki/HTTPS |
|
| 23 |
+[XPath]: https://en.wikipedia.org/wiki/XPath |
|
| 24 |
+[`grep`]: https://en.wikipedia.org/wiki/Grep |
|
| 25 |
+[color]: https://en.wikipedia.org/wiki/ANSI_escape_code#Colors |
|
| 26 |
+[editor]: https://en.wikipedia.org/wiki/Text_editor |
|
| 27 |
+[pager]: https://en.wikipedia.org/wiki/Terminal_pager |
|
| 28 |
+[pipe]: https://en.wikipedia.org/wiki/Pipeline_(Unix) |
|
| 29 |
+[Vim]: https://en.wikipedia.org/wiki/Vim_(text_editor) |
|
| 30 |
+[QuickFix]: https://vimhelp.org/quickfix.txt.html |
|
| 31 |
+[`:cexpr`]: https://vimhelp.org/quickfix.txt.html#%3Acexpr |
|
| 32 |
+[`vim -q`]: https://vimhelp.org/starting.txt.html#-q |
|
| 33 |
+[GNU Emacs]: https://en.wikipedia.org/wiki/GNU_Emacs |
|
| 34 |
+[Compilation Mode]: https://www.gnu.org/software/emacs/manual/html_node/emacs/Compilation-Mode.html |
|
| 35 |
+[`M-x compile`]: https://www.gnu.org/software/emacs/manual/html_node/emacs/Compilation.html#index-compile-2624 |
|
| 36 |
+[profile]: https://www.khronos.org/opengl/wiki/OpenGL_Context#OpenGL_3.2_and_Profiles |
|
| 37 |
+ |
|
| 38 |
+## Usage |
|
| 39 |
+ |
|
| 40 |
+`glregistry --help`: |
|
| 41 |
+ |
|
| 42 |
+``` |
|
| 43 |
+glregistry 1.0 |
|
| 44 |
+ |
|
| 45 |
+Cache and query the OpenGL registry locally. |
|
| 46 |
+ |
|
| 47 |
+Usage: |
|
| 48 |
+ glregistry xml |
|
| 49 |
+ glregistry xml-path |
|
| 50 |
+ glregistry ext <extension> |
|
| 51 |
+ glregistry ext-path <extension> |
|
| 52 |
+ glregistry exts |
|
| 53 |
+ glregistry exts-download |
|
| 54 |
+ glregistry exts-all <name> |
|
| 55 |
+ glregistry type <type> |
|
| 56 |
+ glregistry value <enum> |
|
| 57 |
+ glregistry enum <value> |
|
| 58 |
+ glregistry supports <name> |
|
| 59 |
+ glregistry names [<support>] |
|
| 60 |
+ glregistry groups [<enum>] |
|
| 61 |
+ glregistry enums [<group>] |
|
| 62 |
+ glregistry enums-tree [<group>] |
|
| 63 |
+ glregistry params [<group>] |
|
| 64 |
+ glregistry params-tree [<group>] |
|
| 65 |
+ glregistry audit [<path>] |
|
| 66 |
+ glregistry audit-tree [<path>] |
|
| 67 |
+ glregistry -h|--help |
|
| 68 |
+ |
|
| 69 |
+Commands: |
|
| 70 |
+ xml |
|
| 71 |
+ Download the registry XML and open it with an editor. |
|
| 72 |
+ xml-path |
|
| 73 |
+ Download the registry XML and print its local path. |
|
| 74 |
+ ext <extension> |
|
| 75 |
+ Download the <extension> spec and open it with an editor. |
|
| 76 |
+ ext-path <extension> |
|
| 77 |
+ Download the <extension> spec and print its local path. |
|
| 78 |
+ exts |
|
| 79 |
+ Print the names of all extension specs. |
|
| 80 |
+ exts-download |
|
| 81 |
+ Download all extension specs. |
|
| 82 |
+ exts-all <name> |
|
| 83 |
+ Print all downloaded extensions that mention <name>. |
|
| 84 |
+ type <type> |
|
| 85 |
+ Print the definition of <type>. |
|
| 86 |
+ value <enum> |
|
| 87 |
+ Print the value of <enum>. |
|
| 88 |
+ enum <value> |
|
| 89 |
+ Print the enum(s) that has the given <value>, using exact string matching. |
|
| 90 |
+ supports <name> |
|
| 91 |
+ Print the OpenGL version or extension required to use <name>. |
|
| 92 |
+ names [<support>] |
|
| 93 |
+ Print the names introduced by the OpenGL version or extension <support> if |
|
| 94 |
+ given, or all names if omitted. The special values VERSION and EXTENSION |
|
| 95 |
+ print the names introduced by all versions or all extensions respectively. |
|
| 96 |
+ groups [<enum>] |
|
| 97 |
+ Print the groups of <enum> if given, or all groups if omitted. |
|
| 98 |
+ enums [<group>] |
|
| 99 |
+ Print the enums in <group> if given, or all enums if omitted. |
|
| 100 |
+ enums-tree [<group>] |
|
| 101 |
+ Print the enums in <group> if given, or all enums if omitted, sorted on |
|
| 102 |
+ support, in a tree. |
|
| 103 |
+ params [<group>] |
|
| 104 |
+ Print the parameter names of <group> if given, or all parameter names if |
|
| 105 |
+ omitted. |
|
| 106 |
+ params-tree [<group>] |
|
| 107 |
+ Print the parameter names of <group> if given, or all parameter names if |
|
| 108 |
+ omitted, sorted on count, together with the commands sorted on support, in |
|
| 109 |
+ a tree. |
|
| 110 |
+ audit [<path>] |
|
| 111 |
+ Search files in <path> if given, or the current directory if omitted, |
|
| 112 |
+ recursively for OpenGL API names and print them sorted on location, |
|
| 113 |
+ support, and name, in a list. |
|
| 114 |
+ audit-tree [<path>] |
|
| 115 |
+ Search files in <path> if given, or the current directory if omitted, |
|
| 116 |
+ recursively for OpenGL API names and print them sorted on support, name, |
|
| 117 |
+ and location, in a tree. |
|
| 118 |
+ |
|
| 119 |
+Environment variables: |
|
| 120 |
+ GLREGISTRY_CACHE |
|
| 121 |
+ The directory to cache files in. Defaults to `$XDG_CACHE_HOME/glregistry` |
|
| 122 |
+ or, if `$XDG_CACHE_HOME` is not defined, `$HOME/.cache/glregistry`. |
|
| 123 |
+ GLREGISTRY_EDITOR |
|
| 124 |
+ The editor to use when opening files. Defaults to `$EDITOR` or, if |
|
| 125 |
+ `$EDITOR` is not defined, `editor` if it exists in `$PATH`, else `vi`. The |
|
| 126 |
+ value is interpreted by the shell. |
|
| 127 |
+ GLREGISTRY_PAGER |
|
| 128 |
+ The pager to use when viewing output. Defaults to `$PAGER` or, if `$PAGER` |
|
| 129 |
+ is not defined, `pager` if it exists in `$PATH`, else `less` . The value is |
|
| 130 |
+ interpreted by the shell. If the `$LESS` environment variable is unset, it |
|
| 131 |
+ is set to `FR`. |
|
| 132 |
+``` |
|
| 133 |
+ |
|
| 134 |
+## References |
|
| 135 |
+ |
|
| 136 |
+Note that some of these might be out of date. |
|
| 137 |
+ |
|
| 138 |
+- <https://github.com/KhronosGroup/OpenGL-Registry/blob/main/xml/readme.pdf> |
|
| 139 |
+- <https://github.com/KhronosGroup/OpenGL-Registry/blob/main/xml/registry.rnc> |
|
| 140 |
+- <https://github.com/KhronosGroup/OpenGL-Registry/blob/main/xml/reg.py> |
|
| 141 |
+- <https://github.com/KhronosGroup/OpenGL-Registry/blob/main/xml/genheaders.py> |
|
| 142 |
+- <https://github.com/KhronosGroup/OpenGL-Registry/blob/main/extensions/registry.py> |
|
| 143 |
+- <https://github.com/KhronosGroup/OpenGL-Registry/blob/main/docs/syntaxrules.txt> |
|
| 144 |
+- <https://github.com/KhronosGroup/OpenGL-Registry/blob/main/docs/promoting.html> |
|
| 145 |
+- <https://github.com/KhronosGroup/OpenGL-Registry/blob/main/docs/enums.html> |
|
| 146 |
+- <https://github.com/KhronosGroup/OpenGL-Registry/issues/481> |
|
| 147 |
+ |
|
| 148 |
+See also: |
|
| 149 |
+ |
|
| 150 |
+- The community-driven [OpenGL Hardware Database][]. |
|
| 151 |
+ |
|
| 152 |
+[OpenGL Hardware Database]: https://opengl.gpuinfo.org |
|
| 7 | 153 |
|
| 8 | 154 |
## Install |
| 9 | 155 |
|
| 10 | 156 |
new file mode 100755 |
| ... | ... |
@@ -0,0 +1,498 @@ |
| 1 |
+#!/usr/bin/env python3 |
|
| 2 |
+ |
|
| 3 |
+ |
|
| 4 |
+## Help |
|
| 5 |
+""" |
|
| 6 |
+glregistry 1.0 |
|
| 7 |
+ |
|
| 8 |
+Cache and query the OpenGL registry locally. |
|
| 9 |
+ |
|
| 10 |
+Usage: |
|
| 11 |
+ glregistry xml |
|
| 12 |
+ glregistry xml-path |
|
| 13 |
+ glregistry ext <extension> |
|
| 14 |
+ glregistry ext-path <extension> |
|
| 15 |
+ glregistry exts |
|
| 16 |
+ glregistry exts-download |
|
| 17 |
+ glregistry exts-all <name> |
|
| 18 |
+ glregistry type <type> |
|
| 19 |
+ glregistry value <enum> |
|
| 20 |
+ glregistry enum <value> |
|
| 21 |
+ glregistry supports <name> |
|
| 22 |
+ glregistry names [<support>] |
|
| 23 |
+ glregistry groups [<enum>] |
|
| 24 |
+ glregistry enums [<group>] |
|
| 25 |
+ glregistry enums-tree [<group>] |
|
| 26 |
+ glregistry params [<group>] |
|
| 27 |
+ glregistry params-tree [<group>] |
|
| 28 |
+ glregistry audit [<path>] |
|
| 29 |
+ glregistry audit-tree [<path>] |
|
| 30 |
+ glregistry -h|--help |
|
| 31 |
+ |
|
| 32 |
+Commands: |
|
| 33 |
+ xml |
|
| 34 |
+ Download the registry XML and open it with an editor. |
|
| 35 |
+ xml-path |
|
| 36 |
+ Download the registry XML and print its local path. |
|
| 37 |
+ ext <extension> |
|
| 38 |
+ Download the <extension> spec and open it with an editor. |
|
| 39 |
+ ext-path <extension> |
|
| 40 |
+ Download the <extension> spec and print its local path. |
|
| 41 |
+ exts |
|
| 42 |
+ Print the names of all extension specs. |
|
| 43 |
+ exts-download |
|
| 44 |
+ Download all extension specs. |
|
| 45 |
+ exts-all <name> |
|
| 46 |
+ Print all downloaded extensions that mention <name>. |
|
| 47 |
+ type <type> |
|
| 48 |
+ Print the definition of <type>. |
|
| 49 |
+ value <enum> |
|
| 50 |
+ Print the value of <enum>. |
|
| 51 |
+ enum <value> |
|
| 52 |
+ Print the enum(s) that has the given <value>, using exact string matching. |
|
| 53 |
+ supports <name> |
|
| 54 |
+ Print the OpenGL version or extension required to use <name>. |
|
| 55 |
+ names [<support>] |
|
| 56 |
+ Print the names introduced by the OpenGL version or extension <support> if |
|
| 57 |
+ given, or all names if omitted. The special values VERSION and EXTENSION |
|
| 58 |
+ print the names introduced by all versions or all extensions respectively. |
|
| 59 |
+ groups [<enum>] |
|
| 60 |
+ Print the groups of <enum> if given, or all groups if omitted. |
|
| 61 |
+ enums [<group>] |
|
| 62 |
+ Print the enums in <group> if given, or all enums if omitted. |
|
| 63 |
+ enums-tree [<group>] |
|
| 64 |
+ Print the enums in <group> if given, or all enums if omitted, sorted on |
|
| 65 |
+ support, in a tree. |
|
| 66 |
+ params [<group>] |
|
| 67 |
+ Print the parameter names of <group> if given, or all parameter names if |
|
| 68 |
+ omitted. |
|
| 69 |
+ params-tree [<group>] |
|
| 70 |
+ Print the parameter names of <group> if given, or all parameter names if |
|
| 71 |
+ omitted, sorted on count, together with the commands sorted on support, in |
|
| 72 |
+ a tree. |
|
| 73 |
+ audit [<path>] |
|
| 74 |
+ Search files in <path> if given, or the current directory if omitted, |
|
| 75 |
+ recursively for OpenGL API names and print them sorted on location, |
|
| 76 |
+ support, and name, in a list. |
|
| 77 |
+ audit-tree [<path>] |
|
| 78 |
+ Search files in <path> if given, or the current directory if omitted, |
|
| 79 |
+ recursively for OpenGL API names and print them sorted on support, name, |
|
| 80 |
+ and location, in a tree. |
|
| 81 |
+ |
|
| 82 |
+Environment variables: |
|
| 83 |
+ GLREGISTRY_CACHE |
|
| 84 |
+ The directory to cache files in. Defaults to `$XDG_CACHE_HOME/glregistry` |
|
| 85 |
+ or, if `$XDG_CACHE_HOME` is not defined, `$HOME/.cache/glregistry`. |
|
| 86 |
+ GLREGISTRY_EDITOR |
|
| 87 |
+ The editor to use when opening files. Defaults to `$EDITOR` or, if |
|
| 88 |
+ `$EDITOR` is not defined, `editor` if it exists in `$PATH`, else `vi`. The |
|
| 89 |
+ value is interpreted by the shell. |
|
| 90 |
+ GLREGISTRY_PAGER |
|
| 91 |
+ The pager to use when viewing output. Defaults to `$PAGER` or, if `$PAGER` |
|
| 92 |
+ is not defined, `pager` if it exists in `$PATH`, else `less` . The value is |
|
| 93 |
+ interpreted by the shell. If the `$LESS` environment variable is unset, it |
|
| 94 |
+ is set to `FR`. |
|
| 95 |
+""" |
|
| 96 |
+ |
|
| 97 |
+ |
|
| 98 |
+## Imports |
|
| 99 |
+import os |
|
| 100 |
+import sys |
|
| 101 |
+import collections |
|
| 102 |
+import re |
|
| 103 |
+import subprocess |
|
| 104 |
+import shutil |
|
| 105 |
+import shlex |
|
| 106 |
+import urllib.request |
|
| 107 |
+import docopt |
|
| 108 |
+import lxml.etree |
|
| 109 |
+ |
|
| 110 |
+ |
|
| 111 |
+## Constants |
|
| 112 |
+REGISTRY_URL = 'https://registry.khronos.org/OpenGL/' |
|
| 113 |
+XML_PATH = 'xml/gl.xml' |
|
| 114 |
+REGEX = r'\b(gl|GL_)[0-9A-Z][0-9A-Za-z_]+\b' |
|
| 115 |
+EXCLUDE_DIRS = ['.?*', '_*'] |
|
| 116 |
+EXCLUDE_FILES = ['README*', 'TODO*'] |
|
| 117 |
+INDENT = 2 |
|
| 118 |
+ENV_XDG = lambda var, default: os.environ.get( |
|
| 119 |
+ f'GLREGISTRY_{var}',
|
|
| 120 |
+ os.path.join( |
|
| 121 |
+ os.environ.get( |
|
| 122 |
+ f'XDG_{var}_HOME',
|
|
| 123 |
+ os.path.expanduser(default), |
|
| 124 |
+ ), |
|
| 125 |
+ 'glregistry', |
|
| 126 |
+ ), |
|
| 127 |
+) |
|
| 128 |
+ENV_PRG = lambda var, default: os.environ.get( |
|
| 129 |
+ f'GLREGISTRY_{var}',
|
|
| 130 |
+ os.environ.get( |
|
| 131 |
+ var, |
|
| 132 |
+ shutil.which(var.lower()) or default, |
|
| 133 |
+ ), |
|
| 134 |
+) |
|
| 135 |
+CACHE = ENV_XDG('CACHE', os.path.join('~', '.cache'))
|
|
| 136 |
+EDITOR = ENV_PRG('EDITOR', 'vi')
|
|
| 137 |
+PAGER = ENV_PRG('PAGER', 'less')
|
|
| 138 |
+LESS = os.environ.get('LESS', 'FR')
|
|
| 139 |
+IN = lambda a, v, s: f"contains(concat('{s}',@{a},'{s}'),'{s}{v}{s}')"
|
|
| 140 |
+MAYBE = lambda a, v: f"(@{a}='{v}' or not(@{a}))"
|
|
| 141 |
+TYPES = "/registry/types" |
|
| 142 |
+ENUMS = "/registry/enums" |
|
| 143 |
+COMMANDS = "/registry/commands" |
|
| 144 |
+CATEGORY_ATTRIBS = {
|
|
| 145 |
+ 'VERSION': [ |
|
| 146 |
+ "/registry/feature[@api='gl']", |
|
| 147 |
+ 'number', |
|
| 148 |
+ ], |
|
| 149 |
+ 'EXTENSION': [ |
|
| 150 |
+ f"/registry/extensions/extension[{IN('supported','gl','|')}]",
|
|
| 151 |
+ 'name', |
|
| 152 |
+ ], |
|
| 153 |
+} |
|
| 154 |
+REQUIRE = f"require[{MAYBE('api','gl')} and {MAYBE('profile','core')}]"
|
|
| 155 |
+ |
|
| 156 |
+ |
|
| 157 |
+## Helpers |
|
| 158 |
+ |
|
| 159 |
+ |
|
| 160 |
+### `log` |
|
| 161 |
+def log(*args, **kwargs): |
|
| 162 |
+ print(*args, file=sys.stderr, flush=True, **kwargs) |
|
| 163 |
+ |
|
| 164 |
+ |
|
| 165 |
+### `edit` |
|
| 166 |
+def edit(paths): |
|
| 167 |
+ if EDITOR and sys.stdout.isatty(): |
|
| 168 |
+ args = ' '.join([EDITOR, *map(shlex.quote, paths)]) |
|
| 169 |
+ subprocess.run(args, shell=True) |
|
| 170 |
+ else: |
|
| 171 |
+ for path in paths: |
|
| 172 |
+ with open(path) as f: |
|
| 173 |
+ shutil.copyfileobj(f, sys.stdout) |
|
| 174 |
+ |
|
| 175 |
+ |
|
| 176 |
+### `page` |
|
| 177 |
+def page(lines): |
|
| 178 |
+ lines = ''.join(f'{line}\n' for line in lines)
|
|
| 179 |
+ if lines and PAGER and sys.stdout.isatty(): |
|
| 180 |
+ args = f'LESS={shlex.quote(LESS)} {PAGER}'
|
|
| 181 |
+ subprocess.run(args, shell=True, text=True, input=lines) |
|
| 182 |
+ else: |
|
| 183 |
+ sys.stdout.write(lines) |
|
| 184 |
+ |
|
| 185 |
+ |
|
| 186 |
+### `indentjoin` |
|
| 187 |
+def indentjoin(indent, sep, parts): |
|
| 188 |
+ return ' ' * INDENT * indent + sep.join(map(str, parts)) |
|
| 189 |
+ |
|
| 190 |
+ |
|
| 191 |
+### `removeprefix` |
|
| 192 |
+def removeprefix(prefix, string): |
|
| 193 |
+ if string.startswith(prefix): |
|
| 194 |
+ return string[len(prefix):] |
|
| 195 |
+ return string |
|
| 196 |
+ |
|
| 197 |
+ |
|
| 198 |
+### `download` |
|
| 199 |
+def download(path, exit_on_failure=True): |
|
| 200 |
+ remote = urllib.parse.urljoin(REGISTRY_URL, path) |
|
| 201 |
+ local = os.path.join (CACHE, path) |
|
| 202 |
+ if not os.path.exists(local): |
|
| 203 |
+ try: |
|
| 204 |
+ log(f"Downloading '{path}' ... ", end='')
|
|
| 205 |
+ with urllib.request.urlopen(remote) as response: |
|
| 206 |
+ os.makedirs(os.path.dirname(local), exist_ok=True) |
|
| 207 |
+ with open(local, 'wb') as f: |
|
| 208 |
+ shutil.copyfileobj(response, f) |
|
| 209 |
+ except urllib.error.URLError as error: |
|
| 210 |
+ log(error.reason) |
|
| 211 |
+ if exit_on_failure: |
|
| 212 |
+ exit(1) |
|
| 213 |
+ else: |
|
| 214 |
+ log(response.reason) |
|
| 215 |
+ return local |
|
| 216 |
+ |
|
| 217 |
+ |
|
| 218 |
+### `grep` |
|
| 219 |
+def grep( |
|
| 220 |
+ path=None, |
|
| 221 |
+ regex=REGEX, |
|
| 222 |
+ exclude_dirs=EXCLUDE_DIRS, |
|
| 223 |
+ exclude_files=EXCLUDE_FILES, |
|
| 224 |
+ silent=False, |
|
| 225 |
+): |
|
| 226 |
+ path = path if path else '.' |
|
| 227 |
+ cmd = ['grep', '-EIrno'] |
|
| 228 |
+ exclude_dirs = [f'--exclude-dir={exclude}' for exclude in exclude_dirs]
|
|
| 229 |
+ exclude_files = [f'--exclude={exclude}' for exclude in exclude_files]
|
|
| 230 |
+ process = subprocess.run( |
|
| 231 |
+ [*cmd, *exclude_dirs, *exclude_files, regex, path], |
|
| 232 |
+ stdout=subprocess.PIPE, |
|
| 233 |
+ stderr=subprocess.DEVNULL if silent else None, |
|
| 234 |
+ text=True, |
|
| 235 |
+ ) |
|
| 236 |
+ for string in process.stdout.splitlines(): |
|
| 237 |
+ string = removeprefix(f'.{os.path.sep}', string)
|
|
| 238 |
+ file, line, name = string.split(':', 2)
|
|
| 239 |
+ line = int(line) |
|
| 240 |
+ yield file, line, name |
|
| 241 |
+ |
|
| 242 |
+ |
|
| 243 |
+## Commands |
|
| 244 |
+ |
|
| 245 |
+ |
|
| 246 |
+### `xml_` |
|
| 247 |
+def xml_(): |
|
| 248 |
+ return lxml.etree.parse(download(XML_PATH)) |
|
| 249 |
+ |
|
| 250 |
+ |
|
| 251 |
+### `xml` |
|
| 252 |
+def xml(): |
|
| 253 |
+ return [download(XML_PATH)] |
|
| 254 |
+ |
|
| 255 |
+ |
|
| 256 |
+### `ext_` |
|
| 257 |
+def ext_(extension): |
|
| 258 |
+ prefix, vendor, name = extension.split('_', 2)
|
|
| 259 |
+ if prefix != 'GL': |
|
| 260 |
+ log("Extension names must start with 'GL_'.")
|
|
| 261 |
+ exit(1) |
|
| 262 |
+ return f'extensions/{vendor}/{vendor}_{name}.txt'
|
|
| 263 |
+ |
|
| 264 |
+ |
|
| 265 |
+### `ext` |
|
| 266 |
+def ext(extension): |
|
| 267 |
+ return [download(ext_(extension))] |
|
| 268 |
+ |
|
| 269 |
+ |
|
| 270 |
+### `exts` |
|
| 271 |
+def exts(xml): |
|
| 272 |
+ category, attrib = CATEGORY_ATTRIBS['EXTENSION'] |
|
| 273 |
+ exts = xml.xpath(f"{category}/@{attrib}")
|
|
| 274 |
+ return sorted(exts) |
|
| 275 |
+ |
|
| 276 |
+ |
|
| 277 |
+### `exts_download` |
|
| 278 |
+def exts_download(xml): |
|
| 279 |
+ for ext in exts(xml): |
|
| 280 |
+ download(ext_(ext), exit_on_failure=False) |
|
| 281 |
+ return [] |
|
| 282 |
+ |
|
| 283 |
+ |
|
| 284 |
+### `exts_all` |
|
| 285 |
+def exts_all(xml, name): |
|
| 286 |
+ # exts_download(xml) |
|
| 287 |
+ exts_all = set( |
|
| 288 |
+ 'GL_' + os.path.splitext(os.path.basename(file))[0] |
|
| 289 |
+ for name in set([ |
|
| 290 |
+ name, |
|
| 291 |
+ removeprefix('gl', name),
|
|
| 292 |
+ removeprefix('GL_', name),
|
|
| 293 |
+ ]) |
|
| 294 |
+ for file, *_ in |
|
| 295 |
+ grep(os.path.join(CACHE, 'extensions'), rf'\b{name}\b', [], [])
|
|
| 296 |
+ ) |
|
| 297 |
+ return sorted(exts_all) |
|
| 298 |
+ |
|
| 299 |
+ |
|
| 300 |
+### `type` |
|
| 301 |
+def type(xml, type): |
|
| 302 |
+ return [xml.xpath(f"string({TYPES}/type/name[text()='{type}']/..)")]
|
|
| 303 |
+ |
|
| 304 |
+ |
|
| 305 |
+### `value` |
|
| 306 |
+def value(xml, enum): |
|
| 307 |
+ return xml.xpath(f"{ENUMS}/enum[@name='{enum}']/@value")
|
|
| 308 |
+ |
|
| 309 |
+ |
|
| 310 |
+### `enum` |
|
| 311 |
+def enum(xml, value): |
|
| 312 |
+ enum = xml.xpath(f"{ENUMS}/enum[@value='{value}']/@name")
|
|
| 313 |
+ return sorted(enum) |
|
| 314 |
+ |
|
| 315 |
+ |
|
| 316 |
+### `supports` |
|
| 317 |
+def supports(xml, name): |
|
| 318 |
+ category, attrib = CATEGORY_ATTRIBS['EXTENSION'] |
|
| 319 |
+ if xml.xpath(f"{category}[@{attrib}='{name}']"):
|
|
| 320 |
+ return ['EXTENSION'] |
|
| 321 |
+ supports_ = [ |
|
| 322 |
+ support |
|
| 323 |
+ for category, attrib in CATEGORY_ATTRIBS.values() |
|
| 324 |
+ for support in |
|
| 325 |
+ xml.xpath(f"{category}/{REQUIRE}/*[@name='{name}']/../../@{attrib}")
|
|
| 326 |
+ ] |
|
| 327 |
+ return sorted(supports_) |
|
| 328 |
+ |
|
| 329 |
+ |
|
| 330 |
+### `names` |
|
| 331 |
+def names(xml, support=None): |
|
| 332 |
+ if support in CATEGORY_ATTRIBS.keys(): |
|
| 333 |
+ category_attribs = [ |
|
| 334 |
+ [category, ""] |
|
| 335 |
+ for category, _ in [CATEGORY_ATTRIBS[support]] |
|
| 336 |
+ ] |
|
| 337 |
+ elif support: |
|
| 338 |
+ category_attribs = [ |
|
| 339 |
+ [category, f"[@{attrib}='{support}']"]
|
|
| 340 |
+ for category, attrib in CATEGORY_ATTRIBS.values() |
|
| 341 |
+ ] |
|
| 342 |
+ else: |
|
| 343 |
+ category_attribs = [ |
|
| 344 |
+ [category, ""] |
|
| 345 |
+ for category, _ in CATEGORY_ATTRIBS.values() |
|
| 346 |
+ ] |
|
| 347 |
+ names = set( |
|
| 348 |
+ name |
|
| 349 |
+ for category, attrib in category_attribs |
|
| 350 |
+ for name in |
|
| 351 |
+ xml.xpath(f"{category}{attrib}/{REQUIRE}/*/@name")
|
|
| 352 |
+ ) |
|
| 353 |
+ return sorted(names) |
|
| 354 |
+ |
|
| 355 |
+ |
|
| 356 |
+### `groups` |
|
| 357 |
+def groups(xml, enum=None): |
|
| 358 |
+ name = f"[@name='{enum}']" if enum else ""
|
|
| 359 |
+ return sorted(set( |
|
| 360 |
+ group |
|
| 361 |
+ for groups in xml.xpath(f"{ENUMS}/enum{name}/@group")
|
|
| 362 |
+ for group in groups.split(',')
|
|
| 363 |
+ )) |
|
| 364 |
+ |
|
| 365 |
+ |
|
| 366 |
+### `enums_` |
|
| 367 |
+def enums_(xml, group=None): |
|
| 368 |
+ group = f"[{IN('group',group,',')}]" if group else ""
|
|
| 369 |
+ enums_ = collections.defaultdict(list) |
|
| 370 |
+ for enum in xml.xpath(f"{ENUMS}/enum{group}/@name"):
|
|
| 371 |
+ supports_ = supports(xml, enum) |
|
| 372 |
+ if supports_: |
|
| 373 |
+ enums_[tuple(supports_)].append(enum) |
|
| 374 |
+ return enums_ |
|
| 375 |
+ |
|
| 376 |
+ |
|
| 377 |
+### `enums` |
|
| 378 |
+def enums(xml, group=None): |
|
| 379 |
+ enums = [ |
|
| 380 |
+ enum |
|
| 381 |
+ for _, enums in enums_(xml, group).items() |
|
| 382 |
+ for enum in enums |
|
| 383 |
+ ] |
|
| 384 |
+ return sorted(enums) |
|
| 385 |
+ |
|
| 386 |
+ |
|
| 387 |
+### `enums_tree` |
|
| 388 |
+def enums_tree(xml, group=None): |
|
| 389 |
+ for supports, enums in sorted(enums_(xml, group).items()): |
|
| 390 |
+ yield indentjoin(0, ',', supports) |
|
| 391 |
+ for enum in sorted(enums): |
|
| 392 |
+ yield indentjoin(1, '', [enum]) |
|
| 393 |
+ |
|
| 394 |
+ |
|
| 395 |
+### `params_` |
|
| 396 |
+def params_(xml, group=None): |
|
| 397 |
+ group = f"[@group='{group}']" if group else ""
|
|
| 398 |
+ counts = collections.defaultdict(int) |
|
| 399 |
+ params_ = collections.defaultdict(lambda: collections.defaultdict(list)) |
|
| 400 |
+ for xmlcommand in xml.xpath(f"{COMMANDS}/command/param{group}/.."):
|
|
| 401 |
+ command = xmlcommand.xpath(f"string(proto/name)") |
|
| 402 |
+ supports_ = supports(xml, command) |
|
| 403 |
+ if supports_: |
|
| 404 |
+ for xmlparam in xmlcommand.xpath(f"param{group}"):
|
|
| 405 |
+ param = xmlparam.xpath(f"string(name)") |
|
| 406 |
+ params_[param][tuple(supports_)].append(command) |
|
| 407 |
+ counts[param] -= 1 |
|
| 408 |
+ return {
|
|
| 409 |
+ (count, param): params_[param] |
|
| 410 |
+ for param, count in counts.items() |
|
| 411 |
+ } |
|
| 412 |
+ |
|
| 413 |
+ |
|
| 414 |
+### `params` |
|
| 415 |
+def params(xml, group=None): |
|
| 416 |
+ params = [param for (_, param), _ in params_(xml, group).items()] |
|
| 417 |
+ return sorted(params) |
|
| 418 |
+ |
|
| 419 |
+ |
|
| 420 |
+### `params_tree` |
|
| 421 |
+def params_tree(xml, group=None): |
|
| 422 |
+ for (count, param), occurences in sorted(params_(xml, group).items()): |
|
| 423 |
+ yield indentjoin(0, ':', [ |
|
| 424 |
+ param, |
|
| 425 |
+ -count, |
|
| 426 |
+ ]) |
|
| 427 |
+ for supports_, commands in sorted(occurences.items()): |
|
| 428 |
+ yield indentjoin(1, ',', supports_) |
|
| 429 |
+ for command in sorted(commands): |
|
| 430 |
+ yield indentjoin(2, '', [command]) |
|
| 431 |
+ |
|
| 432 |
+ |
|
| 433 |
+### `audit_` |
|
| 434 |
+def audit_(xml, path=None): |
|
| 435 |
+ audit_ = collections.defaultdict(lambda: collections.defaultdict(list)) |
|
| 436 |
+ for file, line, name in grep(path): |
|
| 437 |
+ supports_ = supports(xml, name) |
|
| 438 |
+ if not supports_: |
|
| 439 |
+ supports_ = ['UNSUPPORTED'] |
|
| 440 |
+ audit_[tuple(supports_)][name].append([file, line]) |
|
| 441 |
+ return audit_ |
|
| 442 |
+ |
|
| 443 |
+ |
|
| 444 |
+### `audit` |
|
| 445 |
+def audit(xml, path=None): |
|
| 446 |
+ for file, line, supports, name in sorted( |
|
| 447 |
+ [file, line, supports, name] |
|
| 448 |
+ for supports, names in audit_(xml, path).items() |
|
| 449 |
+ for name, locations in names.items() |
|
| 450 |
+ for file, line in locations |
|
| 451 |
+ ): |
|
| 452 |
+ yield indentjoin(0, ':', [ |
|
| 453 |
+ file, |
|
| 454 |
+ line, |
|
| 455 |
+ indentjoin(0, ',', supports), |
|
| 456 |
+ name |
|
| 457 |
+ ]) |
|
| 458 |
+ |
|
| 459 |
+ |
|
| 460 |
+### `audit_tree` |
|
| 461 |
+def audit_tree(xml, path=None): |
|
| 462 |
+ for supports, names in sorted(audit_(xml, path).items()): |
|
| 463 |
+ yield indentjoin(0, ',', supports) |
|
| 464 |
+ for name, locations in sorted(names.items()): |
|
| 465 |
+ yield indentjoin(1, '', [name]) |
|
| 466 |
+ for file, line in sorted(locations): |
|
| 467 |
+ yield indentjoin(2, ':', [ |
|
| 468 |
+ file, |
|
| 469 |
+ line, |
|
| 470 |
+ ]) |
|
| 471 |
+ |
|
| 472 |
+ |
|
| 473 |
+## Main |
|
| 474 |
+def main(): |
|
| 475 |
+ args = docopt.docopt(__doc__) |
|
| 476 |
+ if args['xml']: edit(xml ()) |
|
| 477 |
+ if args['xml-path']: page(xml ()) |
|
| 478 |
+ if args['ext']: edit(ext (args['<extension>'])) |
|
| 479 |
+ if args['ext-path']: page(ext (args['<extension>'])) |
|
| 480 |
+ if args['exts']: page(exts (xml_())) |
|
| 481 |
+ if args['exts-download']: page(exts_download(xml_())) |
|
| 482 |
+ if args['exts-all']: page(exts_all (xml_(), args['<name>'])) |
|
| 483 |
+ if args['type']: page(type (xml_(), args['<type>'])) |
|
| 484 |
+ if args['value']: page(value (xml_(), args['<enum>'])) |
|
| 485 |
+ if args['enum']: page(enum (xml_(), args['<value>'])) |
|
| 486 |
+ if args['supports']: page(supports (xml_(), args['<name>'])) |
|
| 487 |
+ if args['names']: page(names (xml_(), args['<support>'])) |
|
| 488 |
+ if args['groups']: page(groups (xml_(), args['<enum>'])) |
|
| 489 |
+ if args['enums']: page(enums (xml_(), args['<group>'])) |
|
| 490 |
+ if args['enums-tree']: page(enums_tree (xml_(), args['<group>'])) |
|
| 491 |
+ if args['params']: page(params (xml_(), args['<group>'])) |
|
| 492 |
+ if args['params-tree']: page(params_tree (xml_(), args['<group>'])) |
|
| 493 |
+ if args['audit']: page(audit (xml_(), args['<path>'])) |
|
| 494 |
+ if args['audit-tree']: page(audit_tree (xml_(), args['<path>'])) |
|
| 495 |
+ |
|
| 496 |
+ |
|
| 497 |
+if __name__ == '__main__': |
|
| 498 |
+ main() |