653 lines
26 KiB
Nix
653 lines
26 KiB
Nix
|
# This expression takes a file like `hackage-packages.nix` and constructs
|
||
|
# a full package set out of that.
|
||
|
|
||
|
{ # package-set used for build tools (all of nixpkgs)
|
||
|
buildPackages
|
||
|
|
||
|
, # A haskell package set for Setup.hs, compiler plugins, and similar
|
||
|
# build-time uses.
|
||
|
buildHaskellPackages
|
||
|
|
||
|
, # package-set used for non-haskell dependencies (all of nixpkgs)
|
||
|
pkgs
|
||
|
|
||
|
, # stdenv provides our build and host platforms
|
||
|
stdenv
|
||
|
|
||
|
, # this module provides the list of known licenses and maintainers
|
||
|
lib
|
||
|
|
||
|
# needed for overrideCabal & packageSourceOverrides
|
||
|
, haskellLib
|
||
|
|
||
|
, # hashes for downloading Hackage packages
|
||
|
# This is either a directory or a .tar.gz containing the cabal files and
|
||
|
# hashes of Hackage as exemplified by this repository:
|
||
|
# https://github.com/commercialhaskell/all-cabal-hashes/tree/hackage
|
||
|
all-cabal-hashes
|
||
|
|
||
|
, # compiler to use
|
||
|
ghc
|
||
|
|
||
|
, # A function that takes `{ pkgs, lib, callPackage }` as the first arg and
|
||
|
# `self` as second, and returns a set of haskell packages
|
||
|
package-set
|
||
|
|
||
|
, # The final, fully overridden package set usable with the nixpkgs fixpoint
|
||
|
# overriding functionality
|
||
|
extensible-self
|
||
|
}:
|
||
|
|
||
|
# return value: a function from self to the package set
|
||
|
self:
|
||
|
|
||
|
let
|
||
|
inherit (stdenv) buildPlatform hostPlatform;
|
||
|
|
||
|
inherit (lib) fix' extends makeOverridable;
|
||
|
inherit (haskellLib) overrideCabal;
|
||
|
|
||
|
mkDerivationImpl = pkgs.callPackage ./generic-builder.nix {
|
||
|
inherit stdenv;
|
||
|
nodejs = buildPackages.nodejs-slim;
|
||
|
inherit (self) buildHaskellPackages ghc ghcWithHoogle ghcWithPackages;
|
||
|
inherit (self.buildHaskellPackages) jailbreak-cabal;
|
||
|
hscolour = overrideCabal (drv: {
|
||
|
isLibrary = false;
|
||
|
doHaddock = false;
|
||
|
hyperlinkSource = false; # Avoid depending on hscolour for this build.
|
||
|
postFixup = "rm -rf $out/lib $out/share $out/nix-support";
|
||
|
}) self.buildHaskellPackages.hscolour;
|
||
|
cpphs = overrideCabal (drv: {
|
||
|
isLibrary = false;
|
||
|
postFixup = "rm -rf $out/lib $out/share $out/nix-support";
|
||
|
}) (self.cpphs.overrideScope (self: super: {
|
||
|
mkDerivation = drv: super.mkDerivation (drv // {
|
||
|
enableSharedExecutables = false;
|
||
|
enableSharedLibraries = false;
|
||
|
doHaddock = false;
|
||
|
useCpphs = false;
|
||
|
});
|
||
|
}));
|
||
|
};
|
||
|
|
||
|
mkDerivation = makeOverridable mkDerivationImpl;
|
||
|
|
||
|
# manualArgs are the arguments that were explicitly passed to `callPackage`, like:
|
||
|
#
|
||
|
# callPackage foo { bar = null; };
|
||
|
#
|
||
|
# here `bar` is a manual argument.
|
||
|
callPackageWithScope = scope: fn: manualArgs:
|
||
|
let
|
||
|
# this code is copied from callPackage in lib/customisation.nix
|
||
|
#
|
||
|
# we cannot use `callPackage` here because we want to call `makeOverridable`
|
||
|
# on `drvScope` (we cannot add `overrideScope` after calling `callPackage` because then it is
|
||
|
# lost on `.override`) but determine the auto-args based on `drv` (the problem here
|
||
|
# is that nix has no way to "passthrough" args while preserving the reflection
|
||
|
# info that callPackage uses to determine the arguments).
|
||
|
drv = if lib.isFunction fn then fn else import fn;
|
||
|
auto = builtins.intersectAttrs (lib.functionArgs drv) scope;
|
||
|
|
||
|
# Converts a returned function to a functor attribute set if necessary
|
||
|
ensureAttrs = v: if builtins.isFunction v then { __functor = _: v; } else v;
|
||
|
|
||
|
# this wraps the `drv` function to add `scope` and `overrideScope` to the result.
|
||
|
drvScope = allArgs: ensureAttrs (drv allArgs) // {
|
||
|
inherit scope;
|
||
|
overrideScope = f:
|
||
|
let newScope = mkScope (fix' (extends f scope.__unfix__));
|
||
|
# note that we have to be careful here: `allArgs` includes the auto-arguments that
|
||
|
# weren't manually specified. If we would just pass `allArgs` to the recursive call here,
|
||
|
# then we wouldn't look up any packages in the scope in the next interation, because it
|
||
|
# appears as if all arguments were already manually passed, so the scope change would do
|
||
|
# nothing.
|
||
|
in callPackageWithScope newScope drv manualArgs;
|
||
|
};
|
||
|
in lib.makeOverridable drvScope (auto // manualArgs);
|
||
|
|
||
|
mkScope = scope: let
|
||
|
ps = pkgs.__splicedPackages;
|
||
|
scopeSpliced = pkgs.splicePackages {
|
||
|
pkgsBuildBuild = scope.buildHaskellPackages.buildHaskellPackages;
|
||
|
pkgsBuildHost = scope.buildHaskellPackages;
|
||
|
pkgsBuildTarget = {};
|
||
|
pkgsHostHost = {};
|
||
|
pkgsHostTarget = scope;
|
||
|
pkgsTargetTarget = {};
|
||
|
} // {
|
||
|
# Don't splice these
|
||
|
inherit (scope) ghc buildHaskellPackages;
|
||
|
};
|
||
|
in ps // ps.xorg // ps.gnome2 // { inherit stdenv; } // scopeSpliced;
|
||
|
defaultScope = mkScope self;
|
||
|
callPackage = drv: args: callPackageWithScope defaultScope drv args;
|
||
|
|
||
|
# Use cabal2nix to create a default.nix for the package sources found at 'src'.
|
||
|
haskellSrc2nix = { name, src, sha256 ? null, extraCabal2nixOptions ? "" }:
|
||
|
let
|
||
|
sha256Arg = if sha256 == null then "--sha256=" else ''--sha256="${sha256}"'';
|
||
|
in buildPackages.runCommand "cabal2nix-${name}" {
|
||
|
nativeBuildInputs = [ buildPackages.cabal2nix-unwrapped ];
|
||
|
preferLocalBuild = true;
|
||
|
allowSubstitutes = false;
|
||
|
LANG = "en_US.UTF-8";
|
||
|
LOCALE_ARCHIVE = pkgs.lib.optionalString (buildPlatform.libc == "glibc") "${buildPackages.glibcLocales}/lib/locale/locale-archive";
|
||
|
} ''
|
||
|
export HOME="$TMP"
|
||
|
mkdir -p "$out"
|
||
|
cabal2nix --compiler=${self.ghc.haskellCompilerName} --system=${hostPlatform.config} ${sha256Arg} "${src}" ${extraCabal2nixOptions} > "$out/default.nix"
|
||
|
'';
|
||
|
|
||
|
# Given a package name and version, e.g. name = "async", version = "2.2.4",
|
||
|
# gives its cabal file and hashes (JSON file) as discovered from the
|
||
|
# all-cabal-hashes value. If that's a directory, it will copy the relevant
|
||
|
# files to $out; if it's a tarball, it will extract and move them to $out.
|
||
|
all-cabal-hashes-component = name: version: buildPackages.runCommand "all-cabal-hashes-component-${name}-${version}" {} ''
|
||
|
mkdir -p $out
|
||
|
if [ -d ${all-cabal-hashes} ]
|
||
|
then
|
||
|
cp ${all-cabal-hashes}/${name}/${version}/${name}.json $out
|
||
|
cp ${all-cabal-hashes}/${name}/${version}/${name}.cabal $out
|
||
|
else
|
||
|
tar --wildcards -xzvf ${all-cabal-hashes} \*/${name}/${version}/${name}.{json,cabal}
|
||
|
mv */${name}/${version}/${name}.{json,cabal} $out
|
||
|
fi
|
||
|
'';
|
||
|
|
||
|
hackage2nix = name: version: let component = all-cabal-hashes-component name version; in self.haskellSrc2nix {
|
||
|
name = "${name}-${version}";
|
||
|
sha256 = ''$(sed -e 's/.*"SHA256":"//' -e 's/".*$//' "${component}/${name}.json")'';
|
||
|
src = "${component}/${name}.cabal";
|
||
|
};
|
||
|
|
||
|
# Adds a nix file derived from cabal2nix in the passthru of the derivation it
|
||
|
# produces. This is useful to debug callHackage / callCabal2nix by looking at
|
||
|
# the content of the nix file pointed by `cabal2nixDeriver`.
|
||
|
# However, it does not keep a reference to that file, which may be garbage
|
||
|
# collected, which may be an annoyance.
|
||
|
callPackageKeepDeriver = src: args:
|
||
|
overrideCabal (orig: {
|
||
|
passthru = orig.passthru or {} // {
|
||
|
# When using callCabal2nix or callHackage, it is often useful
|
||
|
# to debug a failure by inspecting the Nix expression
|
||
|
# generated by cabal2nix. This can be accessed via this
|
||
|
# cabal2nixDeriver field.
|
||
|
cabal2nixDeriver = src;
|
||
|
};
|
||
|
}) (self.callPackage src args);
|
||
|
|
||
|
in package-set { inherit pkgs lib callPackage; } self // {
|
||
|
|
||
|
inherit mkDerivation callPackage haskellSrc2nix hackage2nix buildHaskellPackages;
|
||
|
|
||
|
inherit (haskellLib) packageSourceOverrides;
|
||
|
|
||
|
# callHackage :: Text -> Text -> AttrSet -> HaskellPackage
|
||
|
#
|
||
|
# e.g., while overriding a package set:
|
||
|
# '... foo = self.callHackage "foo" "1.5.3" {}; ...'
|
||
|
callHackage = name: version: callPackageKeepDeriver (self.hackage2nix name version);
|
||
|
|
||
|
# callHackageDirect
|
||
|
# :: { pkg :: Text, ver :: Text, sha256 :: Text }
|
||
|
# -> AttrSet
|
||
|
# -> HaskellPackage
|
||
|
#
|
||
|
# This function does not depend on all-cabal-hashes and therefore will work
|
||
|
# for any version that has been released on hackage as opposed to only
|
||
|
# versions released before whatever version of all-cabal-hashes you happen
|
||
|
# to be currently using.
|
||
|
callHackageDirect = {pkg, ver, sha256, rev ? { revision = null; sha256 = null; }}: args:
|
||
|
let pkgver = "${pkg}-${ver}";
|
||
|
firstRevision = self.callCabal2nix pkg (pkgs.fetchzip {
|
||
|
url = "mirror://hackage/${pkgver}/${pkgver}.tar.gz";
|
||
|
inherit sha256;
|
||
|
}) args;
|
||
|
in overrideCabal (orig: {
|
||
|
revision = rev.revision;
|
||
|
editedCabalFile = rev.sha256;
|
||
|
}) firstRevision;
|
||
|
|
||
|
# Creates a Haskell package from a source package by calling cabal2nix on the source.
|
||
|
callCabal2nixWithOptions = name: src: opts: args:
|
||
|
let
|
||
|
extraCabal2nixOptions = if builtins.isString opts
|
||
|
then opts
|
||
|
else opts.extraCabal2nixOptions or "";
|
||
|
srcModifier = opts.srcModifier or null;
|
||
|
defaultFilter = path: type:
|
||
|
pkgs.lib.hasSuffix ".cabal" path ||
|
||
|
baseNameOf path == "package.yaml";
|
||
|
expr = self.haskellSrc2nix {
|
||
|
inherit name extraCabal2nixOptions;
|
||
|
src = if srcModifier != null
|
||
|
then srcModifier src
|
||
|
else if pkgs.lib.canCleanSource src
|
||
|
then pkgs.lib.cleanSourceWith { inherit src; filter = defaultFilter; }
|
||
|
else src;
|
||
|
};
|
||
|
in overrideCabal (orig: {
|
||
|
inherit src;
|
||
|
}) (callPackageKeepDeriver expr args);
|
||
|
|
||
|
callCabal2nix = name: src: args: self.callCabal2nixWithOptions name src "" args;
|
||
|
|
||
|
# : { root : Path
|
||
|
# , name : Defaulted String
|
||
|
# , source-overrides : Defaulted (Either Path VersionNumber)
|
||
|
# , overrides : Defaulted (HaskellPackageOverrideSet)
|
||
|
# , modifier : Defaulted
|
||
|
# , returnShellEnv : Defaulted
|
||
|
# , withHoogle : Defaulted
|
||
|
# , cabal2nixOptions : Defaulted
|
||
|
# } -> NixShellAwareDerivation
|
||
|
#
|
||
|
# Given a path to a haskell package directory, an optional package name
|
||
|
# which defaults to the base name of the path, an optional set of source
|
||
|
# overrides as appropriate for the 'packageSourceOverrides' function, an
|
||
|
# optional set of arbitrary overrides, and an optional haskell package
|
||
|
# modifier, return a derivation appropriate for nix-build or nix-shell to
|
||
|
# build that package.
|
||
|
#
|
||
|
# If 'returnShellEnv' is true this returns a derivation which will give you
|
||
|
# an environment suitable for developing the listed packages with an
|
||
|
# incremental tool like cabal-install.
|
||
|
#
|
||
|
# If 'withHoogle' is true (the default if a shell environment is requested)
|
||
|
# then 'ghcWithHoogle' is used to generate the derivation (instead of
|
||
|
# 'ghcWithPackages'), see the documentation there for more information.
|
||
|
#
|
||
|
# 'cabal2nixOptions' can contain extra command line arguments to pass to
|
||
|
# 'cabal2nix' when generating the package derivation, for example setting
|
||
|
# a cabal flag with '--flag=myflag'.
|
||
|
developPackage =
|
||
|
{ root
|
||
|
, name ? lib.optionalString (builtins.typeOf root == "path") (builtins.baseNameOf root)
|
||
|
, source-overrides ? {}
|
||
|
, overrides ? self: super: {}
|
||
|
, modifier ? drv: drv
|
||
|
, returnShellEnv ? pkgs.lib.inNixShell
|
||
|
, withHoogle ? returnShellEnv
|
||
|
, cabal2nixOptions ? "" }:
|
||
|
let drv =
|
||
|
(extensible-self.extend
|
||
|
(pkgs.lib.composeExtensions
|
||
|
(self.packageSourceOverrides source-overrides)
|
||
|
overrides))
|
||
|
.callCabal2nixWithOptions name root cabal2nixOptions {};
|
||
|
in if returnShellEnv
|
||
|
then (modifier drv).envFunc {inherit withHoogle;}
|
||
|
else modifier drv;
|
||
|
|
||
|
# This can be used to easily create a derivation containing GHC and the specified set of Haskell packages.
|
||
|
#
|
||
|
# Example:
|
||
|
# $ nix-shell -p 'haskellPackages.ghcWithPackages (hpkgs: [ hpkgs.mtl hpkgs.lens ])'
|
||
|
# $ ghci # in the nix-shell
|
||
|
# Prelude > import Control.Lens
|
||
|
#
|
||
|
# GHC is setup with a package database with all the specified Haskell packages.
|
||
|
#
|
||
|
# ghcWithPackages :: (HaskellPkgSet -> [ HaskellPkg ]) -> Derivation
|
||
|
ghcWithPackages = buildHaskellPackages.callPackage ./with-packages-wrapper.nix {
|
||
|
haskellPackages = self;
|
||
|
inherit (self) hoogleWithPackages;
|
||
|
};
|
||
|
|
||
|
|
||
|
# Put 'hoogle' into the derivation's PATH with a database containing all
|
||
|
# the package's dependencies; run 'hoogle server --local' in a shell to
|
||
|
# host a search engine for the dependencies.
|
||
|
#
|
||
|
# Example usage:
|
||
|
# $ nix-shell -p 'haskellPackages.hoogleWithPackages (p: [ p.mtl p.lens ])'
|
||
|
# [nix-shell] $ hoogle server
|
||
|
#
|
||
|
# hoogleWithPackages :: (HaskellPkgSet -> [ HaskellPkg ]) -> Derivation
|
||
|
#
|
||
|
# To reload the Hoogle server automatically on .cabal file changes try
|
||
|
# this:
|
||
|
# echo *.cabal | entr -r -- nix-shell --run 'hoogle server --local'
|
||
|
hoogleWithPackages = self.callPackage ./hoogle.nix {
|
||
|
haskellPackages = self;
|
||
|
};
|
||
|
hoogleLocal =
|
||
|
{ packages ? [] }:
|
||
|
lib.warn "hoogleLocal is deprecated, use hoogleWithPackages instead" (
|
||
|
self.hoogleWithPackages (_: packages)
|
||
|
);
|
||
|
# This is like a combination of ghcWithPackages and hoogleWithPackages.
|
||
|
# It provides a derivation containing both GHC and Hoogle with an index of
|
||
|
# the given Haskell package database.
|
||
|
#
|
||
|
# Example:
|
||
|
# $ nix-shell -p 'haskellPackages.ghcWithHoogle (hpkgs: [ hpkgs.conduit hpkgs.lens ])'
|
||
|
#
|
||
|
# ghcWithHoogle :: (HaskellPkgSet -> [ HaskellPkg ]) -> Derivation
|
||
|
ghcWithHoogle = self.ghcWithPackages.override {
|
||
|
withHoogle = true;
|
||
|
};
|
||
|
|
||
|
# Returns a derivation whose environment contains a GHC with only
|
||
|
# the dependencies of packages listed in `packages`, not the
|
||
|
# packages themselves. Using nix-shell on this derivation will
|
||
|
# give you an environment suitable for developing the listed
|
||
|
# packages with an incremental tool like cabal-install.
|
||
|
#
|
||
|
# In addition to the "packages" arg and "withHoogle" arg, anything that
|
||
|
# can be passed into stdenv.mkDerivation can be included in the input attrset
|
||
|
#
|
||
|
# # default.nix
|
||
|
# with import <nixpkgs> {};
|
||
|
# haskellPackages.extend (haskell.lib.compose.packageSourceOverrides {
|
||
|
# frontend = ./frontend;
|
||
|
# backend = ./backend;
|
||
|
# common = ./common;
|
||
|
# })
|
||
|
#
|
||
|
# # shell.nix
|
||
|
# let pkgs = import <nixpkgs> {} in
|
||
|
# (import ./.).shellFor {
|
||
|
# packages = p: [p.frontend p.backend p.common];
|
||
|
# withHoogle = true;
|
||
|
# buildInputs = [ pkgs.python pkgs.cabal-install ];
|
||
|
# }
|
||
|
#
|
||
|
# -- cabal.project
|
||
|
# packages:
|
||
|
# frontend/
|
||
|
# backend/
|
||
|
# common/
|
||
|
#
|
||
|
# bash$ nix-shell --run "cabal new-build all"
|
||
|
# bash$ nix-shell --run "python"
|
||
|
shellFor =
|
||
|
{ # Packages to create this development shell for. These are usually
|
||
|
# your local packages.
|
||
|
packages
|
||
|
, # Whether or not to generate a Hoogle database for all the
|
||
|
# dependencies.
|
||
|
withHoogle ? false
|
||
|
, # Whether or not to include benchmark dependencies of your local
|
||
|
# packages. You should set this to true if you have benchmarks defined
|
||
|
# in your local packages that you want to be able to run with cabal benchmark
|
||
|
doBenchmark ? false
|
||
|
# An optional function that can modify the generic builder arguments
|
||
|
# for the fake package that shellFor uses to construct its environment.
|
||
|
#
|
||
|
# Example:
|
||
|
# let
|
||
|
# # elided...
|
||
|
# haskellPkgs = pkgs.haskell.packages.ghc884.override (hpArgs: {
|
||
|
# overrides = pkgs.lib.composeExtensions (hpArgs.overrides or (_: _: { })) (
|
||
|
# _hfinal: hprev: {
|
||
|
# mkDerivation = args: hprev.mkDerivation ({
|
||
|
# doCheck = false;
|
||
|
# doBenchmark = false;
|
||
|
# doHoogle = true;
|
||
|
# doHaddock = true;
|
||
|
# enableLibraryProfiling = false;
|
||
|
# enableExecutableProfiling = false;
|
||
|
# } // args);
|
||
|
# }
|
||
|
# );
|
||
|
# });
|
||
|
# in
|
||
|
# haskellPkgs.shellFor {
|
||
|
# packages = p: [ p.foo ];
|
||
|
# genericBuilderArgsModifier = args: args // { doCheck = true; doBenchmark = true };
|
||
|
# }
|
||
|
#
|
||
|
# This will disable tests and benchmarks for everything in "haskellPkgs"
|
||
|
# (which will invalidate the binary cache), and then re-enable them
|
||
|
# for the "shellFor" environment (ensuring that any test/benchmark
|
||
|
# dependencies for "foo" will be available within the nix-shell).
|
||
|
, genericBuilderArgsModifier ? (args: args)
|
||
|
|
||
|
# Extra dependencies, in the form of cabal2nix build attributes.
|
||
|
#
|
||
|
# An example use case is when you have Haskell scripts that use
|
||
|
# libraries that don't occur in your packages' dependencies.
|
||
|
#
|
||
|
# Example:
|
||
|
#
|
||
|
# extraDependencies = p: {
|
||
|
# libraryHaskellDepends = [ p.releaser ];
|
||
|
# };
|
||
|
, extraDependencies ? p: {}
|
||
|
, ...
|
||
|
} @ args:
|
||
|
let
|
||
|
# A list of the packages we want to build a development shell for.
|
||
|
# This is a list of Haskell package derivations.
|
||
|
selected = packages self;
|
||
|
|
||
|
# This is a list of attribute sets, where each attribute set
|
||
|
# corresponds to the build inputs of one of the packages input to shellFor.
|
||
|
#
|
||
|
# Each attribute has keys like buildDepends, executableHaskellDepends,
|
||
|
# testPkgconfigDepends, etc. The values for the keys of the attribute
|
||
|
# set are lists of dependencies.
|
||
|
#
|
||
|
# Example:
|
||
|
# cabalDepsForSelected
|
||
|
# => [
|
||
|
# # This may be the attribute set corresponding to the `backend`
|
||
|
# # package in the example above.
|
||
|
# { buildDepends = [ gcc ... ];
|
||
|
# libraryHaskellDepends = [ lens conduit ... ];
|
||
|
# ...
|
||
|
# }
|
||
|
# # This may be the attribute set corresponding to the `common`
|
||
|
# # package in the example above.
|
||
|
# { testHaskellDepends = [ tasty hspec ... ];
|
||
|
# libraryHaskellDepends = [ lens aeson ];
|
||
|
# benchmarkHaskellDepends = [ criterion ... ];
|
||
|
# ...
|
||
|
# }
|
||
|
# ...
|
||
|
# ]
|
||
|
cabalDepsForSelected = map (p: p.getCabalDeps) selected;
|
||
|
|
||
|
# A predicate that takes a derivation as input, and tests whether it is
|
||
|
# the same as any of the `selected` packages.
|
||
|
#
|
||
|
# Returns true if the input derivation is not in the list of `selected`
|
||
|
# packages.
|
||
|
#
|
||
|
# isNotSelected :: Derivation -> Bool
|
||
|
#
|
||
|
# Example:
|
||
|
#
|
||
|
# isNotSelected common [ frontend backend common ]
|
||
|
# => false
|
||
|
#
|
||
|
# isNotSelected lens [ frontend backend common ]
|
||
|
# => true
|
||
|
isNotSelected = input: pkgs.lib.all (p: input.outPath or null != p.outPath) selected;
|
||
|
|
||
|
# A function that takes a list of list of derivations, filters out all
|
||
|
# the `selected` packages from each list, and concats the results.
|
||
|
#
|
||
|
# zipperCombinedPkgs :: [[Derivation]] -> [Derivation]
|
||
|
#
|
||
|
# Example:
|
||
|
# zipperCombinedPkgs [ [ lens conduit ] [ aeson frontend ] ]
|
||
|
# => [ lens conduit aeson ]
|
||
|
#
|
||
|
# Note: The reason this isn't just the function `pkgs.lib.concat` is
|
||
|
# that we need to be careful to remove dependencies that are in the
|
||
|
# `selected` packages.
|
||
|
#
|
||
|
# For instance, in the above example, if `common` is a dependency of
|
||
|
# `backend`, then zipperCombinedPkgs needs to be careful to filter out
|
||
|
# `common`, because cabal will end up ignoring that built version,
|
||
|
# assuming new-style commands.
|
||
|
zipperCombinedPkgs = vals:
|
||
|
pkgs.lib.concatMap
|
||
|
(drvList: pkgs.lib.filter isNotSelected drvList)
|
||
|
vals;
|
||
|
|
||
|
# Zip `cabalDepsForSelected` into a single attribute list, combining
|
||
|
# the derivations in all the individual attributes.
|
||
|
#
|
||
|
# Example:
|
||
|
# packageInputs
|
||
|
# => # Assuming the value of cabalDepsForSelected is the same as
|
||
|
# # the example in cabalDepsForSelected:
|
||
|
# { buildDepends = [ gcc ... ];
|
||
|
# libraryHaskellDepends = [ lens conduit aeson ... ];
|
||
|
# testHaskellDepends = [ tasty hspec ... ];
|
||
|
# benchmarkHaskellDepends = [ criterion ... ];
|
||
|
# ...
|
||
|
# }
|
||
|
#
|
||
|
# See the Note in `zipperCombinedPkgs` for what gets filtered out from
|
||
|
# each of these dependency lists.
|
||
|
packageInputs =
|
||
|
pkgs.lib.zipAttrsWith (_name: zipperCombinedPkgs) (cabalDepsForSelected ++ [ (extraDependencies self) ]);
|
||
|
|
||
|
# A attribute set to pass to `haskellPackages.mkDerivation`.
|
||
|
#
|
||
|
# The important thing to note here is that all the fields from
|
||
|
# packageInputs are set correctly.
|
||
|
genericBuilderArgs = {
|
||
|
pname =
|
||
|
if pkgs.lib.length selected == 1
|
||
|
then (pkgs.lib.head selected).name
|
||
|
else "packages";
|
||
|
version = "0";
|
||
|
license = null;
|
||
|
}
|
||
|
// packageInputs
|
||
|
// pkgs.lib.optionalAttrs doBenchmark {
|
||
|
# `doBenchmark` needs to explicitly be set here because haskellPackages.mkDerivation defaults it to `false`. If the user wants benchmark dependencies included in their development shell, it has to be explicitly enabled here.
|
||
|
doBenchmark = true;
|
||
|
};
|
||
|
|
||
|
# This is a pseudo Haskell package derivation that contains all the
|
||
|
# dependencies for the packages in `selected`.
|
||
|
#
|
||
|
# This is a derivation created with `haskellPackages.mkDerivation`.
|
||
|
#
|
||
|
# pkgWithCombinedDeps :: HaskellDerivation
|
||
|
pkgWithCombinedDeps = self.mkDerivation (genericBuilderArgsModifier genericBuilderArgs);
|
||
|
|
||
|
# The derivation returned from `envFunc` for `pkgWithCombinedDeps`.
|
||
|
#
|
||
|
# This is a derivation that can be run with `nix-shell`. It provides a
|
||
|
# GHC with a package database with all the dependencies of our
|
||
|
# `selected` packages.
|
||
|
#
|
||
|
# This is a derivation created with `stdenv.mkDerivation` (not
|
||
|
# `haskellPackages.mkDerivation`).
|
||
|
#
|
||
|
# pkgWithCombinedDepsDevDrv :: Derivation
|
||
|
pkgWithCombinedDepsDevDrv = pkgWithCombinedDeps.envFunc { inherit withHoogle; };
|
||
|
|
||
|
mkDerivationArgs = builtins.removeAttrs args [ "genericBuilderArgsModifier" "packages" "withHoogle" "doBenchmark" "extraDependencies" ];
|
||
|
|
||
|
in pkgWithCombinedDepsDevDrv.overrideAttrs (old: mkDerivationArgs // {
|
||
|
nativeBuildInputs = old.nativeBuildInputs ++ mkDerivationArgs.nativeBuildInputs or [];
|
||
|
buildInputs = old.buildInputs ++ mkDerivationArgs.buildInputs or [];
|
||
|
});
|
||
|
|
||
|
ghc = ghc // {
|
||
|
withPackages = self.ghcWithPackages;
|
||
|
withHoogle = self.ghcWithHoogle;
|
||
|
};
|
||
|
|
||
|
/*
|
||
|
Run `cabal sdist` on a source.
|
||
|
|
||
|
Unlike `haskell.lib.sdistTarball`, this does not require any dependencies
|
||
|
to be present, as it uses `cabal-install` instead of building `Setup.hs`.
|
||
|
This makes `cabalSdist` faster than `sdistTarball`.
|
||
|
*/
|
||
|
cabalSdist = {
|
||
|
src,
|
||
|
name ? if src?name then "${src.name}-sdist.tar.gz" else "source.tar.gz"
|
||
|
}:
|
||
|
pkgs.runCommandLocal name
|
||
|
{
|
||
|
inherit src;
|
||
|
nativeBuildInputs = [
|
||
|
buildHaskellPackages.cabal-install
|
||
|
|
||
|
# TODO after https://github.com/haskell/cabal/issues/8352
|
||
|
# remove ghc
|
||
|
self.ghc
|
||
|
];
|
||
|
dontUnpack = false;
|
||
|
} ''
|
||
|
unpackPhase
|
||
|
cd "''${sourceRoot:-.}"
|
||
|
patchPhase
|
||
|
mkdir out
|
||
|
HOME=$PWD cabal sdist --output-directory out
|
||
|
mv out/*.tar.gz $out
|
||
|
'';
|
||
|
|
||
|
/*
|
||
|
Like `haskell.lib.buildFromSdist`, but using `cabal sdist` instead of
|
||
|
building `./Setup`.
|
||
|
|
||
|
Unlike `haskell.lib.buildFromSdist`, this does not require any dependencies
|
||
|
to be present. This makes `buildFromCabalSdist` faster than `haskell.lib.buildFromSdist`.
|
||
|
*/
|
||
|
buildFromCabalSdist = pkg:
|
||
|
haskellLib.overrideSrc
|
||
|
{
|
||
|
src = self.cabalSdist { inherit (pkg) src; };
|
||
|
version = pkg.version;
|
||
|
}
|
||
|
pkg;
|
||
|
|
||
|
/*
|
||
|
Modify a Haskell package to add shell completion scripts for the
|
||
|
given executables produced by it. These completion scripts will be
|
||
|
picked up automatically if the resulting derivation is installed,
|
||
|
e.g. by `nix-env -i`.
|
||
|
|
||
|
This depends on the `--*-completion` flag `optparse-applicative` provides
|
||
|
automatically. Since we need to invoke installed executables, completions
|
||
|
are not generated if we are cross-compiling.
|
||
|
|
||
|
commands: names of the executables built by the derivation
|
||
|
pkg: Haskell package that builds the executables
|
||
|
|
||
|
Example:
|
||
|
generateOptparseApplicativeCompletions [ "exec1" "exec2" ] pkg
|
||
|
|
||
|
Type: [str] -> drv -> drv
|
||
|
*/
|
||
|
generateOptparseApplicativeCompletions =
|
||
|
self.callPackage (
|
||
|
{ stdenv }:
|
||
|
|
||
|
commands:
|
||
|
pkg:
|
||
|
|
||
|
if stdenv.buildPlatform.canExecute stdenv.hostPlatform
|
||
|
then lib.foldr haskellLib.__generateOptparseApplicativeCompletion pkg commands
|
||
|
else pkg
|
||
|
) { };
|
||
|
|
||
|
/*
|
||
|
Modify given Haskell package to force GHC to employ the LLVM
|
||
|
codegen backend when compiling. Useful when working around bugs
|
||
|
in a native codegen backend GHC defaults to.
|
||
|
|
||
|
Example:
|
||
|
forceLlvmCodegenBackend tls
|
||
|
|
||
|
Type: drv -> drv
|
||
|
*/
|
||
|
forceLlvmCodegenBackend = overrideCabal (drv: {
|
||
|
configureFlags = drv.configureFlags or [ ] ++ [ "--ghc-option=-fllvm" ];
|
||
|
buildTools = drv.buildTools or [ ] ++ [ self.llvmPackages.llvm ];
|
||
|
});
|
||
|
}
|