diff --git a/Makefile b/Makefile index bc17d05..a948549 100644 --- a/Makefile +++ b/Makefile @@ -11,9 +11,39 @@ include extension-ci-tools/makefiles/duckdb_extension.Makefile # both MEOS (meos_initialize_timezone) and DuckDB (DBConfig::SetOptionByName # "TimeZone") to Europe/Brussels. Tests pass on any OS timezone — the # extension is the single source of truth, no TZ env var needed. +# +# LoadInternal also calls ExtensionHelper::AutoLoadExtension(db, "icu") so +# the timezone option is honoured. Autoload looks for the extension on disk +# at $HOME/.duckdb/extensions///icu.duckdb_extension +# and falls back to a hub download. That fails both inside the linux_amd64 +# test docker container (empty path, no network egress) and on the macOS +# osx_arm64 test runner (hub icu not reliably resolvable). We copy the +# icu.duckdb_extension that was built locally as part of this extension's +# build (declared in extension_config.cmake) into the expected path, +# matched to the DuckDB platform string, before running the unittester. +DUCKDB_VERSION_TAG := v1.4.4 + +define stage_icu + @if [ -f ./build/$(1)/extension/icu/icu.duckdb_extension ]; then \ + case "$$(uname -s)-$$(uname -m)" in \ + Linux-x86_64) platform=linux_amd64 ;; \ + Linux-aarch64) platform=linux_arm64 ;; \ + Darwin-arm64) platform=osx_arm64 ;; \ + Darwin-x86_64) platform=osx_amd64 ;; \ + *) platform=$$(uname -m) ;; \ + esac; \ + target=$$HOME/.duckdb/extensions/$(DUCKDB_VERSION_TAG)/$$platform; \ + mkdir -p "$$target" && cp -f ./build/$(1)/extension/icu/icu.duckdb_extension "$$target/" && \ + echo "Staged icu.duckdb_extension at $$target/"; \ + fi +endef + test_release_internal: + $(call stage_icu,release) ./build/release/$(TEST_PATH) "$(PROJ_DIR)test/*" test_debug_internal: + $(call stage_icu,debug) ./build/debug/$(TEST_PATH) "$(PROJ_DIR)test/*" test_reldebug_internal: - ./build/reldebug/$(TEST_PATH) "$(PROJ_DIR)test/*" \ No newline at end of file + $(call stage_icu,reldebug) + ./build/reldebug/$(TEST_PATH) "$(PROJ_DIR)test/*" diff --git a/src/include/tydef.hpp b/src/include/tydef.hpp index b7b2810..b9860ca 100644 --- a/src/include/tydef.hpp +++ b/src/include/tydef.hpp @@ -11,11 +11,27 @@ extern "C" { #include } -// Forward-compat alias for the meosType → MeosType rename (MobilityDB -// pr785-sync-script). Vcpkg's MEOS exposes `MeosType`; existing -// MobilityDuck code still uses `meosType`. This alias bridges the two -// without touching every reference site. -using meosType = MeosType; +// MEOS naming history: `meosType` is the **pre-consolidation** spelling +// and `MeosType` is the **post-consolidation** target (the rename is +// part of the upstream consolidation sweep, not yet reached by the +// vcpkg pin). The current pin +// (`vcpkg_ports/meos/portfile.cmake` REF f11b7443ee98…) is still +// pre-consolidation and exposes `meosType` — see +// meos/include/temporal/meos_catalog.h, where line 121 declares +// `} meosType;`. MobilityDuck's source consistently uses +// `meosType` (verified via `grep -rn '\bmeosType\b' src/`), which +// matches the pin, so no alias is needed today. +// +// An earlier version of this file added `using meosType = MeosType;` +// as a forward-looking bridge for the eventual consolidation bump. +// That alias references `MeosType`, which the current pin does NOT +// yet expose, so it broke the build: +// "'MeosType' does not name a type; did you mean 'meosType'?". +// +// When the MEOS pin is bumped past the consolidation point, restore +// a bridge here (`using meosType = MeosType;` becomes valid then) or +// sweep the source `meosType → MeosType` in one PR — whichever the +// project prefers at that time. namespace duckdb { diff --git a/vcpkg.json b/vcpkg.json index ee22c1e..9ae3398 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -7,6 +7,7 @@ "vcpkg-cmake", "openssl", "zlib", + "libxml2", "expat", { "name": "sqlite3", diff --git a/vcpkg_ports/meos/portfile.cmake b/vcpkg_ports/meos/portfile.cmake index c804af5..22c2f63 100644 --- a/vcpkg_ports/meos/portfile.cmake +++ b/vcpkg_ports/meos/portfile.cmake @@ -43,6 +43,127 @@ endif() ]=] ) +# pgPointCloud enabler. -DPOINTCLOUD=ON makes meos/src/pointcloud/ +# CMakeLists.txt FATAL_ERROR unless pointcloud-pg/lib/libpc.a exists; it +# is built as a side effect of pgPointCloud's `./autogen.sh && +# ./configure && make`, which cannot run in the manylinux extension-ci +# container (no autotools-usable PostgreSQL, no pg_config). The vendored +# pointcloud-pg/lib/ archive sources include only libxml2 and zlib (no +# PostgreSQL headers), and pointcloud-pg/lib/Makefile builds the libpc.a +# target directly via `ar rs` with only the XML2/ZLIB CPPFLAGS from +# config.mk — the CUnit dependency is confined to the `all:` cunit/ +# recurse. So we generate config.mk and lib/pc_config.h the way +# pgPointCloud's autotools would (filling only the @VARS@ that +# config.mk.in declares and the lib OBJS sources consume, with vcpkg's +# libxml2/zlib paths), then build the libpc.a archive target directly. +set(POINTCLOUD_DIR "${SOURCE_PATH}/pointcloud-pg") + +# config.mk: fill the @VARS@ declared by pointcloud-pg/config.mk.in. +# XML2_CPPFLAGS mirrors `xml2-config --cflags` (-I/include/libxml2); +# ZLIB_CPPFLAGS is empty because zlib.h is on the default include path +# (-I/include); the *_LDFLAGS mirror `xml2-config --libs` / -lz +# but only matter for linking, not for the `ar rs` static archive. LazPerf +# and CUnit are disabled exactly as configure leaves them when neither is +# present (no HAVE_LAZPERF / HAVE_CUNIT define). +file(WRITE "${POINTCLOUD_DIR}/config.mk" +"CC = cc +CFLAGS = -O2 -fPIC +CXXFLAGS += -fPIC -std=c++0x +SQLPP = + +XML2_CPPFLAGS = -I${CURRENT_INSTALLED_DIR}/include/libxml2 +XML2_LDFLAGS = -L${CURRENT_INSTALLED_DIR}/lib -lxml2 + +ZLIB_CPPFLAGS = -I${CURRENT_INSTALLED_DIR}/include +ZLIB_LDFLAGS = -L${CURRENT_INSTALLED_DIR}/lib -lz + +CUNIT_CPPFLAGS = +CUNIT_LDFLAGS = + +PG_CONFIG = +PGXS = + +LIB_A = libpc.a +LIB_A_LAZPERF = liblazperf.a + +LAZPERF_STATUS = disabled +LAZPERF_CPPFLAGS = + +PGSQL_MAJOR_VERSION = +") + +# lib/pc_config.h: pc_api.h does `#include \"pc_config.h\"`, normally +# emitted by config.status from lib/pc_config.h.in. Mirror the autotools +# no-lazperf / no-cunit output: substitute the always-AC_DEFINE'd +# version vars, and leave HAVE_LAZPERF / HAVE_CUNIT undefined (so the +# pc_patch_lazperf.c `#ifndef HAVE_LAZPERF` early-return guards stay +# active and no liblazperf symbols are referenced). POINTCLOUD_VERSION +# is pointcloud-pg/Version.config = 1.2.5. +file(WRITE "${POINTCLOUD_DIR}/lib/pc_config.h" +"/* #undef LIBXML2_VERSION */ + +/* #undef PGSQL_VERSION */ + +/* #undef HAVE_LAZPERF */ + +/* #undef HAVE_CUNIT */ + +#define PROJECT_SOURCE_DIR \"${POINTCLOUD_DIR}\" + +#define POINTCLOUD_VERSION \"1.2.5\" +") + +# Build the libpc.a archive target directly (NOT `all`, which recurses +# into cunit/ and needs CUnit). config.mk is included by lib/Makefile. +vcpkg_execute_required_process( + COMMAND make -C "${POINTCLOUD_DIR}/lib" libpc.a + WORKING_DIRECTORY "${POINTCLOUD_DIR}/lib" + LOGNAME "build-libpc-${TARGET_TRIPLET}" +) + +# pgPointCloud's lib/stringbuffer.c is a verbatim fork of PostGIS +# liblwgeom/stringbuffer.c (identical stringbuffer_t layout, identical +# stringbuffer_* signatures). MEOS already bundles liblwgeom — including +# its stringbuffer.c — into libmeos.a, so carrying pgPointCloud's copy in +# libpc.a as well makes the final static link fail with `multiple +# definition of stringbuffer_*`. liblwgeom additionally exports only some +# of these symbols out-of-line (stringbuffer_append / _append_char / +# _append_double are header-only `static inline` there), so simply +# dropping pgPointCloud's stringbuffer.o leaves its callers with +# unresolved stringbuffer_append. Instead, rename every stringbuffer_* +# symbol in libpc.a — definitions and the cross-object references in +# pc_point.o / pc_schema.o / pc_dimstats.o / pc_patch_uncompressed.o — to +# a private pc_stringbuffer_* namespace. libpc.a then resolves these +# entirely against its own copy and never collides with liblwgeom. +vcpkg_execute_required_process( + COMMAND ${CMAKE_COMMAND} -E env bash -c + "set -e + cd '${POINTCLOUD_DIR}/lib' + : > redefine.map + for s in $(nm libpc.a 2>/dev/null | awk '$NF ~ /^stringbuffer_/ {print $NF}' | sort -u); do + echo \"$s pc_$s\" >> redefine.map + done + tmp=$(mktemp -d) + cd \"$tmp\" + ar x '${POINTCLOUD_DIR}/lib/libpc.a' + for o in *.o; do + objcopy --redefine-syms='${POINTCLOUD_DIR}/lib/redefine.map' \"$o\" + done + rm -f '${POINTCLOUD_DIR}/lib/libpc.a' + ar rcs '${POINTCLOUD_DIR}/lib/libpc.a' *.o + cd / + rm -rf \"$tmp\" '${POINTCLOUD_DIR}/lib/redefine.map'" + WORKING_DIRECTORY "${POINTCLOUD_DIR}/lib" + LOGNAME "namespace-libpc-stringbuffer-${TARGET_TRIPLET}" +) + +# meos/src/pointcloud/CMakeLists.txt checks exactly this path. +if(NOT EXISTS "${POINTCLOUD_DIR}/lib/libpc.a") + message(FATAL_ERROR + "pgPointCloud enabler failed: ${POINTCLOUD_DIR}/lib/libpc.a " + "was not produced by the libpc.a make target.") +endif() + vcpkg_cmake_configure( SOURCE_PATH "${SOURCE_PATH}" OPTIONS @@ -54,6 +175,7 @@ vcpkg_cmake_configure( -DNPOINT=ON -DPOSE=ON -DRGEO=ON + -DPOINTCLOUD=ON -DBUILD_SHARED_LIBS=ON -DCMAKE_C_FLAGS="-Dsession_timezone=meos_session_timezone" -DCMAKE_CXX_FLAGS="-Dsession_timezone=meos_session_timezone" @@ -63,6 +185,18 @@ vcpkg_cmake_configure( vcpkg_cmake_build(TARGET all) vcpkg_cmake_install() +# meos/src/pointcloud/CMakeLists.txt links libpc.a into the `pointcloud` +# OBJECT library with target_link_libraries(... PRIVATE libpc.a xml2 z). +# That is only a usage requirement: the static libmeos.a archive does +# not absorb libpc.a's objects nor carry a link interface, so a consumer +# linking the static libmeos.a sees unresolved pc_point_get_x/y/z/... +# Install libpc.a alongside libmeos.a and propagate it (plus libxml2 and +# zlib) through the MEOS::meos imported target's INTERFACE_LINK_LIBRARIES +# so the extension link resolves the pgPointCloud symbols — mirroring the +# canonical POINTCLOUD_LINK_LIBS = libpc.a xml2 z. +file(INSTALL "${SOURCE_PATH}/pointcloud-pg/lib/libpc.a" + DESTINATION "${CURRENT_PACKAGES_DIR}/lib") + file(MAKE_DIRECTORY "${CURRENT_PACKAGES_DIR}/share/meos") file(WRITE "${CURRENT_PACKAGES_DIR}/share/meos/MEOSConfig.cmake" [=[ # Minimal imported target for MEOS @@ -87,6 +221,24 @@ if (NOT TARGET MEOS::meos) IMPORTED_LOCATION "${_meos_lib}" INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_LIST_DIR}/../../include" ) + # When MEOS was built with -DPOINTCLOUD=ON, libmeos.a calls into the + # pgPointCloud archive libpc.a (and libxml2 / zlib). The static + # libmeos.a does not carry that link interface, so propagate it here. + set(_meos_libdir "${CMAKE_CURRENT_LIST_DIR}/../../lib") + if (EXISTS "${_meos_libdir}/libpc.a") + set(_meos_pc_iface "${_meos_libdir}/libpc.a") + file(GLOB _meos_xml2 "${_meos_libdir}/libxml2.a" "${_meos_libdir}/libxml2.lib") + file(GLOB _meos_zlib "${_meos_libdir}/libz.a" "${_meos_libdir}/libzlib.a" "${_meos_libdir}/zlib.lib") + if (_meos_xml2) + list(APPEND _meos_pc_iface ${_meos_xml2}) + endif() + if (_meos_zlib) + list(APPEND _meos_pc_iface ${_meos_zlib}) + endif() + set_target_properties(MEOS::meos PROPERTIES + INTERFACE_LINK_LIBRARIES "${_meos_pc_iface}" + ) + endif() endif() ]=]) diff --git a/vcpkg_ports/meos/vcpkg.json b/vcpkg_ports/meos/vcpkg.json index 22bd9c3..6e2e209 100644 --- a/vcpkg_ports/meos/vcpkg.json +++ b/vcpkg_ports/meos/vcpkg.json @@ -1,7 +1,7 @@ { "name": "meos", "version-string": "git", - "port-version": 1, + "port-version": 2, "description": "MEOS - Mobility Engine Open Source C library (built from MobilityDB with -DMEOS=ON)", "homepage": "https://libmeos.org/", "dependencies": [ @@ -10,6 +10,8 @@ "geos", "proj", "json-c", - "gsl" + "gsl", + "libxml2", + "zlib" ] } \ No newline at end of file