Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ examples/screenshots/diffs
docs/gallery/
docs/_static/*.whl
docs/sg_execution_times.rst
.zed

# Byte-compiled / optimized / DLL files
__pycache__/
Expand Down
119 changes: 119 additions & 0 deletions apigen/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
# wgpu APIGEN

## Introduction

The purpose of this code is to generate the wgpu-py base API, and the API of its backends.
The API is derived from the official WebGPU spec (webgpu.idl), but changed a bit to make it
fit Python better.

The API is stored in a structured data format (see below). This makes it straightforward to produce,
and also to generate the actual Python API from it.

Documentation is also present for every object in the API.

Each backend can define additional API surface, but uses suffixes for anything that is added.


## Code organization

The apigen consists of multiple "actions". Each such action is represented by a module,
which has a function with the same name that performs the action. These modules can also
be executed as a script, which is handy during development.

The names of the actions are typically "verb-noun". Call me crazy but I named them such that
when sorted alphabetically, the order of the actions is correct :)

There is also one utility module that all modules use.

There is a CLI for ease of use, and to run the whole codegen pipeline with one command.


## The steps that this code offers

* download_idl: download the latest webgpu.idl, this is only done manually from time to time.
* extract_idl: parse the IDL to produce 'wgpu_idl.json'.
* format_json: formats the static definitions.
* merge_json: combines multiple definitions files into 'wgpu_api.json'.
* patch_api: produce the classes and functions in _classes.py and all backends.
* write_api: produce API for flags, enums and structs.


## The API format structure

The API is stored in a "json-able" data structure that can be easily loaded and manipulated.
This allows generating an API definition that is precise and sort of human readable, plus
we can easily combine multiple structures, e.g. diff definitions, backend-specific additions,
and docs from multiple sources.

The structure has some two dozen leaf nodes and looks a bit like this:

```
{
"flags": {
"SomeFlag": {
"doc": "",
"fields": {
"SOME_FIELD": {
"doc": "",
"value": 1
}
}
}
},

"enums": {
"SomeEnum": {
"doc": "",
"fields": {
"some_field": {
"doc": "",
"value": "some-field"
}
}
}
},

"structs": {
"SomeStruct": {
"doc": "",
"fields": {
"some_field": {
"doc": "",
"type": "int",
"default": "0"
}
}
}
},

"classes": {
"GPUSomeClass": {
"doc": "",
"bases": ["GPUObject"],
"properties": {
"some_prop": {
"doc": "",
"type": "int"
}
},
"methods": {
"some_method": {
"doc": "",
"returns": {
"doc": "",
"type": "None"
},
"args": {
"some_arg": {
"doc": "",
"type": "",
"default": ""
}
}
}
}
}
}
}

```
19 changes: 19 additions & 0 deletions apigen/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
"""Code to generate the wgpu-py API."""

from download_idl import download_idl
from extract_idl import extract_idl
from format_json import format_json
from merge_json import merge_json
from patch_api import patch_api
from write_api import write_api
from write_js_api import write_js_api

actions = [
download_idl,
extract_idl,
format_json,
merge_json,
patch_api,
write_api,
write_js_api,
]
64 changes: 64 additions & 0 deletions apigen/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
"""
The entrypoint / script to apply automatic patches to the code.
See README.md for more information.
"""

import os
import sys
from argparse import ArgumentParser

from apigen import actions

parser = ArgumentParser(description="apigen - run code generation and patching.")

for func in actions:
parser.add_argument(
"--" + func.__name__.replace("_", "-"),
action="store_true",
dest=func.__name__,
help=func.__doc__.strip().splitlines()[0],
)

parser.add_argument(
"--all",
action="store_true",
dest="all",
help="Run all steps of the codegen (except download).",
)

parser.add_argument(
"--doc",
action="store",
dest="doc",
help="Show doc on specific action.",
)

# Little trick to allow running this file as a script
sys.path.insert(0, os.path.abspath(os.path.join(__file__, "..", "..")))


if __name__ == "__main__":
args = parser.parse_args()
enabled = [k for k, v in vars(args).items() if v is True]

if args.doc is not None:
the_action = args.doc.replace("-", "_").strip("_")
for func in actions:
if func.__name__ == the_action:
print(f"--- More info for {func.__name__}() ---\n")
print(func.__doc__)
break
else:
sys.exit(f"Unknown action {the_action!r}")
elif not enabled:
parser.print_help()
elif "all" in enabled:
for func in actions:
if func.__name__ == "download":
continue
func()
else:
for name in enabled:
for func in actions:
if name == func.__name__:
func()
Loading
Loading