Skip to content

Commit 75825c0

Browse files
committed
Add C structs bindings to gdapi.pxd
1 parent 55f4baf commit 75825c0

File tree

7 files changed

+106
-18
lines changed

7 files changed

+106
-18
lines changed

β€Žscripts/extension_api_parser/api.pyβ€Ž

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from .type import (
1010
TYPES_DB,
1111
TypeInUse,
12-
TypeSpec,
12+
register_variant_in_types_db,
1313
register_builtins_in_types_db,
1414
register_classes_in_types_db,
1515
register_global_enums_in_types_db,
@@ -221,6 +221,7 @@ def parse_extension_api_json(path: Path, build_config: BuildConfig) -> Extension
221221
# This is the kind-of ugly part where we register in a global dict the types
222222
# we've just parsed (so that they could be lazily retreived from all the
223223
# `TypeInUse` that reference them)
224+
register_variant_in_types_db(api.variant_size)
224225
register_builtins_in_types_db(api.builtins)
225226
register_classes_in_types_db(api.classes)
226227
register_global_enums_in_types_db(api.global_enums)

β€Žscripts/extension_api_parser/builtins.pyβ€Ž

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import enum
2-
from typing import Dict, Optional, List
2+
from typing import Dict, Optional, List, Tuple
33
from dataclasses import dataclass
44
from string import ascii_uppercase
55

@@ -250,6 +250,7 @@ def parse(cls, item: dict) -> "BuiltinEnumSpec":
250250
@dataclass
251251
class BuiltinSpec:
252252
name: str
253+
c_struct_name: str
253254
original_name: str
254255
is_scalar: bool
255256
size: Optional[int]
@@ -264,6 +265,17 @@ class BuiltinSpec:
264265
variant_type_name: str
265266
enums: List[BuiltinEnumSpec]
266267

268+
@property
269+
def c_struct_members(self) -> List[Tuple[str, TypeInUse]]:
270+
struct_members = [m for m in self.members if m.offset is not None]
271+
if struct_members:
272+
# Sanity check
273+
assert sum(m.type.size for m in struct_members) == self.size
274+
return struct_members
275+
else:
276+
# Opaque structure
277+
return []
278+
267279
@classmethod
268280
def parse(cls, item: dict) -> "BuiltinSpec":
269281
item["is_scalar"] = item["name"] in ("Nil", "bool", "int", "float")
@@ -276,6 +288,7 @@ def parse(cls, item: dict) -> "BuiltinSpec":
276288
snake += c
277289
item["variant_type_name"] = f"GDNATIVE_VARIANT_TYPE_{snake.upper()}"
278290
item.setdefault("original_name", item["name"])
291+
item.setdefault("c_struct_name", f"C{item['original_name']}")
279292
item.setdefault("indexing_return_type", None)
280293
item.setdefault("methods", [])
281294
item.setdefault("members", [])
@@ -287,6 +300,7 @@ def parse(cls, item: dict) -> "BuiltinSpec":
287300
return cls(
288301
original_name=item["original_name"],
289302
name=correct_type_name(item["name"]),
303+
c_struct_name=item["c_struct_name"],
290304
is_scalar=item["is_scalar"],
291305
size=item["size"],
292306
indexing_return_type=item["indexing_return_type"],

β€Žscripts/extension_api_parser/type.pyβ€Ž

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
@dataclass
1818
class TypeSpec:
19+
size: int
1920
# Type used within Godot `extension_api.json`
2021
gdapi_type: str
2122
# Type used for PEP 484 Python typing
@@ -58,11 +59,9 @@ def __post_init__(self):
5859

5960

6061
TYPES_DB: Dict[str, TypeSpec] = {
61-
"Variant": TypeSpec(
62-
gdapi_type="Variant", c_type="CVariant", cy_type="object", py_type="GDAny", is_builtin=True
63-
),
6462
# Types marked as `meta` are used in the classes method args/return types
6563
"meta:int8": TypeSpec(
64+
size=1,
6665
gdapi_type="int8",
6766
c_type="int8_t",
6867
cy_type="int8_t",
@@ -72,6 +71,7 @@ def __post_init__(self):
7271
is_stack_only=True,
7372
),
7473
"meta:int16": TypeSpec(
74+
size=2,
7575
gdapi_type="int16",
7676
c_type="int16_t",
7777
cy_type="int16_t",
@@ -81,6 +81,7 @@ def __post_init__(self):
8181
is_stack_only=True,
8282
),
8383
"meta:int32": TypeSpec(
84+
size=4,
8485
gdapi_type="int32",
8586
c_type="int32_t",
8687
cy_type="int32_t",
@@ -90,6 +91,7 @@ def __post_init__(self):
9091
is_stack_only=True,
9192
),
9293
"meta:int64": TypeSpec(
94+
size=8,
9395
gdapi_type="int64",
9496
c_type="int64_t",
9597
cy_type="int64_t",
@@ -99,6 +101,7 @@ def __post_init__(self):
99101
is_stack_only=True,
100102
),
101103
"meta:uint8": TypeSpec(
104+
size=1,
102105
gdapi_type="uint8",
103106
c_type="uint8_t",
104107
cy_type="uint8_t",
@@ -108,6 +111,7 @@ def __post_init__(self):
108111
is_stack_only=True,
109112
),
110113
"meta:uint16": TypeSpec(
114+
size=2,
111115
gdapi_type="uint16",
112116
c_type="uint16_t",
113117
cy_type="uint16_t",
@@ -117,6 +121,7 @@ def __post_init__(self):
117121
is_stack_only=True,
118122
),
119123
"meta:uint32": TypeSpec(
124+
size=4,
120125
gdapi_type="uint32",
121126
c_type="uint32_t",
122127
cy_type="uint32_t",
@@ -126,6 +131,7 @@ def __post_init__(self):
126131
is_stack_only=True,
127132
),
128133
"meta:uint64": TypeSpec(
134+
size=8,
129135
gdapi_type="uint64",
130136
c_type="uint64_t",
131137
cy_type="uint64_t",
@@ -136,6 +142,7 @@ def __post_init__(self):
136142
),
137143
# int is always 8bytes long
138144
"int": TypeSpec(
145+
size=8,
139146
gdapi_type="int",
140147
c_type="uint64_t",
141148
cy_type="uint64_t",
@@ -145,6 +152,7 @@ def __post_init__(self):
145152
is_stack_only=True,
146153
),
147154
"meta:float": TypeSpec(
155+
size=4,
148156
gdapi_type="float",
149157
c_type="float",
150158
cy_type="float",
@@ -154,6 +162,7 @@ def __post_init__(self):
154162
is_stack_only=True,
155163
),
156164
"meta:double": TypeSpec(
165+
size=8,
157166
gdapi_type="double",
158167
c_type="double",
159168
cy_type="double",
@@ -166,6 +175,17 @@ def __post_init__(self):
166175
}
167176

168177

178+
def register_variant_in_types_db(variant_size: int) -> None:
179+
TYPES_DB["Variant"] = TypeSpec(
180+
size=variant_size,
181+
gdapi_type="Variant",
182+
c_type="CVariant",
183+
cy_type="object",
184+
py_type="GDAny",
185+
is_builtin=True,
186+
)
187+
188+
169189
def register_builtins_in_types_db(builtins: Iterable["BuiltinSpec"]) -> None:
170190
for spec in builtins:
171191
if spec.name == "Nil":
@@ -175,6 +195,7 @@ def register_builtins_in_types_db(builtins: Iterable["BuiltinSpec"]) -> None:
175195
continue
176196
elif spec.name == "bool":
177197
ts = TypeSpec(
198+
size=spec.size,
178199
gdapi_type=spec.original_name,
179200
py_type="bool",
180201
# Cython provide a `bint` type for boolean, however it is defined in C as
@@ -199,6 +220,7 @@ def register_builtins_in_types_db(builtins: Iterable["BuiltinSpec"]) -> None:
199220
ts = replace(TYPES_DB[f"meta:double"], gdapi_type=spec.original_name)
200221
else:
201222
ts = TypeSpec(
223+
size=spec.size,
202224
gdapi_type=spec.original_name,
203225
py_type=spec.name,
204226
c_type=f"C{spec.name}",
@@ -213,15 +235,19 @@ def register_builtins_in_types_db(builtins: Iterable["BuiltinSpec"]) -> None:
213235
def register_classes_in_types_db(classes: Iterable["ClassSpec"]) -> None:
214236
for spec in classes:
215237
ts = TypeSpec(
238+
# Class instance is always manipulated as a pointer,
239+
# hence `size` field is never supposed to be used here
240+
size=0, # Dummy value
216241
gdapi_type=spec.original_name,
217242
py_type=spec.name,
218-
c_type="Object",
219-
cy_type="Object",
243+
c_type="GDNativeObjectPtr",
244+
cy_type="GDNativeObjectPtr",
220245
variant_type_name="GDNATIVE_VARIANT_TYPE_OBJECT",
221246
)
222247
TYPES_DB[ts.gdapi_type] = ts
223248
for e in spec.enums:
224249
ts = TypeSpec(
250+
size=4,
225251
gdapi_type=f"enum::{spec.original_name}.{e.original_name}",
226252
py_type=f"{spec.name}.{e.name}",
227253
c_type="int",
@@ -237,6 +263,7 @@ def register_classes_in_types_db(classes: Iterable["ClassSpec"]) -> None:
237263
def register_global_enums_in_types_db(enums: Iterable["GlobalEnumSpec"]) -> None:
238264
for spec in enums:
239265
ts = TypeSpec(
266+
size=4,
240267
gdapi_type=f"enum::{spec.original_name}",
241268
py_type=spec.name,
242269
c_type="int",
@@ -269,7 +296,10 @@ def resolve(self) -> TypeSpec:
269296
return
270297

271298
def __getattr__(self, name: str):
272-
return getattr(self.resolve(), name)
299+
try:
300+
return getattr(self.resolve(), name)
301+
except AttributeError as exc:
302+
raise RuntimeError(f"Error in TypeSpec accessing: {exc}") from exc
273303

274304

275305
# ValueInUse is only used to create function argument's default value,

β€Žscripts/generate_tmpl.pyβ€Ž

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,12 @@ def make_jinja_env(import_dir: Path) -> Environment:
2222
return env
2323

2424

25+
def generate_gdapi_pxd(api: ExtensionApi) -> str:
26+
env = make_jinja_env(BASEDIR / "../src/godot/_hazmat")
27+
template = env.get_template("gdapi.pxd.j2")
28+
return template.render(api=api)
29+
30+
2531
def generate_gdapi_ptrs(api: ExtensionApi) -> str:
2632
env = make_jinja_env(BASEDIR / "../src/godot/_hazmat")
2733
template = env.get_template("gdapi_ptrs.pxi.j2")
@@ -47,6 +53,7 @@ def generate_builtins_pyi(api: ExtensionApi) -> str:
4753

4854

4955
OUTPUT_TO_FN = {
56+
"gdapi.pxd": generate_gdapi_pxd,
5057
"gdapi_ptrs.pxi": generate_gdapi_ptrs,
5158
"_builtins.pxd": generate_builtins_pxd,
5259
"_builtins.pyx": generate_builtins_pyx,

β€Žsrc/godot/_hazmat/gdapi.pxdβ€Ž renamed to β€Žsrc/godot/_hazmat/gdapi.pxd.j2β€Ž

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from libc.stdint cimport *
12
from godot._hazmat.gdnative_interface cimport GDNativeInterface
23

34

@@ -17,3 +18,22 @@ cdef extern from * nogil:
1718
"""
1819

1920
cdef const GDNativeInterface *pythonscript_gdapi
21+
22+
23+
cdef struct Variant:
24+
char _gd_data[{{ api.variant_size }}]
25+
26+
27+
{% for spec in api["builtins"] if not spec.is_scalar %}
28+
cdef struct {{ spec.c_struct_name }}:
29+
{% set c_struct_members = spec.c_struct_members %}
30+
{% if c_struct_members %}
31+
{% for member in c_struct_members %}
32+
{{ member.type.c_type }} {{ member.name }}
33+
{% endfor %}
34+
{% else %}
35+
char _gd_data[{{ spec.size }}]
36+
{% endif %}
37+
38+
39+
{% endfor %}

β€Žsrc/godot/_hazmat/meson.buildβ€Ž

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,23 @@
33
##############################################################################
44

55

6-
foreach src: ['__init__.py', 'gdapi.pxd']
6+
foreach src: ['__init__.py']
77
install_data(
88
src,
99
install_dir: join_paths(python_site_packages_install_path, 'godot', '_hazmat'),
1010
)
1111
endforeach
1212

1313

14+
custom_target(
15+
output: 'gdapi.pxd',
16+
input: pxd_godot__hazmat_gdapi,
17+
command: copy_command,
18+
install: true,
19+
install_dir: join_paths(python_site_packages_install_path, 'godot', '_hazmat'),
20+
)
21+
22+
1423
custom_target(
1524
output: 'gdnative_interface.pxd',
1625
input: pxd_godot__hazmat_gdnative_interface,
@@ -50,10 +59,7 @@ pxi_gdapi_ptrs = custom_target(
5059
c_gdapi = custom_target(
5160
output : 'gdapi.c',
5261
input : [
53-
files(
54-
'gdapi.pyx',
55-
'gdapi.pxd',
56-
),
62+
files('gdapi.pyx'),
5763
pxi_gdapi_ptrs,
5864
pxds_godot__hazmat,
5965
],

β€Žsrc/meson.buildβ€Ž

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,6 @@ generate_tmpl_cmd = [
5353
# godot/_hazmat/gdnative_interface.pxd
5454
# └─ gdnative_interface.h
5555

56-
5756
# Convert the header file frome C to Cython
5857
pxd_godot__hazmat_gdnative_interface = custom_target(
5958
output: 'godot#_hazmat#gdnative_interface.pxd',
@@ -65,6 +64,19 @@ pxd_godot__hazmat_gdnative_interface = custom_target(
6564
)
6665

6766

67+
# godot/_hazmat/gdapi.pxd
68+
# └─ extension_api.json
69+
70+
pxd_godot__hazmat_gdapi = custom_target(
71+
output: 'godot#_hazmat#gdapi.pxd',
72+
input: [
73+
generate_tmpl_base_input,
74+
files('godot/_hazmat/gdapi.pxd.j2'),
75+
],
76+
command: generate_tmpl_cmd,
77+
)
78+
79+
6880
# godot/builtins.pxd
6981
# └─ extension_api.json
7082

@@ -84,10 +96,8 @@ pxd_godot__builtins = custom_target(
8496

8597
pxds_godot__hazmat = [
8698
pxd_godot__hazmat_gdnative_interface,
87-
files(
88-
'godot/_hazmat/__init__.pxd',
89-
'godot/_hazmat/gdapi.pxd',
90-
),
99+
pxd_godot__hazmat_gdapi,
100+
files('godot/_hazmat/__init__.pxd'),
91101
]
92102

93103

0 commit comments

Comments
Β (0)