MEOS-API is the root of the per-binding generator policy: it is the catalog producer, not a generated binding. Every other repo is a projection of this catalog.
Every MobilityDB language/surface binding is a pure projection of the MEOS-API catalog,
and each binding owns its own generator, in its own repo, in a canonical layout. The single
source of truth is the catalog this repo produces: output/meos-idl.json, generated from
the MEOS C headers.
run.py <meos/include> parses the MEOS public headers with libclang and emits
output/meos-idl.json: every function, struct, and enum with signatures, ownership, shape
(output arrays / nullability), recovered collapsed C types (bool/int64/Timestamp/…
that the preprocessor flattens to int), @ingroup groups, the @sqlfn SQL-name map, and
the portable bare-name aliases. The generator/ modules project the catalog onto the
language-agnostic service contracts (OpenAPI, MCP, the runtime server, the OGC Moving
Features projection) — the surfaces that need no foreign toolchain. Language bindings live in
their own repos and generate from this catalog.
MobilityDB pin
-> MEOS-API run.py -> output/meos-idl.json (+ libmeos.so built from the same pin)
-> JMEOS (jar) -> { MobilitySpark, MobilityFlink, MobilityKafka }
-> PyMEOS-CFFI -> PyMEOS
-> GoMEOS / MEOS.NET / meos-rs / MobilityDuck / MobilityNebula
The catalog is one run.py invocation over a set of MEOS headers. Two header sources give
different fidelity, and the choice matters:
- the installed headers (
cmake --installoutput) are self-contained —meos.his the generatedmeos_export.h, withpostgres_ext_defs.in.hspliced in place of the source tree's#include <postgres.h>— so libclang resolves every struct field to its real C type and byte offset. FFI bindings need this. - the source tree headers (
<checkout>/meos/include) parse without a build, but wrap the PostgreSQL types in stubs, so struct layouts are approximate.
Either way the Doxygen @ingroup groups and the @sqlfn SQL-name map are read from the
MobilityDB source checkout (meos/src, mobilitydb/src, mobilitydb/sql).
tools/provision-meos.sh is that recipe — configure, build, install, parse — in one script.
The provision-meos GitHub action calls it too, so the by-hand catalog and the CI catalog are
produced the same way and cannot drift. On Ubuntu, with the build/parse dependencies installed
(pip install -r requirements.txt, plus the packages the action's apt steps list):
MDB=~/src/MobilityDB # a checkout at the commit you are deriving from
# Full fidelity: build and install libmeos, then parse the installed headers.
tools/provision-meos.sh --mdb-src "$MDB" --build-libmeos --parse-prefix "$MDB/.prefix"
# Headers only, no build (approximate struct layouts):
tools/provision-meos.sh --mdb-src "$MDB"Both write output/meos-idl.json, which is what every binding consumes; the script sets
MDB_SRC_ROOT and picks the header source for you (--help lists every option). --build-libmeos
also installs libmeos into --parse-prefix (default <mdb-src>/.prefix). Families default to
-DALL=ON, so the catalog covers the whole surface; a narrower --families set yields a
correspondingly narrower catalog.
Each binding then regenerates from that file through its own entry point — for the JVM
substrate, JMEOS/tools/regen-from-catalog.sh <catalog>, which also builds the jar the JVM
consumers bind. See each repo's GENERATION.md.
tools/refresh-binding.sh runs the whole chain for any binding from one command, so a
GoMEOS, meos-rs, PyMEOS, MEOS.NET or JVM developer refreshes their generated surface against the
latest MEOS API without walking the repos by hand:
MobilityDB -> provision-meos.sh (catalog [+ libmeos]) -> the binding's generate + build
It composes the per-leg scripts above — provision-meos.sh, then the binding's own generator and
build — and adds no derivation of its own, only the sequencing and the sibling checkouts. Each
binding carries the tiny wrapper tools/refresh-from-master.sh that locates this script and calls
it, so the developer only ever runs, from their checkout:
tools/refresh-from-master.shMobilityDB defaults to its latest master; --mdb <path> (or $MDB) points at an existing checkout
on any branch instead, to refresh against a MEOS branch still under development, and a binding that
pins a MEOS ref sets MDB_REF in its config. The binding's last leg is a few lines in its
tools/refresh.conf — ENGINE, BUILD_DIR, BUILD_LIBMEOS (native/FFI bindings build libmeos;
pure-catalog ones do not), CATALOG_DEST, the JVM-only JMEOS_COORDS, and the BUILD_CMD;
refresh-binding.sh --help documents the contract. The JVM consumers are the special case whose
config sets JMEOS_COORDS, which inserts the JMEOS-jar leg; refresh-jvm-chain.sh is a thin
compatibility alias for refresh-binding.sh.
tools/ecosystem-generate.sh <PIN> builds the catalog and libmeos.so from one MobilityDB
ref and then walks the bindings in dependency order. It invokes each binding's own
tools/regen-from-pin.sh, and reports and skips any binding that does not have one — JMEOS
regenerates through tools/regen-from-catalog.sh and is skipped by this script, so run it
directly as above.
A binding never commits meos-idl.json (or a libmeos.so). Both are derived artifacts of
one MobilityDB commit, and a committed copy is drift waiting to happen. Instead a binding records
the MobilityDB commit it targets and derives the catalog — and, for native/FFI bindings, an
installed libmeos — in CI via the shared composite action
MobilityDB/MEOS-API/.github/actions/provision-meos@master. One coordinate in, catalog and
native library out: they are generated from the same ref every run, so they always match — zero
drift.
The action checks out MobilityDB@<ref>, runs run.py to emit output/meos-idl.json, and
optionally builds and installs all-families libmeos. Its interface:
- inputs:
mobilitydb-ref(required — SHA or branch),build-libmeos("true"/"false", default"false"),families(default-DALL=ON, for the optional libmeos build). - outputs:
catalog-path(absolute path to the generatedmeos-idl.json) andlibmeos-prefix(/usr/localwhenbuild-libmeos=true, else empty).
- name: Resolve the MEOS source commit
id: meos
run: echo "sha=$(tr -d '[:space:]' < tools/meos-source-commit.txt)" >> "$GITHUB_OUTPUT"
- name: Provision MEOS
id: provision
uses: MobilityDB/MEOS-API/.github/actions/provision-meos@master
with:
mobilitydb-ref: ${{ steps.meos.outputs.sha }}
build-libmeos: "true" # true for native/FFI bindings; false for pure-catalog codegen
# catalog consumers then stage the derived catalog where their generator reads it, e.g.:
# cp "${{ steps.provision.outputs.catalog-path }}" <path/to/meos-idl.json>
# then run the binding's own generator + tests.mobilitydb-ref can be a pinned SHA — read from a tracked meos-source-commit.txt as above,
which makes the run reproducible — or plain master to track latest. Either way there is no
drift: the catalog (and libmeos) are regenerated from that same ref in the same run. Native/FFI
bindings pass build-libmeos: "true" — libmeos installs under /usr/local (libmeos-prefix),
and its install also stages spatial_ref_sys.csv so SRIDs resolve at runtime. Pure-catalog
bindings leave build-libmeos at its default and consume only catalog-path.
- Catalog-deriving — the binding drops its committed
meos-idl.jsonand derives it in CI, copyingcatalog-pathto where its generator reads it before generating sources: MobilitySpark (cp $catalog-path tools/meos-idl.json, PR #37) and JMEOS (stages tocodegen/input/meos-idl.json, PR #44). - libmeos-only — the binding has no catalog of its own (its facades come from
javapover the JMEOS jar, not from a catalog) and uses the action purely to get libmeos installed withbuild-libmeos: "true": MobilityFlink (PR #41) and MobilityKafka (PR #21).
(a) add the two CI steps above; (b) point your generator at catalog-path (or cp it into
place); (c) git rm any committed meos-idl.json / libmeos.so and add them to .gitignore.