diff --git a/.github/actions/provision-meos/action.yml b/.github/actions/provision-meos/action.yml index 7ce8beb..7c66f57 100644 --- a/.github/actions/provision-meos/action.yml +++ b/.github/actions/provision-meos/action.yml @@ -91,9 +91,9 @@ runs: echo "Catalog written: $CATALOG ($(wc -c < "$CATALOG") bytes)" # Optional: build and install libmeos with all families. Mirrors the green - # all-families recipe (MobilityDB meos-arrow.yml + the four JVM bindings): - # purge the runner's preinstalled PostgreSQL and use apt.postgresql.org - # PG17 so the vendored pgPointCloud PGXS build finds clang-19. + # all-families recipe used by the JVM bindings: purge the runner's + # preinstalled PostgreSQL and use apt.postgresql.org PG17 so the vendored + # pgPointCloud PGXS build finds clang-19. - name: Remove preinstalled PostgreSQL [prefer apt.postgresql.org] if: inputs.build-libmeos == 'true' shell: bash diff --git a/parser/extractors.py b/parser/extractors.py index 94a7aee..8d989de 100644 --- a/parser/extractors.py +++ b/parser/extractors.py @@ -19,37 +19,14 @@ def _canonical_spelling(ty) -> str: _BOOL_SPELLINGS = {"bool", "_Bool"} -# External ABI structs that appear in the MEOS public API only at the FFI -# boundary — the Arrow C Data Interface structs. They are forward-declared -# with no field layout and carry no MEOS semantics, so a pointer to one is -# ABI-identical to ``void *``. Emitting them as ``void *`` lets every binding's -# opaque-pointer-family handling (JNR ``Pointer``, cffi ``_ffi.CData``, Go -# ``unsafe.Pointer``, .NET ``IntPtr``, rust ``*mut c_void``) wrap them -# uniformly. The idiomatic Arrow bridge — allocating the struct and importing -# it through the language's Arrow library — lives in each binding's hand-written -# layer, keyed off the ``*_to_arrow`` / ``*_from_arrow`` function name, as with -# any other opaque-pointer-family value. -_EXTERNAL_OPAQUE_STRUCTS = ("ArrowSchema", "ArrowArray") - - -def _demote_external_opaque(spelling: str) -> str: - # Map a pointer to an external, layout-less ABI struct to the equivalent - # ``void``-pointer spelling, preserving const qualifiers and pointer depth. - for name in _EXTERNAL_OPAQUE_STRUCTS: - spelling = re.sub(rf"\bstruct\s+{name}\b", "void", spelling) - spelling = re.sub(rf"\b{name}\b", "void", spelling) - return spelling - - def find_unlisted_foreign_structs(idl) -> list: # A MEOS type is typedef'd, so its declared ``cType`` appears bare (``Pose # *``) in at least one signature; a foreign, forward-declared ABI struct is - # never typedef'd, so it only ever appears elaborated (``struct ArrowSchema - # *``). Any base name seen only in the elaborated form, and not already - # normalised to ``void *`` by ``_EXTERNAL_OPAQUE_STRUCTS``, is an external - # type the bindings handle divergently (permissive ones map it to a raw - # pointer, conservative ones skip it). Surface it so it is classified - # explicitly instead of silently diverging per binding. + # never typedef'd, so it only ever appears elaborated (``struct Foo *``). + # Any base name seen only in the elaborated form is an external type the + # bindings handle divergently (permissive ones map it to a raw pointer, + # conservative ones skip it). Surface it so it is classified explicitly + # instead of silently diverging per binding. elaborated, bare = set(), set() for fn in idl.get("functions", []): spellings = [p.get("cType") for p in fn.get("params", [])] @@ -61,7 +38,7 @@ def find_unlisted_foreign_structs(idl) -> list: if not base: continue (elaborated if re.search(r"\bstruct\b", sp) else bare).add(base) - return sorted(elaborated - bare - set(_EXTERNAL_OPAQUE_STRUCTS)) + return sorted(elaborated - bare) def _bool_norm(spelling: str) -> str: @@ -78,7 +55,7 @@ def _c_spelling(ty) -> str: # in play: # - PostgreSQL headers: ``typedef char bool`` -> spelling already ``"bool"`` # - Stub header: ``#define bool _Bool`` -> spelling is ``"_Bool"`` - return _demote_external_opaque(_bool_norm(ty.spelling)) + return _bool_norm(ty.spelling) # Canonical spellings of plain C scalars/builtins. @@ -136,8 +113,8 @@ def _canonical_c_spelling(ty) -> str: return "bool" preserved = _preserved_opaque(ty) if preserved is not None: - return _bool_norm(_demote_external_opaque(preserved)) - return _bool_norm(_demote_external_opaque(_canonical_spelling(ty))) + return _bool_norm(preserved) + return _bool_norm(_canonical_spelling(ty)) # ----------------------------------------------------------------------------- @@ -174,7 +151,6 @@ def _canonical_c_spelling(ty) -> str: "meos_quadbin.h": "QUADBIN", "meos_pointcloud.h": "POINTCLOUD", "meos_json.h": "JSON", - "meos_arrow.h": "ARROW", "meos_raster.h": "RASTER", } diff --git a/parser/parser.py b/parser/parser.py index b16dfc2..2fbb59d 100644 --- a/parser/parser.py +++ b/parser/parser.py @@ -104,7 +104,7 @@ def merge_meta(idl: dict, meta_path: Path) -> dict: # headers themselves are unguarded, but a handful of core-header declarations # are gated. Kept in sync with MobilityDB CMakeLists.txt's ``if(ALL) foreach``. _ALL_FAMILIES = ( - "ARROW", "CBUFFER", "H3", "JSON", "NPOINT", "POINTCLOUD", "POSE", + "CBUFFER", "H3", "JSON", "NPOINT", "POINTCLOUD", "POSE", "QUADBIN", "RASTER", "RGEO", ) diff --git a/run.py b/run.py index 43d711c..efaa851 100644 --- a/run.py +++ b/run.py @@ -171,14 +171,13 @@ def main(): idl, ngrp = attach_groups(idl, MEOS_SRC) print(f"[5/5] Attached {ngrp} doxygen @ingroup groups", file=sys.stderr) - # Surface any forward-declared external ABI struct pointer not yet - # normalised to void * (see parser.extractors._EXTERNAL_OPAQUE_STRUCTS), - # so a new one is classified explicitly instead of diverging per binding. + # Surface any forward-declared external ABI struct pointer in the API, so a + # new one is classified explicitly instead of diverging per binding. unlisted = find_unlisted_foreign_structs(idl) if unlisted: print(f" WARNING: unlisted external struct pointer(s) in the API: " - f"{', '.join(unlisted)} — add to _EXTERNAL_OPAQUE_STRUCTS to map " - f"them to void * uniformly across bindings", file=sys.stderr) + f"{', '.join(unlisted)} — classify them explicitly so bindings " + f"handle them uniformly", file=sys.stderr) # 6. Attach the temporal-covering descriptor (Parquet/Iceberg projection) print(f" Attaching temporal covering from {COVERING_PATH}...", diff --git a/tests/test_family.py b/tests/test_family.py index 5a8276d..7fb5bbe 100644 --- a/tests/test_family.py +++ b/tests/test_family.py @@ -22,7 +22,7 @@ OPTIONAL_FAMILIES = { "CBUFFER", "NPOINT", "POSE", "RGEO", "H3", - "QUADBIN", "POINTCLOUD", "JSON", "ARROW", "RASTER", + "QUADBIN", "POINTCLOUD", "JSON", "RASTER", } @@ -66,7 +66,7 @@ def test_each_optional_family_is_populated(self): # Hard guard: a healthy full-surface IDL populates every optional family; # an empty one means the header layout or the classifier regressed. present = {f["family"] for f in self.functions} - for family in OPTIONAL_FAMILIES - {"ARROW", "RASTER"}: + for family in OPTIONAL_FAMILIES - {"RASTER"}: self.assertIn(family, present, f"{family} unpopulated — classifier regression?") def test_families_are_known_labels(self):