From 07e200ad85d5a3188f8996484f49c0b4d1494af1 Mon Sep 17 00:00:00 2001 From: Wolfgang Walther Date: Mon, 10 Mar 2025 19:35:50 +0100 Subject: [PATCH] postgresql: replace pg_config with custom script 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. --- .../rust/cargo-pgrx/buildPgrxExtension.nix | 4 +- .../postgresql/buildPostgresqlExtension.nix | 2 +- pkgs/servers/sql/postgresql/ext/postgis.nix | 2 + pkgs/servers/sql/postgresql/generic.nix | 17 +- pkgs/servers/sql/postgresql/libpq.nix | 14 +- pkgs/servers/sql/postgresql/pg_config.env.mk | 13 ++ pkgs/servers/sql/postgresql/pg_config.nix | 18 +++ pkgs/servers/sql/postgresql/pg_config.sh | 151 ++++++++++++++---- 8 files changed, 172 insertions(+), 49 deletions(-) create mode 100644 pkgs/servers/sql/postgresql/pg_config.env.mk create mode 100644 pkgs/servers/sql/postgresql/pg_config.nix diff --git a/pkgs/development/tools/rust/cargo-pgrx/buildPgrxExtension.nix b/pkgs/development/tools/rust/cargo-pgrx/buildPgrxExtension.nix index 504ad1a0bbdff8..bcde00af753b17 100644 --- a/pkgs/development/tools/rust/cargo-pgrx/buildPgrxExtension.nix +++ b/pkgs/development/tools/rust/cargo-pgrx/buildPgrxExtension.nix @@ -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)" @@ -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" diff --git a/pkgs/servers/sql/postgresql/buildPostgresqlExtension.nix b/pkgs/servers/sql/postgresql/buildPostgresqlExtension.nix index a427b7f880538a..c89e2b07784c86 100644 --- a/pkgs/servers/sql/postgresql/buildPostgresqlExtension.nix +++ b/pkgs/servers/sql/postgresql/buildPostgresqlExtension.nix @@ -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"}" diff --git a/pkgs/servers/sql/postgresql/ext/postgis.nix b/pkgs/servers/sql/postgresql/ext/postgis.nix index 556bfd030de7be..97b6f273378b1f 100644 --- a/pkgs/servers/sql/postgresql/ext/postgis.nix +++ b/pkgs/servers/sql/postgresql/ext/postgis.nix @@ -73,6 +73,7 @@ buildPostgresqlExtension (finalAttrs: { dontDisableStatic = true; nativeCheckInputs = [ + postgresql postgresqlTestHook cunit libxslt @@ -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" diff --git a/pkgs/servers/sql/postgresql/generic.nix b/pkgs/servers/sql/postgresql/generic.nix index ca157411536e18..05b873fcb0279a 100644 --- a/pkgs/servers/sql/postgresql/generic.nix +++ b/pkgs/servers/sql/postgresql/generic.nix @@ -41,6 +41,7 @@ let # passthru buildEnv, + buildPackages, newScope, nixosTests, postgresqlTestHook, @@ -125,8 +126,6 @@ let ) else stdenv; - - pg_config = writeShellScriptBin "pg_config" (builtins.readFile ./pg_config.sh); in stdenv'.mkDerivation (finalAttrs: { inherit version; @@ -323,8 +322,7 @@ 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 = @@ -332,12 +330,9 @@ let 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 @@ -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; diff --git a/pkgs/servers/sql/postgresql/libpq.nix b/pkgs/servers/sql/postgresql/libpq.nix index 90496d855a02e3..d584b32dae6bdb 100644 --- a/pkgs/servers/sql/postgresql/libpq.nix +++ b/pkgs/servers/sql/postgresql/libpq.nix @@ -17,6 +17,7 @@ # passthru / meta postgresql, + buildPackages, # GSSAPI gssSupport ? with stdenv.hostPlatform; !isWindows && !isStatic, @@ -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 @@ -135,6 +139,10 @@ stdenv.mkDerivation (finalAttrs: { doCheck = false; + passthru.pg_config = buildPackages.callPackage ./pg_config.nix { + inherit (finalAttrs) finalPackage; + }; + meta = { inherit (postgresql.meta) homepage diff --git a/pkgs/servers/sql/postgresql/pg_config.env.mk b/pkgs/servers/sql/postgresql/pg_config.env.mk new file mode 100644 index 00000000000000..48e836049b9057 --- /dev/null +++ b/pkgs/servers/sql/postgresql/pg_config.env.mk @@ -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' >>$@ diff --git a/pkgs/servers/sql/postgresql/pg_config.nix b/pkgs/servers/sql/postgresql/pg_config.nix new file mode 100644 index 00000000000000..04b71f6935afc9 --- /dev/null +++ b/pkgs/servers/sql/postgresql/pg_config.nix @@ -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; + }; +} diff --git a/pkgs/servers/sql/postgresql/pg_config.sh b/pkgs/servers/sql/postgresql/pg_config.sh index d6293999a12dcc..d56f8ca3dd61d9 100644 --- a/pkgs/servers/sql/postgresql/pg_config.sh +++ b/pkgs/servers/sql/postgresql/pg_config.sh @@ -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 <