1{ stdenv, fetchurl, ghc, pkgconfig, glibcLocales, coreutils, gnugrep, gnused
2, jailbreak-cabal, hscolour, cpphs, nodejs, lib, removeReferencesTo
3}:
4let isCross = (ghc.cross or null) != null; in
5
6{ pname
7, dontStrip ? (ghc.isGhcjs or false)
8, version, revision ? null
9, sha256 ? null
10, src ? fetchurl { url = "mirror://hackage/${pname}-${version}.tar.gz"; inherit sha256; }
11, buildDepends ? [], setupHaskellDepends ? [], libraryHaskellDepends ? [], executableHaskellDepends ? []
12, buildTarget ? ""
13, buildTools ? [], libraryToolDepends ? [], executableToolDepends ? [], testToolDepends ? [], benchmarkToolDepends ? []
14, configureFlags ? []
15, description ? ""
16, doCheck ? !isCross && (stdenv.lib.versionOlder "7.4" ghc.version)
17, withBenchmarkDepends ? false
18, doHoogle ? true
19, editedCabalFile ? null
20, enableLibraryProfiling ? false
21, enableExecutableProfiling ? false
22# TODO enable shared libs for cross-compiling
23, enableSharedExecutables ? !isCross && (((ghc.isGhcjs or false) || stdenv.lib.versionOlder "7.7" ghc.version))
24, enableSharedLibraries ? !isCross && (((ghc.isGhcjs or false) || stdenv.lib.versionOlder "7.7" ghc.version))
25, enableSplitObjs ? null # OBSOLETE, use enableDeadCodeElimination
26, enableDeadCodeElimination ? (!stdenv.isDarwin) # TODO: use -dead_strip for darwin
27, enableStaticLibraries ? true
28, extraLibraries ? [], librarySystemDepends ? [], executableSystemDepends ? []
29, homepage ? "http://hackage.haskell.org/package/${pname}"
30, platforms ? ghc.meta.platforms
31, hydraPlatforms ? platforms
32, hyperlinkSource ? true
33, isExecutable ? false, isLibrary ? !isExecutable
34, jailbreak ? false
35, license
36, maintainers ? []
37, doCoverage ? false
38, doHaddock ? !(ghc.isHaLVM or false)
39, passthru ? {}
40, pkgconfigDepends ? [], libraryPkgconfigDepends ? [], executablePkgconfigDepends ? [], testPkgconfigDepends ? [], benchmarkPkgconfigDepends ? []
41, testDepends ? [], testHaskellDepends ? [], testSystemDepends ? []
42, benchmarkDepends ? [], benchmarkHaskellDepends ? [], benchmarkSystemDepends ? []
43, testTarget ? ""
44, broken ? false
45, preCompileBuildDriver ? "", postCompileBuildDriver ? ""
46, preUnpack ? "", postUnpack ? ""
47, patches ? [], patchPhase ? "", prePatch ? "", postPatch ? ""
48, preConfigure ? "", postConfigure ? ""
49, preBuild ? "", postBuild ? ""
50, installPhase ? "", preInstall ? "", postInstall ? ""
51, checkPhase ? "", preCheck ? "", postCheck ? ""
52, preFixup ? "", postFixup ? ""
53, shellHook ? ""
54, coreSetup ? false # Use only core packages to build Setup.hs.
55, useCpphs ? false
56, hardeningDisable ? lib.optional (ghc.isHaLVM or false) "all"
57, enableSeparateDataOutput ? false
58, enableSeparateDocOutput ? doHaddock
59} @ args:
60
61assert editedCabalFile != null -> revision != null;
62# OBSOLETE, use enableDeadCodeElimination
63assert enableSplitObjs == null;
64
65let
66
67 inherit (stdenv.lib) optional optionals optionalString versionOlder versionAtLeast
68 concatStringsSep enableFeature optionalAttrs toUpper
69 filter makeLibraryPath;
70
71 isGhcjs = ghc.isGhcjs or false;
72 isHaLVM = ghc.isHaLVM or false;
73 packageDbFlag = if isGhcjs || isHaLVM || versionOlder "7.6" ghc.version
74 then "package-db"
75 else "package-conf";
76
77 nativeGhc = if isCross || isGhcjs then ghc.bootPkgs.ghc else ghc;
78 nativePackageDbFlag = if versionOlder "7.6" nativeGhc.version
79 then "package-db"
80 else "package-conf";
81
82 # the target dir for haddock documentation
83 docdir = docoutput: docoutput + "/share/doc";
84
85 newCabalFileUrl = "http://hackage.haskell.org/package/${pname}-${version}/revision/${revision}.cabal";
86 newCabalFile = fetchurl {
87 url = newCabalFileUrl;
88 sha256 = editedCabalFile;
89 name = "${pname}-${version}-r${revision}.cabal";
90 };
91
92 defaultSetupHs = builtins.toFile "Setup.hs" ''
93 import Distribution.Simple
94 main = defaultMain
95 '';
96
97 hasActiveLibrary = isLibrary && (enableStaticLibraries || enableSharedLibraries || enableLibraryProfiling);
98
99 # We cannot enable -j<n> parallelism for libraries because GHC is far more
100 # likely to generate a non-determistic library ID in that case. Further
101 # details are at <https://github.com/peti/ghc-library-id-bug>.
102 enableParallelBuilding = (versionOlder "7.8" ghc.version && !hasActiveLibrary) || versionOlder "8.0.1" ghc.version;
103
104 crossCabalFlags = [
105 "--with-ghc=${ghc.cross.config}-ghc"
106 "--with-ghc-pkg=${ghc.cross.config}-ghc-pkg"
107 "--with-gcc=${ghc.cc}"
108 "--with-ld=${ghc.ld}"
109 "--with-hsc2hs=${nativeGhc}/bin/hsc2hs"
110 ] ++ (if isHaLVM then [] else ["--hsc2hs-options=--cross-compile"]);
111
112 crossCabalFlagsString =
113 stdenv.lib.optionalString isCross (" " + stdenv.lib.concatStringsSep " " crossCabalFlags);
114
115 defaultConfigureFlags = [
116 "--verbose" "--prefix=$out" "--libdir=\\$prefix/lib/\\$compiler" "--libsubdir=\\$pkgid"
117 (optionalString enableSeparateDataOutput "--datadir=$data/share/${ghc.name}")
118 (optionalString enableSeparateDocOutput "--docdir=${docdir "$doc"}")
119 "--with-gcc=$CC" # Clang won't work without that extra information.
120 "--package-db=$packageConfDir"
121 (optionalString (enableSharedExecutables && stdenv.isLinux) "--ghc-option=-optl=-Wl,-rpath=$out/lib/${ghc.name}/${pname}-${version}")
122 (optionalString (enableSharedExecutables && stdenv.isDarwin) "--ghc-option=-optl=-Wl,-headerpad_max_install_names")
123 (optionalString enableParallelBuilding "--ghc-option=-j$NIX_BUILD_CORES")
124 (optionalString useCpphs "--with-cpphs=${cpphs}/bin/cpphs --ghc-options=-cpp --ghc-options=-pgmP${cpphs}/bin/cpphs --ghc-options=-optP--cpp")
125 (enableFeature (enableDeadCodeElimination && (versionAtLeast "8.0.1" ghc.version)) "split-objs")
126 (enableFeature enableLibraryProfiling "library-profiling")
127 (enableFeature enableExecutableProfiling (if versionOlder ghc.version "8" then "executable-profiling" else "profiling"))
128 (enableFeature enableSharedLibraries "shared")
129 (optionalString (versionAtLeast ghc.version "7.10") (enableFeature doCoverage "coverage"))
130 (optionalString (isGhcjs || versionOlder "7" ghc.version) (enableFeature enableStaticLibraries "library-vanilla"))
131 (optionalString (isGhcjs || versionOlder "7.4" ghc.version) (enableFeature enableSharedExecutables "executable-dynamic"))
132 (optionalString (isGhcjs || versionOlder "7" ghc.version) (enableFeature doCheck "tests"))
133 ] ++ optionals (enableDeadCodeElimination && (stdenv.lib.versionOlder "8.0.1" ghc.version)) [
134 "--ghc-option=-split-sections"
135 ] ++ optionals isGhcjs [
136 "--ghcjs"
137 ] ++ optionals isCross ([
138 "--configure-option=--host=${ghc.cross.config}"
139 ] ++ crossCabalFlags);
140
141 setupCompileFlags = [
142 (optionalString (!coreSetup) "-${packageDbFlag}=$packageConfDir")
143 (optionalString isGhcjs "-build-runner")
144 (optionalString (isGhcjs || isHaLVM || versionOlder "7.8" ghc.version) "-j$NIX_BUILD_CORES")
145 # https://github.com/haskell/cabal/issues/2398
146 (optionalString (versionOlder "7.10" ghc.version && !isHaLVM) "-threaded")
147 ];
148
149 isHaskellPkg = x: (x ? pname) && (x ? version) && (x ? env);
150 isSystemPkg = x: !isHaskellPkg x;
151
152 allPkgconfigDepends = pkgconfigDepends ++ libraryPkgconfigDepends ++ executablePkgconfigDepends ++
153 optionals doCheck testPkgconfigDepends ++ optionals withBenchmarkDepends benchmarkPkgconfigDepends;
154
155 nativeBuildInputs = buildTools ++ libraryToolDepends ++ executableToolDepends ++ [ removeReferencesTo ];
156 propagatedBuildInputs = buildDepends ++ libraryHaskellDepends ++ executableHaskellDepends;
157 otherBuildInputs = setupHaskellDepends ++ extraLibraries ++ librarySystemDepends ++ executableSystemDepends ++
158 optionals (allPkgconfigDepends != []) ([pkgconfig] ++ allPkgconfigDepends) ++
159 optionals doCheck (testDepends ++ testHaskellDepends ++ testSystemDepends ++ testToolDepends) ++
160 # ghcjs's hsc2hs calls out to the native hsc2hs
161 optional isGhcjs nativeGhc ++
162 optionals withBenchmarkDepends (benchmarkDepends ++ benchmarkHaskellDepends ++ benchmarkSystemDepends ++ benchmarkToolDepends);
163 allBuildInputs = propagatedBuildInputs ++ otherBuildInputs;
164
165 haskellBuildInputs = stdenv.lib.filter isHaskellPkg allBuildInputs;
166 systemBuildInputs = stdenv.lib.filter isSystemPkg allBuildInputs;
167
168 ghcEnv = ghc.withPackages (p: haskellBuildInputs);
169
170 setupBuilder = if isCross then "${nativeGhc}/bin/ghc" else ghcCommand;
171 setupCommand = "./Setup";
172 ghcCommand' = if isGhcjs then "ghcjs" else "ghc";
173 crossPrefix = if (ghc.cross or null) != null then "${ghc.cross.config}-" else "";
174 ghcCommand = "${crossPrefix}${ghcCommand'}";
175 ghcCommandCaps= toUpper ghcCommand';
176
177in
178
179assert allPkgconfigDepends != [] -> pkgconfig != null;
180
181stdenv.mkDerivation ({
182 name = "${pname}-${version}";
183
184 outputs = if (args ? outputs) then args.outputs else ([ "out" ] ++ (optional enableSeparateDataOutput "data") ++ (optional enableSeparateDocOutput "doc"));
185 setOutputFlags = false;
186
187 pos = builtins.unsafeGetAttrPos "pname" args;
188
189 prePhases = ["setupCompilerEnvironmentPhase"];
190 preConfigurePhases = ["compileBuildDriverPhase"];
191 preInstallPhases = ["haddockPhase"];
192
193 inherit src;
194
195 inherit nativeBuildInputs;
196 buildInputs = otherBuildInputs ++ optionals (!hasActiveLibrary) propagatedBuildInputs;
197 propagatedBuildInputs = optionals hasActiveLibrary propagatedBuildInputs;
198
199 LANG = "en_US.UTF-8"; # GHC needs the locale configured during the Haddock phase.
200
201 prePatch = optionalString (editedCabalFile != null) ''
202 echo "Replace Cabal file with edited version from ${newCabalFileUrl}."
203 cp ${newCabalFile} ${pname}.cabal
204 '' + prePatch;
205
206 postPatch = optionalString jailbreak ''
207 echo "Run jailbreak-cabal to lift version restrictions on build inputs."
208 ${jailbreak-cabal}/bin/jailbreak-cabal ${pname}.cabal
209 '' + postPatch;
210
211 setupCompilerEnvironmentPhase = ''
212 runHook preSetupCompilerEnvironment
213
214 echo "Build with ${ghc}."
215 export PATH="${ghc}/bin:$PATH"
216 ${optionalString (hasActiveLibrary && hyperlinkSource) "export PATH=${hscolour}/bin:$PATH"}
217
218 packageConfDir="$TMPDIR/package.conf.d"
219 mkdir -p $packageConfDir
220
221 setupCompileFlags="${concatStringsSep " " setupCompileFlags}"
222 configureFlags="${concatStringsSep " " defaultConfigureFlags} $configureFlags"
223
224 # nativePkgs defined in stdenv/setup.hs
225 for p in "''${nativePkgs[@]}"; do
226 if [ -d "$p/lib/${ghc.name}/package.conf.d" ]; then
227 cp -f "$p/lib/${ghc.name}/package.conf.d/"*.conf $packageConfDir/
228 continue
229 fi
230 if [ -d "$p/include" ]; then
231 configureFlags+=" --extra-include-dirs=$p/include"
232 fi
233 if [ -d "$p/lib" ]; then
234 configureFlags+=" --extra-lib-dirs=$p/lib"
235 fi
236 done
237 '' + (optionalString stdenv.isDarwin ''
238 # Work around a limit in the macOS Sierra linker on the number of paths
239 # referenced by any one dynamic library:
240 #
241 # Create a local directory with symlinks of the *.dylib (macOS shared
242 # libraries) from all the dependencies.
243 local dynamicLinksDir="$out/lib/links"
244 mkdir -p $dynamicLinksDir
245 for d in $(grep dynamic-library-dirs "$packageConfDir/"*|awk '{print $2}'); do
246 ln -s "$d/"*.dylib $dynamicLinksDir
247 done
248 # Edit the local package DB to reference the links directory.
249 for f in "$packageConfDir/"*.conf; do
250 sed -i "s,dynamic-library-dirs: .*,dynamic-library-dirs: $dynamicLinksDir," $f
251 done
252 '') + ''
253 ${ghcCommand}-pkg --${packageDbFlag}="$packageConfDir" recache
254
255 runHook postSetupCompilerEnvironment
256 '';
257
258 compileBuildDriverPhase = ''
259 runHook preCompileBuildDriver
260
261 for i in Setup.hs Setup.lhs ${defaultSetupHs}; do
262 test -f $i && break
263 done
264
265 echo setupCompileFlags: $setupCompileFlags
266 ${setupBuilder} $setupCompileFlags --make -o Setup -odir $TMPDIR -hidir $TMPDIR $i
267
268 runHook postCompileBuildDriver
269 '';
270
271 configurePhase = ''
272 runHook preConfigure
273
274 unset GHC_PACKAGE_PATH # Cabal complains if this variable is set during configure.
275
276 echo configureFlags: $configureFlags
277 ${setupCommand} configure $configureFlags 2>&1 | ${coreutils}/bin/tee "$NIX_BUILD_TOP/cabal-configure.log"
278 if ${gnugrep}/bin/egrep -q '^Warning:.*depends on multiple versions' "$NIX_BUILD_TOP/cabal-configure.log"; then
279 echo >&2 "*** abort because of serious configure-time warning from Cabal"
280 exit 1
281 fi
282
283 export GHC_PACKAGE_PATH="$packageConfDir:"
284
285 runHook postConfigure
286 '';
287
288 buildPhase = ''
289 runHook preBuild
290 ${setupCommand} build ${buildTarget}${crossCabalFlagsString}
291 runHook postBuild
292 '';
293
294 checkPhase = ''
295 runHook preCheck
296 ${setupCommand} test ${testTarget}
297 runHook postCheck
298 '';
299
300 haddockPhase = ''
301 runHook preHaddock
302 ${optionalString (doHaddock && hasActiveLibrary) ''
303 ${setupCommand} haddock --html \
304 ${optionalString doHoogle "--hoogle"} \
305 ${optionalString (hasActiveLibrary && hyperlinkSource) "--hyperlink-source"}
306 ''}
307 runHook postHaddock
308 '';
309
310 installPhase = ''
311 runHook preInstall
312
313 ${if !hasActiveLibrary then "${setupCommand} install" else ''
314 ${setupCommand} copy
315 local packageConfDir="$out/lib/${ghc.name}/package.conf.d"
316 local packageConfFile="$packageConfDir/${pname}-${version}.conf"
317 mkdir -p "$packageConfDir"
318 ${setupCommand} register --gen-pkg-config=$packageConfFile
319 local pkgId=$( ${gnused}/bin/sed -n -e 's|^id: ||p' $packageConfFile )
320 mv $packageConfFile $packageConfDir/$pkgId.conf
321 ''}
322 ${optionalString isGhcjs ''
323 for exeDir in "$out/bin/"*.jsexe; do
324 exe="''${exeDir%.jsexe}"
325 printWords '#!${nodejs}/bin/node' > "$exe"
326 cat "$exeDir/all.js" >> "$exe"
327 chmod +x "$exe"
328 done
329 ''}
330 ${optionalString doCoverage "mkdir -p $out/share && cp -r dist/hpc $out/share"}
331 ${optionalString (enableSharedExecutables && isExecutable && !isGhcjs && stdenv.isDarwin && stdenv.lib.versionOlder ghc.version "7.10") ''
332 for exe in "$out/bin/"* ; do
333 install_name_tool -add_rpath "$out/lib/ghc-${ghc.version}/${pname}-${version}" "$exe"
334 done
335 ''}
336
337 ${optionalString enableSeparateDocOutput ''
338 for x in ${docdir "$doc"}/html/src/*.html; do
339 remove-references-to -t $out $x
340 done
341 mkdir -p $doc
342 ''}
343 ${optionalString enableSeparateDataOutput "mkdir -p $data"}
344
345 runHook postInstall
346 '';
347
348 passthru = passthru // {
349
350 inherit pname version;
351
352 isHaskellLibrary = hasActiveLibrary;
353
354 # TODO: ask why the split outputs are configurable at all?
355 # TODO: include tests for split if possible
356 # Given the haskell package, returns
357 # the directory containing the haddock documentation.
358 # `null' if no haddock documentation was built.
359 # TODO: fetch the self from the fixpoint instead
360 haddockDir = self: if doHaddock then "${docdir self.doc}/html" else null;
361
362 env = stdenv.mkDerivation {
363 name = "interactive-${pname}-${version}-environment";
364 nativeBuildInputs = [ ghcEnv systemBuildInputs ]
365 ++ optional isGhcjs ghc."socket.io"; # for ghcjsi
366 LANG = "en_US.UTF-8";
367 LOCALE_ARCHIVE = optionalString stdenv.isLinux "${glibcLocales}/lib/locale/locale-archive";
368 shellHook = ''
369 export NIX_${ghcCommandCaps}="${ghcEnv}/bin/${ghcCommand}"
370 export NIX_${ghcCommandCaps}PKG="${ghcEnv}/bin/${ghcCommand}-pkg"
371 # TODO: is this still valid?
372 export NIX_${ghcCommandCaps}_DOCDIR="${ghcEnv}/share/doc/ghc/html"
373 export LD_LIBRARY_PATH="''${LD_LIBRARY_PATH:+''${LD_LIBRARY_PATH}:}${
374 makeLibraryPath (filter (x: !isNull x) systemBuildInputs)
375 }"
376 ${if isHaLVM
377 then ''export NIX_${ghcCommandCaps}_LIBDIR="${ghcEnv}/lib/HaLVM-${ghc.version}"''
378 else ''export NIX_${ghcCommandCaps}_LIBDIR="${ghcEnv}/lib/${ghcCommand}-${ghc.version}"''}
379 ${shellHook}
380 '';
381 };
382 };
383
384 meta = { inherit homepage license platforms; }
385 // optionalAttrs broken { inherit broken; }
386 // optionalAttrs (description != "") { inherit description; }
387 // optionalAttrs (maintainers != []) { inherit maintainers; }
388 // optionalAttrs (hydraPlatforms != platforms) { inherit hydraPlatforms; }
389 ;
390
391}
392// optionalAttrs (preCompileBuildDriver != "") { inherit preCompileBuildDriver; }
393// optionalAttrs (postCompileBuildDriver != "") { inherit postCompileBuildDriver; }
394// optionalAttrs (preUnpack != "") { inherit preUnpack; }
395// optionalAttrs (postUnpack != "") { inherit postUnpack; }
396// optionalAttrs (configureFlags != []) { inherit configureFlags; }
397// optionalAttrs (patches != []) { inherit patches; }
398// optionalAttrs (patchPhase != "") { inherit patchPhase; }
399// optionalAttrs (preConfigure != "") { inherit preConfigure; }
400// optionalAttrs (postConfigure != "") { inherit postConfigure; }
401// optionalAttrs (preBuild != "") { inherit preBuild; }
402// optionalAttrs (postBuild != "") { inherit postBuild; }
403// optionalAttrs (doCheck) { inherit doCheck; }
404// optionalAttrs (withBenchmarkDepends) { inherit withBenchmarkDepends; }
405// optionalAttrs (checkPhase != "") { inherit checkPhase; }
406// optionalAttrs (preCheck != "") { inherit preCheck; }
407// optionalAttrs (postCheck != "") { inherit postCheck; }
408// optionalAttrs (preInstall != "") { inherit preInstall; }
409// optionalAttrs (installPhase != "") { inherit installPhase; }
410// optionalAttrs (postInstall != "") { inherit postInstall; }
411// optionalAttrs (preFixup != "") { inherit preFixup; }
412// optionalAttrs (postFixup != "") { inherit postFixup; }
413// optionalAttrs (dontStrip) { inherit dontStrip; }
414// optionalAttrs (hardeningDisable != []) { inherit hardeningDisable; }
415// optionalAttrs (stdenv.isLinux) { LOCALE_ARCHIVE = "${glibcLocales}/lib/locale/locale-archive"; }
416)