1# PostgreSQL's build system for extensions (PGXS) makes the following assumptions:
2# - All extensions will be installed in the same prefix as PostgreSQL itself.
3# - pg_config is able to return the correct paths for bindir/libdir/datadir etc.
4#
5# Both of those assumptions break with nix. Since each extension is a separate
6# derivation, we need to put all its files into a different folder. At the same
7# time, pg_config only points to the PostgreSQL derivation's paths.
8#
9# When building extensions, the paths provided by pg_config are used for two
10# purposes:
11# - To find postgres libs and headers and reference those paths via -L and -I flags.
12# - To determine the correct install directory.
13#
14# The PGXS Makefiles also support an environment variable DESTDIR, which is added as
15# a prefix to all install locations. This is primarily used for temporary installs
16# while running the test suite. Since pg_config returns absolute paths to /nix/store
17# for us, using DESTDIR will result in install locations of the form:
18# $DESTIDR/nix/store/<postgresql-output>/...
19#
20# In multiple iterations, the following approaches have been tried to work around all
21# of this:
22# 1. For a long time, all extensions in nixpkgs just overwrote the installPhase
23# and moved the respective files to the correct location manually. This approach
24# is not maintainable, because whenever upstream adds a new file, we'd have to
25# make sure the file is correctly installed as well. Additionally, it makes adding
26# a new extension harder than it should be.
27#
28# 2. A wrapper around pg_config could just replace the returned paths with paths to
29# $out of currently building derivation, i.e. the extension. This works for install-
30# ation, but breaks for any of the libs and headers the extension needs from postgres
31# itself.
32#
33# 3. A variation of 2., but make the pg_config wrapper only return the changed paths
34# during the installPahse. During configure and build, it would return the regular
35# paths to the PostgreSQL derivation. This works better, but not for every case.
36# Some extensions try to be smarter and search for the "postgres" binary to deduce
37# the necessary paths from that. Those would still need special handling.
38#
39# 4. Use the fact that DESTDIR is prepended to every installation directory - and only
40# there, to run a replacement of all Makefiles in postgres' lib/pgxs/ folder and
41# all Makefiles in the extension's source. "$DESTDIR/$bindir" can be replaced with
42# "$out/bin" etc. - thus mapping the installPhase directly into the right output.
43# This works beautifully - for the majority of cases. But it doesn't work for
44# some extensions that use CMake. And it doesn't work for some extensions that use
45# custom variables instead of the default "bindir" and friends.
46#
47# 5. Just set DESTDIR to the extensions's output and then clean up afterward. This will
48# result in paths like this:
49# /nix/store/<extension-output>/nix/store/<postgresql-output>/...
50# Directly after the installPhase, we'll move the files in the right folder.
51# This seems to work consistently across all extensions we have in nixpkgs right now.
52# Of course, it would break down for any extension that doesn't support DESTDIR -
53# but that just means PGXS is not used either, so that's OK.
54#
55# This last approach is the one we're taking in this file. To make sure the removal of the
56# nested nix/store happens immediately after the installPhase, before any other postInstall
57# hooks run, this needs to be run in an override of `mkDerivation` and not in a setup hook.
58
59{
60 lib,
61 stdenv,
62 postgresql,
63 nix-update-script,
64}:
65
66args:
67
68let
69 buildPostgresqlExtension =
70 finalAttrs:
71 {
72 enableUpdateScript ? true,
73 ...
74 }@prevAttrs:
75 {
76 passthru =
77 prevAttrs.passthru or { }
78 // lib.optionalAttrs enableUpdateScript {
79 updateScript =
80 prevAttrs.passthru.updateScript or (nix-update-script (
81 lib.optionalAttrs (lib.hasInfix "unstable" prevAttrs.version) {
82 extraArgs = [ "--version=branch" ];
83 }
84 ));
85 };
86
87 buildInputs = [ postgresql ] ++ prevAttrs.buildInputs or [ ];
88
89 installFlags = [
90 "DESTDIR=${placeholder "out"}"
91 ] ++ prevAttrs.installFlags or [ ];
92
93 postInstall =
94 ''
95 # DESTDIR + pg_config install the files into
96 # /nix/store/<extension>/nix/store/<postgresql>/...
97 # We'll now remove the /nix/store/<postgresql> part:
98 if [[ -d "$out${postgresql}" ]]; then
99 cp -alt "$out" "$out${postgresql}"/*
100 rm -r "$out${postgresql}"
101 fi
102
103 if [[ -d "$out${postgresql.dev}" ]]; then
104 mkdir -p "''${dev:-$out}"
105 cp -alt "''${dev:-$out}" "$out${postgresql.dev}"/*
106 rm -r "$out${postgresql.dev}"
107 fi
108
109 if [[ -d "$out${postgresql.lib}" ]]; then
110 mkdir -p "''${lib:-$out}"
111 cp -alt "''${lib:-$out}" "$out${postgresql.lib}"/*
112 rm -r "$out${postgresql.lib}"
113 fi
114
115 if [[ -d "$out${postgresql.doc}" ]]; then
116 mkdir -p "''${doc:-$out}"
117 cp -alt "''${doc:-$out}" "$out${postgresql.doc}"/*
118 rm -r "$out${postgresql.doc}"
119 fi
120
121 if [[ -d "$out${postgresql.man}" ]]; then
122 mkdir -p "''${man:-$out}"
123 cp -alt "''${man:-$out}" "$out${postgresql.man}"/*
124 rm -r "$out${postgresql.man}"
125 fi
126
127 # In some cases (postgis) parts of the install script
128 # actually work "OK", before we add DESTDIR, so some
129 # files end up in
130 # /nix/store/<extension>/nix/store/<extension>/...
131 if [[ -d "$out$out" ]]; then
132 cp -alt "$out" "$out$out"/*
133 rm -r "$out$out"
134 fi
135
136 if [[ -d "$out/nix/store" ]]; then
137 if ! rmdir "$out/nix/store" "$out/nix"; then
138 find "$out/nix"
139 nixErrorLog 'Found left-overs in $out/nix/store, make sure to move them into $out properly.'
140 exit 1
141 fi
142 fi
143 ''
144 + prevAttrs.postInstall or "";
145 };
146in
147stdenv.mkDerivation (lib.extends buildPostgresqlExtension (lib.toFunction args))