@@ -1,138 +1,88 @@
1−{ pkg-def
1+let f = { hackage, pkgs, pkg-def, pkg-def-overlays ? [], modules ? [] }: let
2−, pkgs ? import <nixpkgs> {}
2+ buildModules = f { inherit hackage pkg-def pkg-def-overlays modules; pkgs = pkgs.buildPackages; };
3−, hackage ? import <hackage>
3+in pkgs.lib.evalModules {
4−, haskell ? import <haskell> }:
4+ modules = modules ++ [
6+ # Provide all modules with haskellLib, pkgs, and pkgconfPkgs arguments
8+ # this is *not* the hasekllLib from nixpkgs; it is rather our own
9+ # library from haskell.nix
10+ haskellLib = let hl = import ./lib { inherit lib; haskellLib = hl; }; in hl;
6−{ extraDeps ? hsPkgs: {} }:
12+ # The package descriptions depend on pkgs, which are used to resolve system package dependencies
7−let
13+ # as well as pkgconfPkgs, which are used to resolve pkgconfig name to nixpkgs names. We simply
14+ # augment the existing pkgs set with the specific mappings:
15+ pkgs = pkgs // (import ./lib/system-nixpkgs-map.nix pkgs);
16+ pkgconfPkgs = pkgs // (import ./lib/pkgconf-nixpkgs-map.nix pkgs);
9− # packages that we must never try to reinstall.
18+ inherit buildModules;
10− nonReinstallablePkgs = [ "rts" "ghc" "ghc-prim" "integer-gmp" "integer-simple" "base"
19+ };
11− "array" "deepseq" "pretty" "ghc-boot-th" "template-haskell"];
13− hackagePkgs = with pkgs.lib;
21+ # Set the hackage DB for modules/hackage.nix
14− let shippedPkgs = filterAttrs (n: _: builtins.elem n nonReinstallablePkgs)
22+ hackage.db = hackage;
15− (mapAttrs (name: version: { ${version} = { revisions = { default = null; }; }; })
16− (pkg-def {}).compiler.packages);
17− in recursiveUpdate hackage shippedPkgs;
19− # We may depend on packages shipped with ghc, or need to rebuild them.
24+ # Set the plan for modules/plan.nix
20− ghcPackages = pkgs.lib.mapAttrs (name: version: hackagePkgs.${name}.${version}
25+ plan.pkg-def = hackage: with builtins;
21− ) (pkg-def {}).compiler.packages;
26+ # The desugar reason.
28+ # it is quite combersome to write
29+ # (hackage: { packages.x.revision = hackage...;
30+ # packages.y.revision = import ./foo.nix; })
31+ # where we'd rather write:
32+ # (hackage: { x = hackage...; })
35+ # As such the desugarer desugars this short hand syntax.
36+ let desugar = overlay:
38+ isPath = x: builtins.typeOf x == "path";
42+ # { package = { ... }; }
43+ inject-packages = o: if o ? "packages" then o else { packages = o; };
48+ inject-revision = pkg: if pkg ? "revision" then pkg else { revision = pkg; };
50+ # x.revision = ./some/path;
52+ # x.revision = import ./some/path;
53+ expand-paths = pkg: if !(isPath pkg.revision) then pkg else { revision = import pkg.revision; };
54+ # apply injection and expansion to the "packages" in overlay.
55+ in lib.mapAttrs (k: v: if k != "packages"
57+ else lib.mapAttrs (_: pkg: (expand-paths (inject-revision pkg))) v)
58+ (inject-packages overlay);
59+ # fold any potential `pkg-def-overlays`
60+ # onto the `pkg-def`.
62+ # This means you can have a base definition (e.g. stackage)
63+ # and augment it with custom packages to your liking.
64+ in foldl' lib.recursiveUpdate
66+ (map (p: desugar (if builtins.isFunction p then p hackage else p)) pkg-def-overlays)
23− # Thus the final package set in our augmented (extrDeps) lts set is the following:
69+ })
24− ltsPkgs = ghcPackages
25− // (pkg-def hackagePkgs).packages
26− // extraDeps hackagePkgs;
28− driver = haskell.compat.driver;
71+ # Supplies metadata
29− host-map = haskell.compat.host-map;
72+ ./modules/cabal.nix
31− # compiler this lts set is built against.
74+ # Converts config.packages into config.hsPkgs
32− compiler = pkgs.haskell.packages.${(pkg-def {}).compiler.nix-name};
75+ # Replace this with compat-driver.nix to use nixpkgs haskell build infra
76+ ./modules/component-driver.nix
34− # This is a tiny bit better than doJailbreak.
78+ # Converts config.hackage.db to config.hackage.configs
35− #
79+ ./modules/hackage.nix
36− # We essentially *know* the dependencies, and with the
37− # full cabal file representation, we also know all the
38− # flags. As such we can sidestep the solver.
41− # - no need for doJailbreak
42− # - no need for jailbreak-cabal to be built with
43− # Cabal2 if the cabal file requires it.
44− # - no reliance on --allow-newer, which only made
45− # a very short lived appearance in Cabal.
46− # (Cabal-2.0.0.2 -- Cabal-2.2.0.0)
49− # - automatic flag resolution won't happen and will
50− # have to be hard coded.
52− # Ideally we'd just inspect the haskell*Depends fields
53− # we feed the builder. However because we null out the
54− # lirbaries ghc ships (e.g. base, ghc, ...) this would
55− # result in an incomplete --dependency=<name>=<name>-<version>
56− # set and not lead to the desired outcome.
58− # If we could still have base, etc. not nulled, but
59− # produce some virtual derivation, that might allow us
60− # to just use the haskell*Depends fields to extract the
61− # name and version for each dependency.
63− # Ref: https://github.com/haskell/cabal/issues/3163#issuecomment-185833150
65− # ghc-pkg should be ${ghcCommand}-pkg; and --package-db
66− # should better be --${packageDbFlag}; but we don't have
67− # those variables in scope.
68− doExactConfig = pkgs: pkg: let targetPrefix = with pkgs.stdenv; lib.optionalString
69− (hostPlatform != buildPlatform)
70− "${hostPlatform.config}-";
72− in pkgs.haskell.lib.overrideCabal pkg (drv: {
81+ # Converts config.hackage.configs and pkg-def to config.packages
73− # TODO: need to run `ghc-pkg field <pkg> id` over all `--dependency`
82+ ./modules/plan.nix
74− # values. Should we encode the `id` in the nix-pkg as well?
75− preConfigure = (drv.preConfigure or "") + ''
76− configureFlags+=" --exact-configuration"
77− globalPackages=$(${targetPrefix}ghc-pkg list --global --simple-output)
78− localPackages=$(${targetPrefix}ghc-pkg --package-db="$packageConfDir" list --simple-output)
79− for pkg in $globalPackages; do
81− if [ "$pkgName" != "rts" ]; then
82− if [[ " ${pkgs.lib.concatStringsSep " " nonReinstallablePkgs} " =~ " $pkgName " ]]; then
83− configureFlags+=" --dependency="''${pkg%-*}=$pkg
87− for pkg in $localPackages; do
88− configureFlags+=" --dependency="''${pkg%-*}=$pkg
91− #echo ''${configureFlags}
92− configureFlags=$(for flag in ''${configureFlags};do case "X''${flag}" in
94− pkgId=$(${targetPrefix}ghc-pkg --package-db="$packageConfDir" field ''${flag##*=} id || ${targetPrefix}ghc-pkg --global field ''${flag##*=} id)
95− echo ''${flag%=*}=$(echo $pkgId | awk -F' ' '{ print $2 }')
100− #echo ''${configureFlags}
105− toGenericPackage = stackPkgs: args: name: path:
84+ # Configuration that applies to all plans
106− let path' = pkgs.lib.traceVal path.revision.outPath # either a fixed revision from, say plan.nix
85+ ./modules/configuration-nix.nix
107−# or path.revisions.default.outPath # a package which doesn't have a revision specified; assume we want the default.
86+ ];
108− or path.revision
87+};
109− or path; # or a direct path
88+in f
111− if path' == null then null else
113− cabalexpr = import path';
114− pkgs = pkgs // { haskellPackages = stackPkgs; };
116− inherit (host-map pkgs.stdenv) os arch;
117− version = compiler.ghc.version; };
118− # Use `callPackage` from the `compiler` here, to get the
120− in compiler.callPackage expr args;
122−in let stackPackages = pkgs: self:
123− (let p = (pkgs.lib.mapAttrs (toGenericPackage self {}) ltsPkgs);
124− # for all packages do the `exactConfig` logic. That is, we
125− # *know* that that our package-db contains only a single valid
126− # set of proper packages. So we can sidestep cabals solver.
127− in (pkgs.lib.mapAttrs (_: v: if v == null
129− else doExactConfig pkgs v) p)
130− // (with pkgs.haskell.lib;
133− buildPackages = pkgs.buildPackages.haskellPackages; }));
134− in compiler.override {
135− initialPackages = { pkgs, stdenv, callPackage }: self: (stackPackages pkgs self);
136− configurationCommon = { ... }: self: super: {};
137− compilerConfig = self: super: {};