Skip to content

Fix macos installs #348

Description

macOS source installs are broken: make install looks for pg_durable.so, cargo-pgrx produces pg_durable.dylib

Summary

On macOS, make package && make install fails. The packaging step succeeds and
writes pg_durable.dylib; the install step looks for pg_durable.so, does not
find it, and exits with a message telling you to run make package first —
which you just did.

This is currently invisible because every CI job in the repository runs on
Linux, and it becomes user-visible the moment we publish to PGXN (#344), where
pgxn install pg_durable runs make and make install on the user's machine.
macOS users have no other install path: the .deb packages are Debian-only and
the GHCR image is linux/amd64.

Why the suffix differs

PostgreSQL 16 changed the module suffix on macOS from .so to .dylib:

cargo-pgrx follows suit. From cargo-pgrx/src/command/install.rs:189-192 at the
pinned version =0.16.1:

// Since Postgres 16, the shared library extension on macOS is `dylib`, not `so`.
let so_suffix = if cfg!(target_os = "macos") && pg_config.major_version().unwrap() < 16 {
    ".so"
} else {
    std::env::consts::DLL_SUFFIX
};

pg_durable supports only PostgreSQL 17 and 18 (Makefile:148), so on macOS the
packaged library is always pg_durable.dylib. There is no supported
configuration where macOS produces .so.

Renaming to .so is not a valid workaround

pg_durable.control:6 declares:

module_pathname = 'pg_durable'

That is a bare name with no suffix, so PostgreSQL appends its own compiled-in
DLSUFFIX at load time — see expand_dynamic_library_name() in
src/backend/utils/fmgr/dfmgr.c.
On macOS with PG 16+ that resolves to pg_durable.dylib. A file installed as
pg_durable.so would not be found by CREATE EXTENSION. The installed file
must be named .dylib on macOS.

Work items

1. Derive the suffix in the Makefile

Add a variable and use it at all four hardcoded sites:

PG_DLSUFFIX := $(if $(filter Darwin,$(shell uname -s)),.dylib,.so)
Line Current Role
Makefile:179 PACKAGE_LIBRARY = $(PGRX_PACKAGE_DIR)$(PG_PKGLIBDIR)/pg_durable.so source path checked by install
Makefile:194 ! -path "./$(PG_PKGLIBDIR_REL)/pg_durable.so" packaged-tree audit exclusion
Makefile:203 install -m 0755 "$$package_library" "$(DESTDIR)$(PG_PKGLIBDIR)/pg_durable.so" install destination
Makefile:212 library="$(DESTDIR)$(PG_PKGLIBDIR)/pg_durable.so" uninstall target

Makefile:194 matters independently: the audit at Makefile:193-201 fails the
build when the packaged tree contains a file the install recipe does not handle,
so a .dylib would trip it even after Makefile:179 is fixed.

Two implementation notes:

  • pg_config has no --dlsuffix option, so uname -s is the practical source.
    This matches cargo-pgrx, which keys off cfg!(target_os) — the host, not
    anything reported by pg_config.
  • Make PG_DLSUFFIX overridable (?= or a conditional assignment) so tests can
    force the Darwin naming on a Linux runner.
  • Consider having uninstall remove both suffixes. It already tolerates a
    partial install; a machine installed before this fix would otherwise keep an
    orphaned file.

2. Fix scripts/test-make-install.sh

The contract test is Linux-only in two separate ways.

GNU stat. Lines 133-135 use stat -c %a, which BSD/macOS stat does not
accept — it needs stat -f %Lp. Add a small portable helper, e.g.:

file_mode() {
    stat -c %a "$1" 2>/dev/null || stat -f %Lp "$1"
}

Hardcoded .so. Line 76 has the fake cargo write the packaged library as
pg_durable.so, and lines 129 and 155 assert .so paths. These need to follow
the same suffix rule as the Makefile.

Coverage. Since we cannot count on a macOS runner for every PR, add a case
that runs the install/uninstall contract with PG_DLSUFFIX=.dylib forced, so
Linux CI actually exercises the macOS naming.

The script is #!/usr/bin/env bash and uses ${!var} indirect expansion at line
100. That is supported by the bash 3.2 that ships with macOS, so no change is
needed there — but avoid mapfile/readarray if you extend it.

3. Add a macOS CI job

All 16 runs-on: entries across the seven workflow files are ubuntu-latest.
Nothing has ever compiled this extension on macOS, so the packaging fix is
unverified until a macos-latest job in ci.yml does at least:

  1. cargo pgrx init for PG 17 (and 18 if it installs cleanly)
  2. make package
  3. make install DESTDIR=<staging> and assert pg_durable.dylib lands in
    $(pg_config --pkglibdir)
  4. make uninstall and assert the tree is clean
  5. scripts/test-make-install.sh

Ideally also make installcheck, though that is a larger lift.

4. Developer helper scripts (lower priority)

Not on the PGXN install path, but wrong on a Mac dev box:

  • scripts/pg-start.sh:68[ -f "$PKGLIBDIR/pg_durable.so" ] presence check
  • scripts/test-coverage.sh:160-162 — globs pgrx-install/lib/postgresql/pg_durable.so

scripts/package-deb.sh:36 is Debian-specific and should keep .so.

What is already in place

  • .cargo/config.toml already sets -C link-arg=-undefined -C link-arg=dynamic_lookup
    for both aarch64-apple-darwin and x86_64-apple-darwin, which is the link
    configuration pgrx requires on Darwin. The link step looks prepared; it has
    simply never been run.
  • reqwest is pinned to native-tls, which resolves to Security.framework on
    Apple targets rather than OpenSSL, so no OpenSSL build dependency is expected.
    Unverified.

Unknowns to settle while implementing

These cannot be answered without a Mac, and they are the reason this needs
someone with a laptop rather than a blind patch:

  1. Does cargo pgrx package complete at all on macOS, on both aarch64 and
    x86_64? Nothing in CI has ever tried.
  2. Does make installcheck (pg_regress via PGXS) work on macOS?
  3. Homebrew PostgreSQL vs a pgrx-managed PostgreSQL — which pg_config should
    the macOS install instructions point at?

If (1) turns out to be broken for reasons beyond the suffix, that is worth
splitting into its own issue; this one should stay scoped to packaging and
installation.

Acceptance criteria

  • On macOS with PostgreSQL 17 or 18, make package && make install installs
    pg_durable.dylib into pg_config --pkglibdir along with the control and SQL
    files, and CREATE EXTENSION pg_durable loads it.
  • make uninstall removes exactly what install placed and stays idempotent.
  • Linux behaviour is unchanged: still pg_durable.so.
  • scripts/test-make-install.sh passes on both platforms and exercises both
    suffixes on Linux CI.
  • A macos-latest job runs the above and is green.
  • README / USER_GUIDE state which platforms are supported for source installs.

Out of scope

  • Windows.
  • Publishing macOS binaries. PGXN carries the source distribution; this is about
    making a source install work, not about shipping artifacts.

Related

  • Add PGXN distribution metadata #344 — PGXN distribution metadata. Makes source install the advertised path,
    so this should land before the first PGXN upload.
  • TODO.md:21 — "figure out process to build/release the extension for linux,
    windows and macos, with instructions for installation".

Evidence gathered against microsoft/pg_durable at commit 146b41b
(waldemort/pgxn-distribution), pgrx v0.16.1, and PostgreSQL branches
REL_15_STABLE through REL_18_STABLE.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions