mirror of
https://github.com/simplex-chat/haskell.nix.git
synced 2026-06-03 09:17:32 +00:00
159 lines
6.9 KiB
Nix
159 lines
6.9 KiB
Nix
{ lib, stdenv, mkShell, glibcLocales, pkgconfig, ghcForComponent, makeConfigFiles, hsPkgs, hoogleLocal, haskellLib, buildPackages, compiler }:
|
|
|
|
{ # `packages` function selects packages that will be worked on in the shell itself.
|
|
# These packages will not be built by `shellFor`, but their
|
|
# dependencies will be included in the shell's `ghc-pkg list`.
|
|
packages ? ps: builtins.attrValues (haskellLib.selectLocalPackages ps)
|
|
# `components` function selects components that will be worked on in the shell itself.
|
|
# By default `shellFor` will include the dependencies of all of the components
|
|
# in the selected packages. If only as subset of the components will be
|
|
# worked on in the shell then we can pass a different `components` function
|
|
# to select those.
|
|
, components ? ps: lib.concatMap haskellLib.getAllComponents (packages ps)
|
|
# Additional packages to be added unconditionally
|
|
, additional ? _: []
|
|
, withHoogle ? true
|
|
, exactDeps ? false
|
|
, tools ? {}
|
|
, packageSetupDeps ? true
|
|
, enableDWARF ? false
|
|
, ... } @ args:
|
|
|
|
let
|
|
# TODO find out why hoogle index creation can be made to work for cross compilers
|
|
withHoogle' = withHoogle && !haskellLib.isCrossHost;
|
|
|
|
selectedPackages = packages hsPkgs;
|
|
additionalPackages = additional hsPkgs;
|
|
selectedComponents = components hsPkgs;
|
|
|
|
# The configs of all the selected components
|
|
selectedConfigs = map (c: c.config) selectedComponents
|
|
++ lib.optionals packageSetupDeps (map (p: p.setup.config) selectedPackages);
|
|
|
|
name = if lib.length selectedPackages == 1
|
|
then "ghc-shell-for-${(lib.head selectedPackages).identifier.name}"
|
|
else "ghc-shell-for-packages";
|
|
|
|
# Given a `packages = [ a b ]` we construct a shell with the dependencies of `a` and `b`.
|
|
#
|
|
# But if `a` depends on `b`, we don't want to include `b`, because
|
|
# - cabal will end up ignoring that built version;
|
|
# - The user has indicated that's what they're working on, so they probably don't want to have to build
|
|
# it first (and it will change often).
|
|
# Generally we never want to include any of (the components of) the selected packages as dependencies.
|
|
#
|
|
# Furthermore, if `a` depends on `b`, `a` will include the library component of `b` in its `buildInputs`
|
|
# (to make `propagatedBuildInputs` of `pkgconfig-depends` work). So we also need to filter those
|
|
# (the pkgconfig depends of `b` will still be included in the
|
|
# system shell's `buildInputs` via `b`'s own `buildInputs`).
|
|
# We have to do this slightly differently because we will be looking at the actual components rather
|
|
# than the packages.
|
|
|
|
# Given a list of `depends`, removes those which are components of packages which were selected as part of the shell.
|
|
removeSelectedInputs =
|
|
# All the components of the selected packages: we shouldn't add any of these as dependencies
|
|
let selectedPackageComponents = map (x: x.name) selectedComponents;
|
|
in lib.filter (input:
|
|
!(builtins.elem ((haskellLib.dependToLib input).name or null) selectedPackageComponents));
|
|
|
|
# We need to remove any dependencies which are selected packages (see above).
|
|
packageInputs = removeSelectedInputs (lib.concatMap (cfg: cfg.depends) selectedConfigs)
|
|
++ additionalPackages;
|
|
|
|
# Add the system libraries and build tools of the selected haskell packages to the shell.
|
|
# We need to remove any inputs which are selected components (see above).
|
|
# `buildInputs`, `propagatedBuildInputs`, and `executableToolDepends` contain component
|
|
# derivations, not packages, so we use `removeSelectedInputs`).
|
|
systemInputs = removeSelectedInputs (lib.concatMap
|
|
(c: c.buildInputs ++ c.propagatedBuildInputs) selectedComponents);
|
|
nativeBuildInputs = removeSelectedInputs
|
|
(lib.concatMap (c: c.executableToolDepends) selectedComponents);
|
|
|
|
# Set up a "dummy" component to use with ghcForComponent.
|
|
component = {
|
|
depends = packageInputs;
|
|
libs = [];
|
|
frameworks = [];
|
|
doExactConfig = false;
|
|
};
|
|
configFiles = makeConfigFiles {
|
|
fullName = args.name or name;
|
|
identifier.name = name;
|
|
inherit component;
|
|
};
|
|
ghcEnv = ghcForComponent {
|
|
inherit configFiles;
|
|
componentName = name;
|
|
postInstall = lib.optionalString withHoogle' ''
|
|
ln -s ${hoogleIndex}/bin/hoogle $out/bin
|
|
'';
|
|
inherit enableDWARF;
|
|
};
|
|
|
|
hoogleIndex = let
|
|
# Get the doc package for a component, and add attributes that
|
|
# hoogle.nix expects.
|
|
docPackage = p: lib.getOutput "doc" p // {
|
|
pname = p.identifier.name;
|
|
haddockDir = lib.const p.haddockDir;
|
|
};
|
|
in hoogleLocal ({
|
|
packages = map docPackage (haskellLib.flatLibDepends component);
|
|
|
|
# Need to add hoogle to hsPkgs.
|
|
# inherit (hsPkgs) hoogle;
|
|
} // (
|
|
lib.optionalAttrs (args ? tools && args.tools ? hoogle) {
|
|
hoogle = buildPackages.haskell-nix.tool compiler.nix-name "hoogle" args.tools.hoogle;
|
|
}
|
|
));
|
|
|
|
mkDrvArgs = builtins.removeAttrs args ["packages" "components" "additional" "withHoogle" "tools"];
|
|
in
|
|
mkShell (mkDrvArgs // {
|
|
name = mkDrvArgs.name or name;
|
|
|
|
buildInputs = systemInputs
|
|
++ mkDrvArgs.buildInputs or [];
|
|
nativeBuildInputs = [ ghcEnv ]
|
|
++ nativeBuildInputs
|
|
++ mkDrvArgs.nativeBuildInputs or []
|
|
++ lib.attrValues (buildPackages.haskell-nix.tools compiler.nix-name tools)
|
|
# If this shell is a cross compilation shell include
|
|
# wrapper script for running cabal build with appropriate args.
|
|
# Includes `--with-compiler` in case the `cabal.project` file has `with-compiler:` in it.
|
|
++ lib.optional (ghcEnv.targetPrefix != "") (
|
|
buildPackages.writeShellScriptBin "${ghcEnv.targetPrefix}cabal" ''
|
|
exec cabal \
|
|
--with-ghc=${ghcEnv.targetPrefix}ghc \
|
|
--with-compiler=${ghcEnv.targetPrefix}ghc \
|
|
--with-ghc-pkg=${ghcEnv.targetPrefix}ghc-pkg \
|
|
--with-hsc2hs=${ghcEnv.targetPrefix}hsc2hs \
|
|
${lib.optionalString (ghcEnv.targetPrefix == "js-unknown-ghcjs-") ''
|
|
--with-ghcjs=${ghcEnv.targetPrefix}ghc \
|
|
--with-ghcjs-pkg=${ghcEnv.targetPrefix}ghc-pkg \
|
|
--ghcjs \
|
|
''} $(builtin type -P "${ghcEnv.targetPrefix}pkg-config" &> /dev/null && echo "--with-pkg-config=${ghcEnv.targetPrefix}pkg-config") \
|
|
"$@"
|
|
'');
|
|
phases = ["installPhase"];
|
|
installPhase = ''
|
|
echo "${"Shell for " + toString (builtins.map (p : p.identifier.name) selectedPackages)}"
|
|
echo $nativeBuildInputs $buildInputs > $out
|
|
'';
|
|
LANG = "en_US.UTF-8";
|
|
LOCALE_ARCHIVE = lib.optionalString (stdenv.hostPlatform.libc == "glibc") "${glibcLocales}/lib/locale/locale-archive";
|
|
|
|
# This helps tools like `ghcide` (that use the ghc api) to find
|
|
# the correct global package DB.
|
|
NIX_GHC_LIBDIR = ghcEnv + "/" + configFiles.libDir;
|
|
|
|
passthru = (mkDrvArgs.passthru or {}) // {
|
|
ghc = ghcEnv;
|
|
inherit configFiles;
|
|
};
|
|
} // lib.optionalAttrs exactDeps {
|
|
CABAL_CONFIG = "${configFiles}/cabal.config";
|
|
})
|