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
66lib.extendMkDerivation {
67 constructDrv = stdenv.mkDerivation;
68
69 excludeDrvArgNames = [
70 "enableUpdateScript"
71 ];
72
73 extendDrvArgs =
74 finalAttrs:
75 {
76 enableUpdateScript ? true,
77 ...
78 }@prevAttrs:
79 {
80 passthru =
81 prevAttrs.passthru or { }
82 // lib.optionalAttrs enableUpdateScript {
83 updateScript =
84 prevAttrs.passthru.updateScript or (nix-update-script (
85 lib.optionalAttrs (lib.hasInfix "unstable" prevAttrs.version) {
86 extraArgs = [ "--version=branch" ];
87 }
88 ));
89 };
90
91 strictDeps = true;
92 buildInputs = [ postgresql ] ++ prevAttrs.buildInputs or [ ];
93 nativeBuildInputs = [ postgresql.pg_config ] ++ prevAttrs.nativeBuildInputs or [ ];
94
95 installFlags = [
96 "DESTDIR=${placeholder "out"}"
97 ]
98 ++ prevAttrs.installFlags or [ ];
99
100 postInstall = ''
101 # DESTDIR + pg_config install the files into
102 # /nix/store/<extension>/nix/store/<postgresql>/...
103 # We'll now remove the /nix/store/<postgresql> part:
104 if [[ -d "$out${postgresql}" ]]; then
105 cp -alt "$out" "$out${postgresql}"/*
106 rm -r "$out${postgresql}"
107 fi
108
109 if [[ -d "$out${postgresql.dev}" ]]; then
110 mkdir -p "''${dev:-$out}"
111 cp -alt "''${dev:-$out}" "$out${postgresql.dev}"/*
112 rm -r "$out${postgresql.dev}"
113 fi
114
115 if [[ -d "$out${postgresql.lib}" ]]; then
116 mkdir -p "''${lib:-$out}"
117 cp -alt "''${lib:-$out}" "$out${postgresql.lib}"/*
118 rm -r "$out${postgresql.lib}"
119 fi
120
121 if [[ -d "$out${postgresql.doc}" ]]; then
122 mkdir -p "''${doc:-$out}"
123 cp -alt "''${doc:-$out}" "$out${postgresql.doc}"/*
124 rm -r "$out${postgresql.doc}"
125 fi
126
127 if [[ -d "$out${postgresql.man}" ]]; then
128 mkdir -p "''${man:-$out}"
129 cp -alt "''${man:-$out}" "$out${postgresql.man}"/*
130 rm -r "$out${postgresql.man}"
131 fi
132
133 # In some cases (postgis) parts of the install script
134 # actually work "OK", before we add DESTDIR, so some
135 # files end up in
136 # /nix/store/<extension>/nix/store/<extension>/...
137 if [[ -d "$out$out" ]]; then
138 cp -alt "$out" "$out$out"/*
139 rm -r "$out$out"
140 fi
141
142 if [[ -d "$out/nix/store" ]]; then
143 if ! rmdir "$out/nix/store" "$out/nix"; then
144 find "$out/nix"
145 nixErrorLog 'Found left-overs in $out/nix/store, make sure to move them into $out properly.'
146 exit 1
147 fi
148 fi
149 ''
150 + prevAttrs.postInstall or "";
151 };
152}