From b5258c7790d482b7dbc27eb22a0a2e833f64729a Mon Sep 17 00:00:00 2001 From: Esteban Zimanyi Date: Wed, 28 Oct 2026 00:50:47 +0100 Subject: [PATCH] Provision MEOS with a GDAL built without GEOS MEOS calls no GEOS function, and the GDAL a distribution packages links GEOS, so a libmeos linked against libgdal-dev loads GEOS with it. The provision-meos action builds GDAL 3.12.0 from its release source, pinned by its SHA-256, with GEOS off (build-gdal.sh), and installs it under /usr/local before libmeos is built or restored; MobilityDB's CMake finds it there, and every consumer builds and runs against it. The configuration is the one MEOS.js builds its GDAL with: every optional driver off, GDAL's internal libraries in place of external ones, and PROJ, SQLite and zlib from the system, the libraries libmeos already takes. MEOS reads raster bands through the generic GDAL API and the built-in drivers. The GDAL prefix is cached on the runner image and on build-gdal.sh, so a runner builds it once; the libmeos cache already keys on the action's files. The package list names libsqlite3-dev and zlib1g-dev, GDAL's own inputs, in place of libgdal-dev. A last step fails the action where any GEOS library loads with libmeos, naming it. --- .github/actions/provision-meos/action.yml | 58 ++++++++++++++++---- .github/actions/provision-meos/build-gdal.sh | 54 ++++++++++++++++++ 2 files changed, 102 insertions(+), 10 deletions(-) create mode 100755 .github/actions/provision-meos/build-gdal.sh diff --git a/.github/actions/provision-meos/action.yml b/.github/actions/provision-meos/action.yml index d75dc24..7d88d76 100644 --- a/.github/actions/provision-meos/action.yml +++ b/.github/actions/provision-meos/action.yml @@ -138,22 +138,23 @@ runs: bash "$GITHUB_ACTION_PATH/apt-get.sh" --purge remove postgresql* -y || true sudo rm -rf /var/lib/postgresql/ /etc/postgresql/ /var/log/postgresql/ || true - # The two caches of the build path. The packages: libgdal-dev depends on the -dev - # packages of its drivers (mariadb, poppler, spatialite, xerces) and postgresql-server-dev-17 - # on clang-19 and llvm-19, about 200 MB a runner downloads on every run, so the .deb files - # are kept in a runner-writable directory keyed on the runner image and the package list; - # a new image restores the previous one's files and downloads only what changed. The - # derivation: the libmeos prefix and the catalog are a function of the MobilityDB commit, - # the MEOS-API files that derive them, the families and the runner image, so a run on the - # same four restores them instead of building. A cross-repo action has no .git, so the - # MEOS-API side of the key is a hash of its files' contents. + # The three caches of the build path. The packages: postgresql-server-dev-17 depends on + # clang-19 and llvm-19, which a runner would download on every run, so the .deb files are + # kept in a runner-writable directory keyed on the runner image and the package list; a new + # image restores the previous one's files and downloads only what changed. GDAL: libmeos + # links the GDAL build-gdal.sh builds from its release source without GEOS, a function of the + # runner image and of that script, so a run on the same two restores it. The derivation: the + # libmeos prefix and the catalog are a function of the MobilityDB commit, the MEOS-API files + # that derive them, the families and the runner image, so a run on the same four restores them + # instead of building. A cross-repo action has no .git, so the MEOS-API side of each key is a + # hash of its files' contents. - name: Key the provision caches id: keys if: inputs.build-libmeos == 'true' shell: bash env: PACKAGES: >- - libproj-dev libjson-c-dev libh3-dev libgdal-dev libxml2-dev + libproj-dev libjson-c-dev libh3-dev libsqlite3-dev zlib1g-dev libxml2-dev autoconf automake libtool pkg-config postgresql-17 postgresql-server-dev-17 FAMILIES: ${{ inputs.families }} run: | @@ -165,10 +166,12 @@ runs: | sort -z | xargs -0 sha256sum | sha256sum | cut -c1-16)" pkg_hash="$(echo "$PACKAGES" | tr -s ' \n' '\n\n' | sort | sha256sum | cut -c1-16)" fam_hash="$(echo "$FAMILIES" | sha256sum | cut -c1-16)" + gdal_hash="$(sha256sum "$GITHUB_ACTION_PATH/build-gdal.sh" | cut -c1-16)" { echo "packages=$PACKAGES" echo "deb-prefix=provision-meos-debs-${ImageOS:-unknown}-" echo "deb-key=provision-meos-debs-$image-$pkg_hash" + echo "gdal-key=provision-meos-gdal-$image-$gdal_hash" echo "build-key=provision-meos-build-$image-$mdb_sha-$api_hash-$fam_hash" } >> "$GITHUB_OUTPUT" @@ -205,6 +208,28 @@ runs: sudo chmod -R a+rX "$APT_ARCHIVES" sudo rm -f "$APT_ARCHIVES/lock" + - name: Restore the GDAL build + id: gdal-cache + if: inputs.build-libmeos == 'true' + uses: actions/cache@v4 + with: + path: ${{ runner.temp }}/gdal-prefix + key: ${{ steps.keys.outputs.gdal-key }} + + - name: Build GDAL without GEOS + if: inputs.build-libmeos == 'true' && steps.gdal-cache.outputs.cache-hit != 'true' + shell: bash + run: bash "$GITHUB_ACTION_PATH/build-gdal.sh" "$RUNNER_TEMP/gdal-prefix" + + # Installed on a cache hit of the libmeos prefix too, as libmeos.so links it. MobilityDB's + # CMake finds it under /usr/local, the GDAL every consumer then builds and runs against. + - name: Install GDAL + if: inputs.build-libmeos == 'true' + shell: bash + run: | + sudo cp -a "$RUNNER_TEMP/gdal-prefix/." /usr/local/ + sudo ldconfig + # Build path: provision-meos.sh builds libmeos, stages a clean MEOS-only # header prefix ($RUNNER_TEMP/meos-headers) that the parse reads, and installs # the runtime library under /usr/local for the consumer builds. It derives the @@ -247,3 +272,16 @@ runs: echo "libmeos-prefix=/usr/local" } >> "$GITHUB_OUTPUT" echo "Restored libmeos and the catalog ($(wc -c < "$REPO_ROOT/output/meos-idl.json") bytes)" >&2 + + # MEOS calls no GEOS function, so no GEOS library loads with libmeos: the step fails where + # one does, naming the library that brings it. + - name: Check that no GEOS loads with libmeos + if: inputs.build-libmeos == 'true' + shell: bash + run: | + deps="$(ldd /usr/local/lib/libmeos.so)" + echo "$deps" | grep -E 'libgdal|libproj|libmeos' >&2 || true + if echo "$deps" | grep -i 'libgeos'; then + echo "::error::libmeos loads GEOS: $(echo "$deps" | grep -i libgeos | awk '{print $1}' | tr '\n' ' ')" + exit 1 + fi diff --git a/.github/actions/provision-meos/build-gdal.sh b/.github/actions/provision-meos/build-gdal.sh new file mode 100755 index 0000000..e8d58c4 --- /dev/null +++ b/.github/actions/provision-meos/build-gdal.sh @@ -0,0 +1,54 @@ +#!/usr/bin/env bash +# build-gdal.sh — build the GDAL libmeos links, from its release source, without GEOS, into a +# prefix the provision-meos action caches and installs under /usr/local. MEOS calls no GEOS +# function, and the GDAL a distribution packages (libgdal-dev on Ubuntu) links GEOS, so a +# libmeos linked against it loads GEOS with it; this GDAL loads none. +# +# The configuration is the one MEOS.js builds its GDAL with: every optional driver off, the +# libraries GDAL carries internally used in place of external ones, and only PROJ, SQLite and +# zlib taken from the system, which libmeos already needs. MEOS reads raster bands through the +# generic GDAL API and the built-in drivers (MEM, VRT, GTiff), so the core is the whole +# requirement. The libraries are shared, as the hosted bindings link libmeos.so. +# +# The release is pinned by its SHA-256, so the build is the same on every runner and a changed +# file fails the step rather than building something else. +# +# Usage: +# build-gdal.sh +# +# Environment: +# CMAKE_BUILD_PARALLEL_LEVEL build jobs (default: the number of processors) +set -euo pipefail + +PREFIX="${1:?usage: build-gdal.sh }" +VERSION=3.12.0 +SHA256=44c95baabe6c1a047c1ebe5043e38dd73e4936bc4c481db14efbfd03342eab73 +URL="https://github.com/OSGeo/gdal/releases/download/v$VERSION/gdal-$VERSION.tar.gz" + +WORK="$(mktemp -d)" +trap 'rm -rf "$WORK"' EXIT + +curl -fsSL --retry 3 --max-time 600 -o "$WORK/gdal.tar.gz" "$URL" +echo "$SHA256 $WORK/gdal.tar.gz" | sha256sum -c - +tar xzf "$WORK/gdal.tar.gz" -C "$WORK" + +cmake -S "$WORK/gdal-$VERSION" -B "$WORK/build" \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_INSTALL_PREFIX="$PREFIX" \ + -DBUILD_SHARED_LIBS=ON \ + -DBUILD_APPS=OFF \ + -DBUILD_TESTING=OFF \ + -DBUILD_DOCS=OFF \ + -DBUILD_PYTHON_BINDINGS=OFF \ + -DBUILD_JAVA_BINDINGS=OFF \ + -DBUILD_CSHARP_BINDINGS=OFF \ + -DGDAL_BUILD_OPTIONAL_DRIVERS=OFF \ + -DOGR_BUILD_OPTIONAL_DRIVERS=OFF \ + -DGDAL_USE_EXTERNAL_LIBS=OFF \ + -DGDAL_USE_INTERNAL_LIBS=ON \ + -DGDAL_USE_PROJ=ON \ + -DGDAL_USE_GEOS=OFF \ + -DGDAL_USE_SQLITE3=ON \ + -DGDAL_USE_ZLIB=ON +cmake --build "$WORK/build" -j "${CMAKE_BUILD_PARALLEL_LEVEL:-$(nproc)}" +cmake --install "$WORK/build"