on e94de40 help sign in
haskell.nix/builder/make-config-files.nix
189 lines 8.5 kB history raw
1{ stdenv, lib, haskellLib, ghc, nonReinstallablePkgs, runCommand, writeText, writeScript }:
3{ identifier, component, fullName, flags ? {}, needsProfiling ? false, chooseDrv ? drv: drv }:
5let
6 flagsAndConfig = field: xs: lib.optionalString (xs != []) ''
7 echo ${lib.concatStringsSep " " (map (x: "--${field}=${x}") xs)} >> $out/configure-flags
8 echo "${field}: ${lib.concatStringsSep " " xs}" >> $out/cabal.config
9 '';
11 target-pkg = "${ghc.targetPrefix}ghc-pkg";
13 # This is a bit of a hack. So we'll have a slightly longer explanation here:
15 # Every library component built with `comp-builder.nix` includes an `exactDep`
16 # and `envDep` directory with precomputed values used here.
17 # GHC derivations include `exactDep` and `envDep` directories that have
18 # the same information for each of the built in packages.
20 # exactDep will pass --exact-configuration to the `SETUP_HS configure` command.
21 # This requires us to pass --dependency={dep name}={pkg id}. The dependency
22 # name will usually be the name of the package `p`, which we can locate in the
23 # package-db, passed in via `pdbArg`. Thus querying the package-db for the
24 # id field for package `p`, will usually provide us with the right value. Sublibs
25 # need a bit of special handling:
26 #
27 # - Sublibs: if the dependency is a sublibrary of a package, we need to use
28 # the sublibrary's name for the dep name, and lookup the sublibraries
29 # pkg id for z-{pkg name}-z-{sublib name}. As we do not provide the
30 # sublib name to exactDep, as we don't have access to it at the call-site,
31 # we resort to a bit of globbing, which (as pkg db's should contain only
32 # a single package) work.
34 getLibComponent = dep:
35 dep.components.library # Regular package dependency
36 or dep; # or a sublib
38 # Work our suitable packageCfgDir subdirectory
39 isGhcjs = ghc.isGhcjs or false;
40 ghcCommand' = if isGhcjs then "ghcjs" else "ghc";
41 ghcCommand = "${ghc.targetPrefix}${ghcCommand'}";
42 ghcCommandCaps = lib.toUpper ghcCommand';
43 libDir = "lib/${ghcCommand}-${ghc.version}";
44 packageCfgDir = "${libDir}/package.conf.d";
46 # Filters out only library packages that for this GHC target
47 # TODO investigate why this is needed
48 # TODO find out why p ? configFiles helps (for instance for `R1909.aarch64-unknown-linux-gnu.tests.cabal-22.run.x86_64-linux`)
49 libDeps = map chooseDrv (
50 (if needsProfiling then (x: map (p: p.profiled or p) x) else x: x)
51 (lib.filter (p: (p ? configFiles) && p.configFiles.targetPrefix == ghc.targetPrefix)
52 (map getLibComponent component.depends))
53 );
54 cfgFiles =
55 let xs = map
56 (p: "${p.configFiles}")
57 libDeps;
58 in lib.concatStringsSep "\" \"" xs;
59 libs = lib.concatMapStringsSep "\" \"" (p: "${p}") libDeps;
60in
61 runCommand "${ghc.targetPrefix}${fullName}-config" {
62 nativeBuildInputs = [ghc];
63 passthru = {
64 inherit (ghc) targetPrefix;
65 inherit ghcCommand ghcCommandCaps libDir packageCfgDir;
66 };
67 } (''
68 mkdir -p $out
70 ${target-pkg} init $out/${packageCfgDir}
72 ${lib.concatStringsSep "\n" (lib.mapAttrsToList flagsAndConfig {
73 "extra-lib-dirs" = map (p: "${lib.getLib p}/lib") component.libs;
74 "extra-include-dirs" = map (p: "${lib.getDev p}/include") component.libs;
75 "extra-framework-dirs" = map (p: "${p}/Library/Frameworks") component.frameworks;
76 })}
78 ghc=${ghc}
79 ${ # Copy over the nonReinstallablePkgs from the global package db.
80 ''
81 for p in ${lib.concatStringsSep " " nonReinstallablePkgs}; do
82 find $ghc/lib/${ghc.name}/package.conf.d -name $p'*.conf' -exec cp -f {} $out/${packageCfgDir} \;
83 done
84 ''}
86 for l in "${cfgFiles}"; do
87 if [ -n "$l" ]; then
88 cp -f "$l/${packageCfgDir}/"*.conf $out/${packageCfgDir}
89 fi
90 done
91 for l in "${libs}"; do
92 if [ -n "$l" ]; then
93 cp -f "$l/package.conf.d/"*.conf $out/${packageCfgDir}
94 fi
95 done
97 ${ # Note: we pass `clear` first to ensure that we never consult the implicit global package db.
98 flagsAndConfig "package-db" ["clear" "$out/${packageCfgDir}"]
99 }
101 echo ${lib.concatStringsSep " " (lib.mapAttrsToList (fname: val: "--flags=${lib.optionalString (!val) "-" + fname}") flags)} >> $out/configure-flags
103 ${ # Provide a cabal config without remote package repositories
104 ''
105 echo "write-ghc-environment-files: never" >> $out/cabal.config
106 ''}
108 ${ # Provide a GHC environment file
109 ''
110 cat > $out/ghc-environment <<EOF
111 package-db $out/${packageCfgDir}
112 EOF
113 ''}
115 ${ lib.optionalString component.doExactConfig ''
116 echo "--exact-configuration" >> $out/configure-flags
117 echo "allow-newer: ${identifier.name}:*" >> $out/cabal.config
118 echo "allow-older: ${identifier.name}:*" >> $out/cabal.config
119 ''}
121 for p in ${lib.concatStringsSep " " libDeps}; do
122 cat $p/envDep >> $out/ghc-environment
123 ${ lib.optionalString component.doExactConfig ''
124 cat $p/exactDep/configure-flags >> $out/configure-flags
125 cat $p/exactDep/cabal.config >> $out/cabal.config
126 ''}
127 done
128 for p in ${lib.concatStringsSep " " (lib.remove "ghc" nonReinstallablePkgs)}; do
129 if [ -e $ghc/envDeps/$p ]; then
130 cat $ghc/envDeps/$p >> $out/ghc-environment
131 fi
132 done
133 '' + lib.optionalString component.doExactConfig ''
134 for p in ${lib.concatStringsSep " " nonReinstallablePkgs}; do
135 if [ -e $ghc/exactDeps/$p ]; then
136 cat $ghc/exactDeps/$p/configure-flags >> $out/configure-flags
137 cat $ghc/exactDeps/$p/cabal.config >> $out/cabal.config
138 fi
139 done
140 ''
141 # This code originates in the `generic-builder.nix` from nixpkgs. However GHC has been fixed
142 # to drop unused libraries referenced from libraries; and this patch is usually included in the
143 # nixpkgs's GHC builds. This doesn't sadly make this stupid hack unnecessary. It resurfaces in
144 # the form of Cabal trying to be smart. Cabal when linking a library figures out that you likely
145 # need those `rpath` entries, and passes `-optl-Wl,-rpath,...` for each dynamic library path to
146 # GHC, thus subverting the linker and forcing it to insert all those RPATHs weather or not they
147 # are needed. We therefore reuse the linker hack here to move all dynamic libraries into a
148 # common folder (as links) and thus prevent Cabal from going nuts.
149 #
150 # TODO: Fix Cabal.
151 # TODO: this is only needed if we do dynamic libraries.
152 #
153 # NOTE [ln -s -f]: we force link, as we may have dependencies that contain shared deps
154 # (e.g. libiconv), and thus we don't want to fail, but just link it again.
155 #
156 # Confusing sed stuff:
157 # '/^ ./{H;$!d} ; x' Groups lines that start with a space with the initial
158 # line of a block. Needs a blank line added to the file
159 # to terminate the last block.
160 # 's/ /\n/g ; s/\n\n*/\n/g; s/^\n//;' Puts each field on its own line.
161 # 's|/nix/store/|''${pkgroot}/../../../|' Convert store path to pkgroot relative path
162 # 's|''${pkgroot}/../../../|/nix/store/|' Convert pkgroot relative path to store path
164 # Work around a limit in the macOS Sierra linker on the number of paths
165 # referenced by any one dynamic library:
166 #
167 # Create a local directory with symlinks of the *.dylib (macOS shared
168 # libraries) from all the dependencies.
169 + lib.optionalString stdenv.isDarwin ''
170 local dynamicLinksDir="$out/lib/links"
171 mkdir -p $dynamicLinksDir
172 # Enumerate dynamic-library-dirs with ''${pkgroot} expanded.
173 local dirsToLink=$(
174 for f in "$out/${packageCfgDir}/"*.conf; do
175 (cat $f; echo) | sed -En '/^ ./{H;$!d} ; x ; /^dynamic-library-dirs:/ {s/^dynamic-library-dirs:// ; s/ /\n/g ; s/\n\n*/\n/g; s/^\n//; p}'
176 done | sed 's|''${pkgroot}/../../../|/nix/store/|' | sort -u
177 )
178 for d in $dirsToLink; do
179 ln -f -s "$d/"*.{a,dylib,so} $dynamicLinksDir
180 done
181 # Edit the local package DB to reference the links directory.
182 for f in "$out/${packageCfgDir}/"*.conf; do
183 chmod +w $f
184 echo >> $f
185 sed -i -E "/^ ./{H;$!d} ; x ; s,^dynamic-library-dirs:.*,dynamic-library-dirs: $dynamicLinksDir," $f
186 done
187 '' + ''
188 ${target-pkg} -v0 --package-db $out/${packageCfgDir} recache
189 '')