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