summary refs log tree commit diff
path: root/pkgs/development
diff options
context:
space:
mode:
authorNicholas Clarke <nick@topos.org.uk>2017-07-28 16:51:34 +0100
committerNicholas Clarke <nick@topos.org.uk>2017-09-04 12:35:57 +0100
commit676362494d54b8ee1c7411a8963aaa72041fa91e (patch)
tree97c545356adc514192e5b4e1c86ebaf5f586dcdf /pkgs/development
parentda3640ec56d2466144844ff14d07c81ebcb15349 (diff)
downloadnixpkgs-676362494d54b8ee1c7411a8963aaa72041fa91e.tar
nixpkgs-676362494d54b8ee1c7411a8963aaa72041fa91e.tar.gz
nixpkgs-676362494d54b8ee1c7411a8963aaa72041fa91e.tar.bz2
nixpkgs-676362494d54b8ee1c7411a8963aaa72041fa91e.tar.lz
nixpkgs-676362494d54b8ee1c7411a8963aaa72041fa91e.tar.xz
nixpkgs-676362494d54b8ee1c7411a8963aaa72041fa91e.tar.zst
nixpkgs-676362494d54b8ee1c7411a8963aaa72041fa91e.zip
Enable multiple outputs for Haskell packages.
Diffstat (limited to 'pkgs/development')
-rw-r--r--pkgs/development/haskell-modules/configuration-common.nix4
-rw-r--r--pkgs/development/haskell-modules/generic-builder.nix116
-rw-r--r--pkgs/development/haskell-modules/lib.nix2
3 files changed, 102 insertions, 20 deletions
diff --git a/pkgs/development/haskell-modules/configuration-common.nix b/pkgs/development/haskell-modules/configuration-common.nix
index cdb15fd9f81..5ab9ed11469 100644
--- a/pkgs/development/haskell-modules/configuration-common.nix
+++ b/pkgs/development/haskell-modules/configuration-common.nix
@@ -899,4 +899,8 @@ self: super: {
     sha256 = "1vss7b99zrhw3r29krl1b60r4qk0m2mpwmrz8q8zdxrh33hb8pd7";
   });
 
+  # Has extra data files which are referred to from the binary output,
+  # creating a store reference cycle. Putting data in separate output
+  # solves the problem.
+  happy = overrideCabal super.happy (drv: { enableSeparateDataOutput = true; });
 }
diff --git a/pkgs/development/haskell-modules/generic-builder.nix b/pkgs/development/haskell-modules/generic-builder.nix
index 60cce56cca0..7beafe4ce1f 100644
--- a/pkgs/development/haskell-modules/generic-builder.nix
+++ b/pkgs/development/haskell-modules/generic-builder.nix
@@ -56,6 +56,10 @@ let isCross = (ghc.cross or null) != null; in
 , 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;
@@ -79,9 +83,6 @@ let
                         then "package-db"
                         else "package-conf";
 
-  # the target dir for haddock documentation
-  docdir = docoutput: docoutput + "/share/doc";
-
   newCabalFileUrl = "http://hackage.haskell.org/package/${pname}-${version}/revision/${revision}.cabal";
   newCabalFile = fetchurl {
     url = newCabalFileUrl;
@@ -95,6 +96,13 @@ let
                    '';
 
   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<n> parallelism for libraries because GHC is far more
   # likely to generate a non-determistic library ID in that case. Further
@@ -113,12 +121,20 @@ let
     stdenv.lib.optionalString isCross (" " + stdenv.lib.concatStringsSep " " crossCabalFlags);
 
   defaultConfigureFlags = [
-    "--verbose" "--prefix=$out" "--libdir=\\$prefix/lib/\\$compiler" "--libsubdir=\\$pkgid"
-    (optionalString enableSeparateDataOutput "--datadir=$data/share/${ghc.name}")
-    (optionalString enableSeparateDocOutput "--docdir=${docdir "$doc"}")
+    "--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=$out/lib/${ghc.name}/${pname}-${version}")
+    (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")
@@ -152,7 +168,8 @@ let
   allPkgconfigDepends = pkgconfigDepends ++ libraryPkgconfigDepends ++ executablePkgconfigDepends ++
                         optionals doCheck testPkgconfigDepends ++ optionals withBenchmarkDepends benchmarkPkgconfigDepends;
 
-  nativeBuildInputs = buildTools ++ libraryToolDepends ++ executableToolDepends ++ [ removeReferencesTo ];
+  nativeBuildInputs = map stdenv.lib.getBin
+    (buildTools ++ libraryToolDepends ++ executableToolDepends ++ [ removeReferencesTo ]);
   propagatedBuildInputs = buildDepends ++ libraryHaskellDepends ++ executableHaskellDepends;
   otherBuildInputs = setupHaskellDepends ++ extraLibraries ++ librarySystemDepends ++ executableSystemDepends ++
                      optionals (allPkgconfigDepends != []) ([pkgconfig] ++ allPkgconfigDepends) ++
@@ -181,7 +198,15 @@ assert allPkgconfigDepends != [] -> pkgconfig != null;
 stdenv.mkDerivation ({
   name = "${pname}-${version}";
 
-  outputs = if (args ? outputs) then args.outputs else ([ "out" ] ++ (optional enableSeparateDataOutput "data") ++ (optional enableSeparateDocOutput "doc"));
+  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;
@@ -205,7 +230,7 @@ stdenv.mkDerivation ({
 
   postPatch = optionalString jailbreak ''
     echo "Run jailbreak-cabal to lift version restrictions on build inputs."
-    ${jailbreak-cabal}/bin/jailbreak-cabal ${pname}.cabal
+    ${stdenv.lib.getBin jailbreak-cabal}/bin/jailbreak-cabal ${pname}.cabal
   '' + postPatch;
 
   setupCompilerEnvironmentPhase = ''
@@ -213,7 +238,7 @@ stdenv.mkDerivation ({
 
     echo "Build with ${ghc}."
     export PATH="${ghc}/bin:$PATH"
-    ${optionalString (hasActiveLibrary && hyperlinkSource) "export PATH=${hscolour}/bin:$PATH"}
+    ${optionalString (hasActiveLibrary && hyperlinkSource) "export PATH=${hscolour.bin}/bin:$PATH"}
 
     packageConfDir="$TMPDIR/package.conf.d"
     mkdir -p $packageConfDir
@@ -240,7 +265,7 @@ stdenv.mkDerivation ({
     #
     # Create a local directory with symlinks of the *.dylib (macOS shared
     # libraries) from all the dependencies.
-    local dynamicLinksDir="$out/lib/links"
+    local dynamicLinksDir="${libDir}/links"
     mkdir -p $dynamicLinksDir
     for d in $(grep dynamic-library-dirs "$packageConfDir/"*|awk '{print $2}'); do
       ln -s "$d/"*.dylib $dynamicLinksDir
@@ -312,7 +337,7 @@ stdenv.mkDerivation ({
 
     ${if !hasActiveLibrary then "${setupCommand} install" else ''
       ${setupCommand} copy
-      local packageConfDir="$out/lib/${ghc.name}/package.conf.d"
+      local packageConfDir="${libDir}/package.conf.d"
       local packageConfFile="$packageConfDir/${pname}-${version}.conf"
       mkdir -p "$packageConfDir"
       ${setupCommand} register --gen-pkg-config=$packageConfFile
@@ -320,7 +345,7 @@ stdenv.mkDerivation ({
       mv $packageConfFile $packageConfDir/$pkgId.conf
     ''}
     ${optionalString isGhcjs ''
-      for exeDir in "$out/bin/"*.jsexe; do
+      for exeDir in "${binDir}/"*.jsexe; do
         exe="''${exeDir%.jsexe}"
         printWords '#!${nodejs}/bin/node' > "$exe"
         cat "$exeDir/all.js" >> "$exe"
@@ -329,18 +354,68 @@ stdenv.mkDerivation ({
     ''}
     ${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 "$out/bin/"* ; do
-        install_name_tool -add_rpath "$out/lib/ghc-${ghc.version}/${pname}-${version}" "$exe"
+      for exe in "${binDir}/"* ; do
+        install_name_tool -add_rpath "${libDir}/${pname}-${version}" "$exe"
       done
     ''}
 
     ${optionalString enableSeparateDocOutput ''
-    for x in ${docdir "$doc"}/html/src/*.html; do
-      remove-references-to -t $out $x
+    # 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
-    mkdir -p $doc
     ''}
-    ${optionalString enableSeparateDataOutput "mkdir -p $data"}
+
+    ${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
   '';
@@ -386,6 +461,7 @@ stdenv.mkDerivation ({
          // optionalAttrs (description != "")  { inherit description; }
          // optionalAttrs (maintainers != [])  { inherit maintainers; }
          // optionalAttrs (hydraPlatforms != platforms) { inherit hydraPlatforms; }
+         // optionalAttrs (outputsToInstall != []) { inherit outputsToInstall; }
          ;
 
 }
diff --git a/pkgs/development/haskell-modules/lib.nix b/pkgs/development/haskell-modules/lib.nix
index 48110cffabf..518c3c82e6c 100644
--- a/pkgs/development/haskell-modules/lib.nix
+++ b/pkgs/development/haskell-modules/lib.nix
@@ -142,4 +142,6 @@ rec {
   overrideSrc = drv: { src, version ? drv.version }:
     overrideCabal drv (_: { inherit src version; editedCabalFile = null; });
 
+  installOutputs = drv: outputs: overrideCabal drv
+    (drv: { outputsToInstall = outputs; });
 }