{ stdenv, fetchurl, ghc, pkgconfig, glibcLocales, coreutils, gnugrep, gnused , jailbreak-cabal, hscolour, cpphs, nodejs, lib, removeReferencesTo }: let isCross = (ghc.cross or null) != null; in { pname , dontStrip ? (ghc.isGhcjs or false) , version, revision ? null , sha256 ? null , src ? fetchurl { url = "mirror://hackage/${pname}-${version}.tar.gz"; inherit sha256; } , buildDepends ? [], setupHaskellDepends ? [], libraryHaskellDepends ? [], executableHaskellDepends ? [] , buildTarget ? "" , buildTools ? [], libraryToolDepends ? [], executableToolDepends ? [], testToolDepends ? [], benchmarkToolDepends ? [] , configureFlags ? [] , description ? "" , doCheck ? !isCross && (stdenv.lib.versionOlder "7.4" ghc.version) , withBenchmarkDepends ? false , doHoogle ? true , editedCabalFile ? null , enableLibraryProfiling ? false , enableExecutableProfiling ? false # TODO enable shared libs for cross-compiling , enableSharedExecutables ? !isCross && (((ghc.isGhcjs or false) || stdenv.lib.versionOlder "7.7" ghc.version)) , enableSharedLibraries ? !isCross && (((ghc.isGhcjs or false) || stdenv.lib.versionOlder "7.7" ghc.version)) , enableSplitObjs ? null # OBSOLETE, use enableDeadCodeElimination , enableDeadCodeElimination ? (!stdenv.isDarwin) # TODO: use -dead_strip for darwin , enableStaticLibraries ? true , extraLibraries ? [], librarySystemDepends ? [], executableSystemDepends ? [] , homepage ? "http://hackage.haskell.org/package/${pname}" , platforms ? ghc.meta.platforms , hydraPlatforms ? platforms , hyperlinkSource ? true , isExecutable ? false, isLibrary ? !isExecutable , jailbreak ? false , license , maintainers ? [] , doCoverage ? false , doHaddock ? !(ghc.isHaLVM or false) , passthru ? {} , pkgconfigDepends ? [], libraryPkgconfigDepends ? [], executablePkgconfigDepends ? [], testPkgconfigDepends ? [], benchmarkPkgconfigDepends ? [] , testDepends ? [], testHaskellDepends ? [], testSystemDepends ? [] , benchmarkDepends ? [], benchmarkHaskellDepends ? [], benchmarkSystemDepends ? [] , testTarget ? "" , broken ? false , preCompileBuildDriver ? "", postCompileBuildDriver ? "" , preUnpack ? "", postUnpack ? "" , patches ? [], patchPhase ? "", prePatch ? "", postPatch ? "" , preConfigure ? "", postConfigure ? "" , preBuild ? "", postBuild ? "" , installPhase ? "", preInstall ? "", postInstall ? "" , checkPhase ? "", preCheck ? "", postCheck ? "" , preFixup ? "", postFixup ? "" , shellHook ? "" , coreSetup ? false # Use only core packages to build Setup.hs. , useCpphs ? false , hardeningDisable ? lib.optional (ghc.isHaLVM or false) "all" , enableSeparateDataOutput ? false , enableSeparateDocOutput ? doHaddock , enableSeparateBinOutput ? isExecutable , outputsToInstall ? [] , enableSeparateLibOutput ? true , enableSeparateEtcOutput ? (stdenv.lib.versionOlder "7.7" ghc.version) } @ args: assert editedCabalFile != null -> revision != null; # OBSOLETE, use enableDeadCodeElimination assert enableSplitObjs == null; let inherit (stdenv.lib) optional optionals optionalString versionOlder versionAtLeast concatStringsSep enableFeature optionalAttrs toUpper filter makeLibraryPath; isGhcjs = ghc.isGhcjs or false; isHaLVM = ghc.isHaLVM or false; packageDbFlag = if isGhcjs || isHaLVM || versionOlder "7.6" ghc.version then "package-db" else "package-conf"; nativeGhc = if isCross || isGhcjs then ghc.bootPkgs.ghc else ghc; nativePackageDbFlag = if versionOlder "7.6" nativeGhc.version then "package-db" else "package-conf"; newCabalFileUrl = "http://hackage.haskell.org/package/${pname}-${version}/revision/${revision}.cabal"; newCabalFile = fetchurl { url = newCabalFileUrl; sha256 = editedCabalFile; name = "${pname}-${version}-r${revision}.cabal"; }; defaultSetupHs = builtins.toFile "Setup.hs" '' import Distribution.Simple main = defaultMain ''; hasActiveLibrary = isLibrary && (enableStaticLibraries || enableSharedLibraries || enableLibraryProfiling); hasLibOutput = enableSeparateLibOutput && hasActiveLibrary; libDir = if hasLibOutput then "$lib/lib/${ghc.name}" else "$out/lib/${ghc.name}"; binDir = if enableSeparateBinOutput then "$bin/bin" else "$out/bin"; libexecDir = if enableSeparateBinOutput then "$libexec/bin" else "$out/libexec"; etcDir = if enableSeparateEtcOutput then "$etc/etc" else "$out/etc"; docDir = if enableSeparateDocOutput then "$doc/share/doc" else "$out/share/doc"; dataDir = if enableSeparateDataOutput then "$data/share/${ghc.name}" else "$out/share/${ghc.name}"; # We cannot enable -j parallelism for libraries because GHC is far more # likely to generate a non-determistic library ID in that case. Further # details are at . enableParallelBuilding = (versionOlder "7.8" ghc.version && !hasActiveLibrary) || versionOlder "8.0.1" ghc.version; crossCabalFlags = [ "--with-ghc=${ghc.cross.config}-ghc" "--with-ghc-pkg=${ghc.cross.config}-ghc-pkg" "--with-gcc=${ghc.cc}" "--with-ld=${ghc.ld}" "--with-hsc2hs=${nativeGhc}/bin/hsc2hs" ] ++ (if isHaLVM then [] else ["--hsc2hs-options=--cross-compile"]); crossCabalFlagsString = stdenv.lib.optionalString isCross (" " + stdenv.lib.concatStringsSep " " crossCabalFlags); defaultConfigureFlags = [ "--verbose" "--prefix=$out" # Binary directory has to be $bin/bin instead of just $bin: this # is so that the package is added to the PATH when it's used as a # build input. Sadly mkDerivation won't add inputs that don't have # bin subdirectory. "--bindir=${binDir}" "--libdir=${libDir}" "--libsubdir=\\$pkgid" "--libexecdir=${libexecDir}" (optionalString (enableSeparateEtcOutput) "--sysconfdir=${etcDir}") # Old versions of cabal don't support this flag. "--datadir=${dataDir}" "--docdir=${docDir}" "--with-gcc=$CC" # Clang won't work without that extra information. "--package-db=$packageConfDir" (optionalString (enableSharedExecutables && stdenv.isLinux) "--ghc-option=-optl=-Wl,-rpath=${libDir}/${pname}-${version}") (optionalString (enableSharedExecutables && stdenv.isDarwin) "--ghc-option=-optl=-Wl,-headerpad_max_install_names") (optionalString enableParallelBuilding "--ghc-option=-j$NIX_BUILD_CORES") (optionalString useCpphs "--with-cpphs=${cpphs}/bin/cpphs --ghc-options=-cpp --ghc-options=-pgmP${cpphs}/bin/cpphs --ghc-options=-optP--cpp") (enableFeature (enableDeadCodeElimination && (versionAtLeast "8.0.1" ghc.version)) "split-objs") (enableFeature enableLibraryProfiling "library-profiling") (enableFeature enableExecutableProfiling (if versionOlder ghc.version "8" then "executable-profiling" else "profiling")) (enableFeature enableSharedLibraries "shared") (optionalString (versionAtLeast ghc.version "7.10") (enableFeature doCoverage "coverage")) (optionalString (isGhcjs || versionOlder "7" ghc.version) (enableFeature enableStaticLibraries "library-vanilla")) (optionalString (isGhcjs || versionOlder "7.4" ghc.version) (enableFeature enableSharedExecutables "executable-dynamic")) (optionalString (isGhcjs || versionOlder "7" ghc.version) (enableFeature doCheck "tests")) ] ++ optionals (enableDeadCodeElimination && (stdenv.lib.versionOlder "8.0.1" ghc.version)) [ "--ghc-option=-split-sections" ] ++ optionals isGhcjs [ "--ghcjs" ] ++ optionals isCross ([ "--configure-option=--host=${ghc.cross.config}" ] ++ crossCabalFlags); setupCompileFlags = [ (optionalString (!coreSetup) "-${packageDbFlag}=$packageConfDir") (optionalString isGhcjs "-build-runner") (optionalString (isGhcjs || isHaLVM || versionOlder "7.8" ghc.version) "-j$NIX_BUILD_CORES") # https://github.com/haskell/cabal/issues/2398 (optionalString (versionOlder "7.10" ghc.version && !isHaLVM) "-threaded") ]; isHaskellPkg = x: (x ? pname) && (x ? version) && (x ? env); isSystemPkg = x: !isHaskellPkg x; allPkgconfigDepends = pkgconfigDepends ++ libraryPkgconfigDepends ++ executablePkgconfigDepends ++ optionals doCheck testPkgconfigDepends ++ optionals withBenchmarkDepends benchmarkPkgconfigDepends; nativeBuildInputs = map stdenv.lib.getBin (buildTools ++ libraryToolDepends ++ executableToolDepends ++ [ removeReferencesTo ]); propagatedBuildInputs = buildDepends ++ libraryHaskellDepends ++ executableHaskellDepends; otherBuildInputs = setupHaskellDepends ++ extraLibraries ++ librarySystemDepends ++ executableSystemDepends ++ optionals (allPkgconfigDepends != []) ([pkgconfig] ++ allPkgconfigDepends) ++ optionals doCheck (testDepends ++ testHaskellDepends ++ testSystemDepends ++ testToolDepends) ++ # ghcjs's hsc2hs calls out to the native hsc2hs optional isGhcjs nativeGhc ++ optionals withBenchmarkDepends (benchmarkDepends ++ benchmarkHaskellDepends ++ benchmarkSystemDepends ++ benchmarkToolDepends); allBuildInputs = propagatedBuildInputs ++ otherBuildInputs; haskellBuildInputs = stdenv.lib.filter isHaskellPkg allBuildInputs; systemBuildInputs = stdenv.lib.filter isSystemPkg allBuildInputs; ghcEnv = ghc.withPackages (p: haskellBuildInputs); setupBuilder = if isCross then "${nativeGhc}/bin/ghc" else ghcCommand; setupCommand = "./Setup"; ghcCommand' = if isGhcjs then "ghcjs" else "ghc"; crossPrefix = if (ghc.cross or null) != null then "${ghc.cross.config}-" else ""; ghcCommand = "${crossPrefix}${ghcCommand'}"; ghcCommandCaps= toUpper ghcCommand'; in assert allPkgconfigDepends != [] -> pkgconfig != null; stdenv.mkDerivation ({ name = "${pname}-${version}"; outputs = if (args ? outputs) then args.outputs else ( (optional enableSeparateBinOutput "bin") ++ (optional enableSeparateBinOutput "libexec") ++ [ "out" ] ++ (optional enableSeparateDataOutput "data") ++ (optional enableSeparateDocOutput "doc") ++ (optional enableSeparateEtcOutput "etc") ++ (optional hasLibOutput "lib") ); setOutputFlags = false; pos = builtins.unsafeGetAttrPos "pname" args; prePhases = ["setupCompilerEnvironmentPhase"]; preConfigurePhases = ["compileBuildDriverPhase"]; preInstallPhases = ["haddockPhase"]; inherit src; inherit nativeBuildInputs; buildInputs = otherBuildInputs ++ optionals (!hasActiveLibrary) propagatedBuildInputs; propagatedBuildInputs = optionals hasActiveLibrary propagatedBuildInputs; LANG = "en_US.UTF-8"; # GHC needs the locale configured during the Haddock phase. prePatch = optionalString (editedCabalFile != null) '' echo "Replace Cabal file with edited version from ${newCabalFileUrl}." cp ${newCabalFile} ${pname}.cabal '' + prePatch; postPatch = optionalString jailbreak '' echo "Run jailbreak-cabal to lift version restrictions on build inputs." ${stdenv.lib.getBin jailbreak-cabal}/bin/jailbreak-cabal ${pname}.cabal '' + postPatch; setupCompilerEnvironmentPhase = '' runHook preSetupCompilerEnvironment echo "Build with ${ghc}." export PATH="${ghc}/bin:$PATH" ${optionalString (hasActiveLibrary && hyperlinkSource) "export PATH=${stdenv.lib.getBin hscolour}/bin:$PATH"} packageConfDir="$TMPDIR/package.conf.d" mkdir -p $packageConfDir setupCompileFlags="${concatStringsSep " " setupCompileFlags}" configureFlags="${concatStringsSep " " defaultConfigureFlags} $configureFlags" # nativePkgs defined in stdenv/setup.hs for p in "''${nativePkgs[@]}"; do if [ -d "$p/lib/${ghc.name}/package.conf.d" ]; then cp -f "$p/lib/${ghc.name}/package.conf.d/"*.conf $packageConfDir/ continue fi if [ -d "$p/include" ]; then configureFlags+=" --extra-include-dirs=$p/include" fi if [ -d "$p/lib" ]; then configureFlags+=" --extra-lib-dirs=$p/lib" fi done '' + (optionalString stdenv.isDarwin '' # Work around a limit in the macOS Sierra linker on the number of paths # referenced by any one dynamic library: # # Create a local directory with symlinks of the *.dylib (macOS shared # libraries) from all the dependencies. local dynamicLinksDir="${libDir}/links" mkdir -p $dynamicLinksDir for d in $(grep dynamic-library-dirs "$packageConfDir/"*|awk '{print $2}'); do ln -s "$d/"*.dylib $dynamicLinksDir done # Edit the local package DB to reference the links directory. for f in "$packageConfDir/"*.conf; do sed -i "s,dynamic-library-dirs: .*,dynamic-library-dirs: $dynamicLinksDir," $f done '') + '' ${ghcCommand}-pkg --${packageDbFlag}="$packageConfDir" recache runHook postSetupCompilerEnvironment ''; compileBuildDriverPhase = '' runHook preCompileBuildDriver for i in Setup.hs Setup.lhs ${defaultSetupHs}; do test -f $i && break done echo setupCompileFlags: $setupCompileFlags ${setupBuilder} $setupCompileFlags --make -o Setup -odir $TMPDIR -hidir $TMPDIR $i runHook postCompileBuildDriver ''; configurePhase = '' runHook preConfigure unset GHC_PACKAGE_PATH # Cabal complains if this variable is set during configure. echo configureFlags: $configureFlags ${setupCommand} configure $configureFlags 2>&1 | ${coreutils}/bin/tee "$NIX_BUILD_TOP/cabal-configure.log" if ${gnugrep}/bin/egrep -q '^Warning:.*depends on multiple versions' "$NIX_BUILD_TOP/cabal-configure.log"; then echo >&2 "*** abort because of serious configure-time warning from Cabal" exit 1 fi export GHC_PACKAGE_PATH="$packageConfDir:" runHook postConfigure ''; buildPhase = '' runHook preBuild ${setupCommand} build ${buildTarget}${crossCabalFlagsString} runHook postBuild ''; checkPhase = '' runHook preCheck ${setupCommand} test ${testTarget} runHook postCheck ''; haddockPhase = '' runHook preHaddock ${optionalString (doHaddock && hasActiveLibrary) '' ${setupCommand} haddock --html \ ${optionalString doHoogle "--hoogle"} \ ${optionalString (hasActiveLibrary && hyperlinkSource) "--hyperlink-source"} ''} runHook postHaddock ''; installPhase = '' runHook preInstall ${if !hasActiveLibrary then "${setupCommand} install" else '' ${setupCommand} copy local packageConfDir="${libDir}/package.conf.d" local packageConfFile="$packageConfDir/${pname}-${version}.conf" mkdir -p "$packageConfDir" ${setupCommand} register --gen-pkg-config=$packageConfFile local pkgId=$( ${gnused}/bin/sed -n -e 's|^id: ||p' $packageConfFile ) mv $packageConfFile $packageConfDir/$pkgId.conf ''} ${optionalString isGhcjs '' for exeDir in "${binDir}/"*.jsexe; do exe="''${exeDir%.jsexe}" printWords '#!${nodejs}/bin/node' > "$exe" cat "$exeDir/all.js" >> "$exe" chmod +x "$exe" done ''} ${optionalString doCoverage "mkdir -p $out/share && cp -r dist/hpc $out/share"} ${optionalString (enableSharedExecutables && isExecutable && !isGhcjs && stdenv.isDarwin && stdenv.lib.versionOlder ghc.version "7.10") '' for exe in "${binDir}/"* ; do install_name_tool -add_rpath "${libDir}/${pname}-${version}" "$exe" done ''} ${optionalString enableSeparateDocOutput '' # Remove references back to $out but also back to $lib if we have # docs. $lib is needed as it stores path to haddock interfaces in the # conf file which creates a cycle if docs refer back to library # path. mkdir -p ${docDir} for x in ${docDir}/html/src/*.html; do remove-references-to -t $out -t ${libDir} -t ${binDir} ${optionalString enableSeparateDataOutput "-t $data"} $x done ''} ${optionalString hasLibOutput '' # Even if we don't have binary output for the package, things like # Paths files will embed paths to bin/libexec directories in themselves # which results in .lib <-> $out cyclic store reference. We # therefore patch out the paths from separate library if we don't have # separate bin output too. # # If we _do_ have separate bin and lib outputs, we may still be in # trouble in case of shared executables: executable contains path to # .lib, .lib contains path (through Paths) to .bin and we have a # cycle. # # Lastly we have to deal with references from .lib back into # $out/share if we're not splitting out data directory. # # It may happen that we have hasLibOutput set but the library # directory was not created: this happens in the case that library # section is not exposing any modules. See "fail" package for an # example where no modules are exposed for GHC >= 8.0. if [ -d ${libDir} ]; then find ${libDir} -type f -exec \ remove-references-to -t ${binDir} -t ${libexecDir} "{}" \; fi ''} ${optionalString (hasLibOutput && ! enableSeparateDocOutput) '' # If we don't have separate docs, we have to patch out the ref to # docs in package conf. This will likely break Haddock # cross-package links but is necessary to break store cycle… find ${libDir}/ -type f -name '*.conf' -exec \ remove-references-to -t ${docDir} "{}" \; ''} ${optionalString (hasLibOutput && ! enableSeparateDataOutput) '' # Just like for doc output path in $out potentially landing in # *.conf, we have to also remove the data directory so that it # doesn't appear under data-dir field creating a cycle. find ${libDir}/ -type f -exec echo Removing ${dataDir} refs from "{}" \; find ${libDir}/ -type f -exec \ remove-references-to -t ${dataDir} "{}" \; ''} ${optionalString enableSeparateDataOutput "mkdir -p ${dataDir}"} ${optionalString enableSeparateBinOutput "mkdir -p ${binDir} ${libexecDir}"} ${optionalString enableSeparateEtcOutput "mkdir -p ${etcDir}"} runHook postInstall ''; passthru = passthru // { inherit pname version; isHaskellLibrary = hasActiveLibrary; # TODO: ask why the split outputs are configurable at all? # TODO: include tests for split if possible # Given the haskell package, returns # the directory containing the haddock documentation. # `null' if no haddock documentation was built. # TODO: fetch the self from the fixpoint instead haddockDir = self: if doHaddock then "${docDir}/html" else null; env = stdenv.mkDerivation { name = "interactive-${pname}-${version}-environment"; nativeBuildInputs = [ ghcEnv systemBuildInputs ] ++ optional isGhcjs ghc."socket.io"; # for ghcjsi LANG = "en_US.UTF-8"; LOCALE_ARCHIVE = optionalString stdenv.isLinux "${glibcLocales}/lib/locale/locale-archive"; shellHook = '' export NIX_${ghcCommandCaps}="${ghcEnv}/bin/${ghcCommand}" export NIX_${ghcCommandCaps}PKG="${ghcEnv}/bin/${ghcCommand}-pkg" # TODO: is this still valid? export NIX_${ghcCommandCaps}_DOCDIR="${ghcEnv}/share/doc/ghc/html" export LD_LIBRARY_PATH="''${LD_LIBRARY_PATH:+''${LD_LIBRARY_PATH}:}${ makeLibraryPath (filter (x: !isNull x) systemBuildInputs) }" ${if isHaLVM then ''export NIX_${ghcCommandCaps}_LIBDIR="${ghcEnv}/lib/HaLVM-${ghc.version}"'' else ''export NIX_${ghcCommandCaps}_LIBDIR="${ghcEnv}/lib/${ghcCommand}-${ghc.version}"''} ${shellHook} ''; }; }; meta = { inherit homepage license platforms; } // optionalAttrs broken { inherit broken; } // optionalAttrs (description != "") { inherit description; } // optionalAttrs (maintainers != []) { inherit maintainers; } // optionalAttrs (hydraPlatforms != platforms) { inherit hydraPlatforms; } // optionalAttrs (outputsToInstall != []) { inherit outputsToInstall; } ; } // optionalAttrs (preCompileBuildDriver != "") { inherit preCompileBuildDriver; } // optionalAttrs (postCompileBuildDriver != "") { inherit postCompileBuildDriver; } // optionalAttrs (preUnpack != "") { inherit preUnpack; } // optionalAttrs (postUnpack != "") { inherit postUnpack; } // optionalAttrs (configureFlags != []) { inherit configureFlags; } // optionalAttrs (patches != []) { inherit patches; } // optionalAttrs (patchPhase != "") { inherit patchPhase; } // optionalAttrs (preConfigure != "") { inherit preConfigure; } // optionalAttrs (postConfigure != "") { inherit postConfigure; } // optionalAttrs (preBuild != "") { inherit preBuild; } // optionalAttrs (postBuild != "") { inherit postBuild; } // optionalAttrs (doCheck) { inherit doCheck; } // optionalAttrs (withBenchmarkDepends) { inherit withBenchmarkDepends; } // optionalAttrs (checkPhase != "") { inherit checkPhase; } // optionalAttrs (preCheck != "") { inherit preCheck; } // optionalAttrs (postCheck != "") { inherit postCheck; } // optionalAttrs (preInstall != "") { inherit preInstall; } // optionalAttrs (installPhase != "") { inherit installPhase; } // optionalAttrs (postInstall != "") { inherit postInstall; } // optionalAttrs (preFixup != "") { inherit preFixup; } // optionalAttrs (postFixup != "") { inherit postFixup; } // optionalAttrs (dontStrip) { inherit dontStrip; } // optionalAttrs (hardeningDisable != []) { inherit hardeningDisable; } // optionalAttrs (stdenv.isLinux) { LOCALE_ARCHIVE = "${glibcLocales}/lib/locale/locale-archive"; } )