Skip to content

Commit

Permalink
postgresql: replace pg_config with custom script
Browse files Browse the repository at this point in the history
By replacing upstream's pg_config binary with a shell script, we:
- gain the ability to run pg_config easily when cross-compiling,
- can remove the fake pg_config in the default output,
- can remove the pg_config wrapper script dealing with special cases.

Some 20 years ago, pg_config *was* a shell script upstream, too. It was
changed to a binary, when it was made "relocatable", so it would return
paths depending on the location of the "postgres" binary. However, this
is exactly the thing that just hurts us in nixpkgs - we don't want those
paths to change, we want them to always point at the right outputs. By
writing the script ourselves, this becomes a lot less painful.

This approach means more lines of codes, but all of them are dead simple
and we have a lot less complexity overall.

Additionally, pg_config is now made a separate derivation, only exposed
as "postgresql.pg_config". This has the nice side-effect, that all users
of postgresql and libpq in nixpkgs must be very *explicit* about their
dependency on pg_config. This gives a lot more visibility into the state
of affairs regarding pkg-config support for libpq, which ultimately is
the much better solution.
  • Loading branch information
wolfgangwalther committed Mar 10, 2025
1 parent bcfa2a0 commit c733b7b
Show file tree
Hide file tree
Showing 8 changed files with 170 additions and 49 deletions.
4 changes: 2 additions & 2 deletions pkgs/development/tools/rust/cargo-pgrx/buildPgrxExtension.nix
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ let
preBuildAndTest = ''
export PGRX_HOME="$(mktemp -d)"
export PGDATA="$PGRX_HOME/data-${pgrxPostgresMajor}/"
cargo-pgrx pgrx init "--pg${pgrxPostgresMajor}" ${lib.getDev postgresql}/bin/pg_config
cargo-pgrx pgrx init "--pg${pgrxPostgresMajor}" ${postgresql.pg_config}/bin/pg_config
# unix sockets work in sandbox, too.
export PGHOST="$(mktemp -d)"
Expand Down Expand Up @@ -140,7 +140,7 @@ let
PGRX_BUILD_FLAGS="--frozen -j $NIX_BUILD_CORES ${builtins.concatStringsSep " " cargoBuildFlags}" \
${lib.optionalString stdenv.hostPlatform.isDarwin ''RUSTFLAGS="''${RUSTFLAGS:+''${RUSTFLAGS} }-Clink-args=-Wl,-undefined,dynamic_lookup"''} \
cargo pgrx package \
--pg-config ${lib.getDev postgresql}/bin/pg_config \
--pg-config ${postgresql.pg_config}/bin/pg_config \
${maybeDebugFlag} \
--features "${builtins.concatStringsSep " " buildFeatures}" \
--out-dir "$out"
Expand Down
2 changes: 1 addition & 1 deletion pkgs/servers/sql/postgresql/buildPostgresqlExtension.nix
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ let

strictDeps = true;
buildInputs = [ postgresql ] ++ prevAttrs.buildInputs or [ ];
nativeBuildInputs = [ postgresql ] ++ prevAttrs.nativeBuildInputs or [ ];
nativeBuildInputs = [ postgresql.pg_config ] ++ prevAttrs.nativeBuildInputs or [ ];

installFlags = [
"DESTDIR=${placeholder "out"}"
Expand Down
2 changes: 2 additions & 0 deletions pkgs/servers/sql/postgresql/ext/postgis.nix
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ buildPostgresqlExtension (finalAttrs: {
dontDisableStatic = true;

nativeCheckInputs = [
postgresql
postgresqlTestHook
cunit
libxslt
Expand All @@ -90,6 +91,7 @@ buildPostgresqlExtension (finalAttrs: {
'';

configureFlags = [
"--with-pgconfig=${postgresql.pg_config}/bin/pg_config"
"--with-gdalconfig=${gdal}/bin/gdal-config"
"--with-jsondir=${json_c.dev}"
"--disable-extension-upgrades-install"
Expand Down
17 changes: 7 additions & 10 deletions pkgs/servers/sql/postgresql/generic.nix
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ let

# passthru
buildEnv,
buildPackages,
newScope,
nixosTests,
postgresqlTestHook,
Expand Down Expand Up @@ -125,8 +126,6 @@ let
)
else
stdenv;

pg_config = writeShellScriptBin "pg_config" (builtins.readFile ./pg_config.sh);
in
stdenv'.mkDerivation (finalAttrs: {
inherit version;
Expand Down Expand Up @@ -323,21 +322,17 @@ let

postPatch = ''
substituteInPlace "src/Makefile.global.in" --subst-var out
# Hardcode the path to pgxs so pg_config returns the path in $dev
substituteInPlace "src/common/config_info.c" --subst-var dev
cat ${./pg_config.env.mk} >> src/common/Makefile
'';

postInstall =
''
moveToOutput "bin/ecpg" "$dev"
moveToOutput "lib/pgxs" "$dev"
# Pretend pg_config is located in $out/bin to return correct paths, but
# actually have it in -dev to avoid pulling in all other outputs. See the
# pg_config.sh script's comments for details.
moveToOutput "bin/pg_config" "$dev"
install -c -m 755 "${pg_config}"/bin/pg_config "$out/bin/pg_config"
wrapProgram "$dev/bin/pg_config" --argv0 "$out/bin/pg_config"
rm "$out/bin/pg_config"
make -C src/common pg_config.env
install -D src/common/pg_config.env "$dev/nix-support/pg_config.env"
# postgres exposes external symbols get_pkginclude_path and similar. Those
# can't be stripped away by --gc-sections/LTO, because they could theoretically
Expand Down Expand Up @@ -466,6 +461,8 @@ let
postgresql = this;
};

pg_config = buildPackages.callPackage ./pg_config.nix { inherit (finalAttrs) finalPackage; };

tests =
{
postgresql = nixosTests.postgresql.postgresql.passthru.override finalAttrs.finalPackage;
Expand Down
12 changes: 9 additions & 3 deletions pkgs/servers/sql/postgresql/libpq.nix
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

# passthru / meta
postgresql,
buildPackages,

# GSSAPI
gssSupport ? with stdenv.hostPlatform; !isWindows && !isStatic,
Expand Down Expand Up @@ -105,16 +106,19 @@ stdenv.mkDerivation (finalAttrs: {
./patches/socketdir-in-run-13+.patch
];

postPatch = ''
cat ${./pg_config.env.mk} >> src/common/Makefile
'';

installPhase = ''
runHook preInstall
make -C src/bin/pg_config install
make -C src/common install
make -C src/common install pg_config.env
make -C src/include install
make -C src/interfaces/libpq install
make -C src/port install
moveToOutput bin/pg_config "$dev"
install -D src/common/pg_config.env "$dev/nix-support/pg_config.env"
moveToOutput "lib/*.a" "$dev"
rm -rfv $out/share
Expand All @@ -135,6 +139,8 @@ stdenv.mkDerivation (finalAttrs: {

doCheck = false;

passthru.pg_config = buildPackages.callPackage ./pg_config.nix { inherit (finalAttrs) finalPackage; };

meta = {
inherit (postgresql.meta)
homepage
Expand Down
13 changes: 13 additions & 0 deletions pkgs/servers/sql/postgresql/pg_config.env.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Everything that pg_config normally returns is put into pg_config.env, so
# that we can read it from our own pg_config script later on.
pg_config.env: $(top_builddir)/src/port/pg_config_paths.h | $(top_builddir)/src/include/pg_config.h
echo "CC=\"$(CC)\"" >$@
echo "CPPFLAGS=\"$(STD_CPPFLAGS)\"" >>$@
echo "CFLAGS=\"$(CFLAGS)\"" >>$@
echo "CFLAGS_SL=\"$(CFLAGS_SL)\"" >>$@
echo "LDFLAGS=\"$(STD_LDFLAGS)\"" >>$@
echo "LDFLAGS_EX=\"$(LDFLAGS_EX)\"" >>$@
echo "LDFLAGS_SL=\"$(LDFLAGS_SL)\"" >>$@
echo "LIBS=\"$(LIBS)\"" >>$@
cat $(top_builddir)/src/port/pg_config_paths.h $(top_builddir)/src/include/pg_config.h \
| sed -nE 's/^#define ([^ ]+) ("?)(.*)\2$$/\1="\3"/p' >>$@
18 changes: 18 additions & 0 deletions pkgs/servers/sql/postgresql/pg_config.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
lib,
replaceVarsWith,
runtimeShell,
# PostgreSQL package
finalPackage,
}:

replaceVarsWith {
name = "pg_config";
src = ./pg_config.sh;
dir = "bin";
isExecutable = true;
replacements = {
inherit runtimeShell;
postgresql-dev = lib.getDev finalPackage;
};
}
151 changes: 118 additions & 33 deletions pkgs/servers/sql/postgresql/pg_config.sh
Original file line number Diff line number Diff line change
@@ -1,37 +1,122 @@
#!@runtimeShell@
set -euo pipefail

# The real pg_config needs to be in the same path as the "postgres" binary
# to return proper paths. However, we want it in the -dev output to prevent
# cyclic references and to prevent blowing up the runtime closure. Thus, we
# have wrapped -dev/bin/pg_config to fake its argv0 to be in the default
# output. Unfortunately, pg_config tries to be smart and tries to find itself -
# which will then fail with:
# pg_config: could not find own program executable
# To counter this, we're creating *this* fake pg_config script and put it into
# the default output. The real pg_config is happy.
# Some extensions, e.g. timescaledb, use the reverse logic and look for pg_config
# in the same path as the "postgres" binary to support multi-version-installs.
# Thus, they will end up calling this script during build, even though the real
# pg_config would be available on PATH, provided by nativeBuildInputs. To help
# this case, we're redirecting the call to pg_config to the one found in PATH,
# iff we can be convinced that it belongs to our -dev output.

# Avoid infinite recursion
if [[ ! -v PG_CONFIG_CALLED ]]; then
# compares "path of *this* script" with "path, which pg_config on PATH believes it is in"
if [[ "$(readlink -f -- "$0")" == "$(PG_CONFIG_CALLED=1 pg_config --bindir)/pg_config" ]]; then
# The pg_config in PATH returns the same bindir that we're actually called from.
# This means that the pg_config in PATH is the one from "our" -dev output.
# This happens when the -dev output has been put in native build
# inputs and allows us to call the real pg_config without referencing
# the -dev output itself.
exec pg_config "$@"
fi
# This replacement script for the pg_config binary is based on the original pg_config
# shell script, which was removed from PostgreSQL's codebase in 2004:
# https://github.com/postgres/postgres/commit/cc07f8cfe73f56fce1ddda4ea25d7b0b6c4f0ae9
#
# The main reason for removal was the ability to relocate an existing installation, which
# is exactly the one feature that we are trying to work around in nixpkgs.
# Going back to a shell script is much better for cross compiling.
#
# This file is a combination of the following two files:
# https://github.com/postgres/postgres/blob/7510ac6203bc8e3c56eae95466feaeebfc1b4f31/src/bin/pg_config/pg_config.sh
# https://github.com/postgres/postgres/blob/master/src/bin/pg_config/pg_config.c

source @postgresql-dev@/nix-support/pg_config.env

help="
pg_config provides information about the installed version of PostgreSQL.
Usage:
pg_config [OPTION]...
Options:
--bindir show location of user executables
--docdir show location of documentation files
--htmldir show location of HTML documentation files
--includedir show location of C header files of the client
interfaces
--pkgincludedir show location of other C header files
--includedir-server show location of C header files for the server
--libdir show location of object code libraries
--pkglibdir show location of dynamically loadable modules
--localedir show location of locale support files
--mandir show location of manual pages
--sharedir show location of architecture-independent support files
--sysconfdir show location of system-wide configuration files
--pgxs show location of extension makefile
--configure show options given to \"configure\" script when
PostgreSQL was built
--cc show CC value used when PostgreSQL was built
--cppflags show CPPFLAGS value used when PostgreSQL was built
--cflags show CFLAGS value used when PostgreSQL was built
--cflags_sl show CFLAGS_SL value used when PostgreSQL was built
--ldflags show LDFLAGS value used when PostgreSQL was built
--ldflags_ex show LDFLAGS_EX value used when PostgreSQL was built
--ldflags_sl show LDFLAGS_SL value used when PostgreSQL was built
--libs show LIBS value used when PostgreSQL was built
--version show the PostgreSQL version
-?, --help show this help, then exit
With no arguments, all known items are shown.
Report bugs to <${PACKAGE_BUGREPORT}>.
${PACKAGE_NAME} home page: <${PACKAGE_URL}>"

show=()

for opt; do
case "$opt" in
--bindir) show+=("$PGBINDIR") ;;
--docdir) show+=("$DOCDIR") ;;
--htmldir) show+=("$HTMLDIR") ;;
--includedir) show+=("$INCLUDEDIR") ;;
--pkgincludedir) show+=("$PKGINCLUDEDIR") ;;
--includedir-server) show+=("$INCLUDEDIRSERVER") ;;
--libdir) show+=("$LIBDIR") ;;
--pkglibdir) show+=("$PKGLIBDIR") ;;
--localedir) show+=("$LOCALEDIR") ;;
--mandir) show+=("$MANDIR") ;;
--sharedir) show+=("$PGSHAREDIR") ;;
--sysconfdir) show+=("$SYSCONFDIR") ;;
--pgxs) show+=("@postgresql-dev@/lib/pgxs/src/makefiles/pgxs.mk") ;;
--configure) show+=("$CONFIGURE_ARGS") ;;
--cc) show+=("$CC") ;;
--cppflags) show+=("$CPPFLAGS") ;;
--cflags) show+=("$CFLAGS") ;;
--cflags_sl) show+=("$CFLAGS_SL") ;;
--ldflags) show+=("$LDFLAGS") ;;
--ldflags_ex) show+=("$LDFLAGS_EX") ;;
--ldflags_sl) show+=("$LDFLAGS_SL") ;;
--libs) show+=("$LIBS") ;;
--version) show+=("PostgreSQL $PG_VERSION") ;;
--help|-\?) echo "$help"
exit 0 ;;
*) >&2 echo "pg_config: invalid argument: $opt"
>&2 echo "Try \"pg_config --help\" for more information."
exit 1 ;;
esac
done

if [ ${#show[@]} -gt 0 ]; then
printf '%s\n' "${show[@]}"
exit 0
fi

# This will happen in one of these cases:
# - *this* script is the first on PATH
# - np pg_config on PATH
# - some other pg_config on PATH, not from our -dev output
echo The real pg_config can be found in the -dev output.
exit 1
# no arguments -> print everything
cat <<EOF
BINDIR = $PGBINDIR
DOCDIR = $DOCDIR
HTMLDIR = $HTMLDIR
INCLUDEDIR = $INCLUDEDIR
PKGINCLUDEDIR = $PKGINCLUDEDIR
INCLUDEDIR-SERVER = $INCLUDEDIRSERVER
LIBDIR = $LIBDIR
PKGLIBDIR = $PKGLIBDIR
LOCALEDIR = $LOCALEDIR
MANDIR = $MANDIR
SHAREDIR = $PGSHAREDIR
SYSCONFDIR = $SYSCONFDIR
PGXS = @postgresql-dev@/lib/postgresql/pgxs/src/makefiles/pgxs.mk
CONFIGURE = $CONFIGURE_ARGS
CC = $CC
CPPFLAGS = $CPPFLAGS
CFLAGS = $CFLAGS
CFLAGS_SL = $CFLAGS_SL
LDFLAGS = $LDFLAGS
LDFLAGS_EX = $LDFLAGS_EX
LDFLAGS_SL = $LDFLAGS_SL
LIBS = $LIBS
VERSION = PostgreSQL $PG_VERSION
EOF

0 comments on commit c733b7b

Please sign in to comment.