Skip to content

Commit a479589

Browse files
committed
Add loadCabalPlan function
1 parent 1257535 commit a479589

File tree

2 files changed

+170
-158
lines changed

2 files changed

+170
-158
lines changed

lib/load-cabal-plan.nix

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
{haskellLib, pkgs}:
2+
{callProjectResults, selectedCompiler}:
3+
let
4+
# Read the plan.json file `plan-nix` derivation
5+
plan-json = builtins.fromJSON (
6+
builtins.unsafeDiscardStringContext (
7+
builtins.readFile (callProjectResults.projectNix + "/plan.json")));
8+
# All the units in the plan indexed by unit ID.
9+
by-id = pkgs.lib.listToAttrs (map (x: { name = x.id; value = x; }) plan-json.install-plan);
10+
# Find the names of all the pre-existing packages used by a list of dependencies
11+
# (includes transitive dependencies)
12+
lookupPreExisting = depends:
13+
pkgs.lib.concatMap (d: builtins.attrNames pre-existing-depends.${d}) depends;
14+
pre-existing-depends =
15+
pkgs.lib.listToAttrs (map (p: {
16+
name = p.id;
17+
value = pkgs.lib.optionalAttrs (p.type == "pre-existing") { ${p.pkg-name} = null; } //
18+
pkgs.lib.listToAttrs (
19+
map (dname: { name = dname; value = null; }) (lookupPreExisting (p.depends or p.components.lib.depends)));
20+
}) plan-json.install-plan);
21+
# Lookup a dependency in `hsPkgs`
22+
lookupDependency = hsPkgs: d:
23+
pkgs.lib.optional (by-id.${d}.type != "pre-existing") (
24+
if by-id.${d}.component-name or "lib" == "lib"
25+
then hsPkgs.${d} or hsPkgs.${by-id.${d}.pkg-name}
26+
else hsPkgs.${d}.components.sublibs.${pkgs.lib.removePrefix "lib:" by-id.${d}.component-name});
27+
# Lookup an executable dependency in `hsPkgs.pkgsBuildBuild`
28+
lookupExeDependency = hsPkgs: d:
29+
# Try to lookup by ID, but if that fails use the name (currently a different plan is used by pkgsBuildBuild when cross compiling)
30+
(hsPkgs.pkgsBuildBuild.${d} or hsPkgs.pkgsBuildBuild.${by-id.${d}.pkg-name}).components.exes.${pkgs.lib.removePrefix "exe:" by-id.${d}.component-name};
31+
# Populate `depends`, `pre-existing` and `build-tools`
32+
lookupDependencies = hsPkgs: depends: exe-depends: {
33+
depends = pkgs.lib.concatMap (lookupDependency hsPkgs) depends;
34+
pre-existing = lookupPreExisting depends;
35+
build-tools = map (lookupExeDependency hsPkgs) exe-depends;
36+
};
37+
# Calculate the packages for a component
38+
getComponents = cabal2nixComponents: hsPkgs: p:
39+
let
40+
components = p.components or { ${p.component-name or "lib"} = { inherit (p) depends; exe-depends = p.exe-depends or []; }; };
41+
# Other than the `lib` and `setup` components, component names
42+
# have a prefix based on their type.
43+
componentsWithPrefix = collectionName: prefix:
44+
pkgs.lib.listToAttrs (pkgs.lib.concatLists (pkgs.lib.mapAttrsToList (n: c:
45+
pkgs.lib.optional (pkgs.lib.hasPrefix "${prefix}:" n) (
46+
let
47+
name = pkgs.lib.removePrefix "${prefix}:" n;
48+
value = (if cabal2nixComponents == null then {} else cabal2nixComponents.${collectionName}.${name}) // {
49+
buildable = true;
50+
} // lookupDependencies hsPkgs c.depends c.exe-depends;
51+
in { inherit name value; }
52+
)) components));
53+
in
54+
pkgs.lib.mapAttrs componentsWithPrefix haskellLib.componentPrefix
55+
// pkgs.lib.optionalAttrs (components ? lib) {
56+
library = (if cabal2nixComponents == null then {} else cabal2nixComponents.library) // {
57+
buildable = true;
58+
} // lookupDependencies hsPkgs components.lib.depends components.lib.exe-depends;
59+
} // pkgs.lib.optionalAttrs (components ? setup) {
60+
setup = {
61+
buildable = true;
62+
} // lookupDependencies hsPkgs.pkgsBuildBuild (components.setup.depends or []) (components.setup.exe-depends or []);
63+
};
64+
nixFilesDir = callProjectResults.projectNix + callProjectResults.src.origSubDir or "";
65+
in {
66+
# This replaces the `plan-nix/default.nix`
67+
pkgs = (hackage: {
68+
packages = pkgs.lib.listToAttrs (
69+
# Include entries for the `pre-existing` packages, but leave them as `null`
70+
pkgs.lib.concatMap (p:
71+
pkgs.lib.optional (p.type == "pre-existing") {
72+
name = p.id;
73+
value.revision = null;
74+
}) plan-json.install-plan
75+
# The other packages that are not part of the project itself.
76+
++ pkgs.lib.concatMap (p:
77+
pkgs.lib.optional (p.type == "configured" && (p.style == "global" || p.style == "inplace") ) {
78+
name = p.id;
79+
value.revision =
80+
{hsPkgs, ...}@args:
81+
let
82+
# Read the output of `Cabal2Nix.hs`. We need it for information not
83+
# in the `plan.json` file.
84+
cabal2nix = (
85+
if builtins.pathExists (nixFilesDir + "/cabal-files/${p.pkg-name}.nix")
86+
then import (nixFilesDir + "/cabal-files/${p.pkg-name}.nix")
87+
else if builtins.pathExists (nixFilesDir + "/.plan.nix/${p.pkg-name}.nix")
88+
then import (nixFilesDir + "/.plan.nix/${p.pkg-name}.nix")
89+
else (((hackage.${p.pkg-name}).${p.pkg-version}).revisions).default) (args // { hsPkgs = {}; });
90+
in pkgs.lib.optionalAttrs (p ? pkg-src-sha256) {
91+
sha256 = p.pkg-src-sha256;
92+
} // pkgs.lib.optionalAttrs (p.pkg-src.type or "" == "source-repo") {
93+
# Replace the source repository packages with versions created when
94+
# parsing the `cabal.project` file.
95+
src = pkgs.lib.lists.elemAt callProjectResults.sourceRepos (pkgs.lib.strings.toInt p.pkg-src.source-repo.location) + "/${p.pkg-src.source-repo.subdir}";
96+
} // pkgs.lib.optionalAttrs (cabal2nix ? package-description-override && p.pkg-version == cabal2nix.package.identifier.version) {
97+
# Use the `.cabal` file from the `Cabal2Nix` if it for the matching
98+
# version of the package (the one in the plan).
99+
inherit (cabal2nix) package-description-override;
100+
} // {
101+
flags = p.flags; # Use the flags from `plan.json`
102+
components = getComponents cabal2nix.components hsPkgs p;
103+
package = cabal2nix.package // {
104+
identifier = { name = p.pkg-name; version = p.pkg-version; id = p.id; };
105+
isProject = false;
106+
setup-depends = []; # The correct setup depends will be in `components.setup.depends`
107+
};
108+
};
109+
}) plan-json.install-plan);
110+
compiler = {
111+
inherit (selectedCompiler) version;
112+
};
113+
});
114+
# Packages in the project (those that are both configure and local)
115+
extras = (_hackage: {
116+
packages = pkgs.lib.listToAttrs (
117+
pkgs.lib.concatMap (p:
118+
pkgs.lib.optional (p.type == "configured" && p.style == "local") {
119+
name = p.id;
120+
value =
121+
{hsPkgs, ...}@args:
122+
let cabal2nix = import (nixFilesDir + "/.plan.nix/${p.pkg-name}.nix") (args // { hsPkgs = {}; });
123+
in pkgs.lib.optionalAttrs (p ? pkg-src-sha256) {
124+
sha256 = p.pkg-src-sha256;
125+
} // pkgs.lib.optionalAttrs (p.pkg-src.type or "" == "local" && cabal2nix ? cabal-generator) {
126+
inherit (cabal2nix) cabal-generator;
127+
} // pkgs.lib.optionalAttrs (p.pkg-src.type or "" == "local") {
128+
# Find the `src` location based on `p.pkg-src.path`
129+
src = if pkgs.lib.hasPrefix "/" p.pkg-src.path
130+
then p.pkg-src.path # Absolute path
131+
else haskellLib.appendSubDir {
132+
# Relative to the project path
133+
inherit (callProjectResults) src;
134+
subDir = pkgs.lib.removePrefix "./" (pkgs.lib.removePrefix "/" (pkgs.lib.removeSuffix "/." (pkgs.lib.removeSuffix "/." (
135+
if pkgs.lib.hasPrefix ".${callProjectResults.src.origSubDir or ""}/" (p.pkg-src.path + "/")
136+
then pkgs.lib.removePrefix ".${callProjectResults.src.origSubDir or ""}" p.pkg-src.path
137+
else throw "Unexpected path ${p.pkg-src.path} expected it to start with .${callProjectResults.src.origSubDir or ""}"))));
138+
includeSiblings = true; # Filtering sibling dirs of the package dir is done in the
139+
# component builder so that relative paths can be used to
140+
# reference project directories not in the package subDir.
141+
};
142+
} // {
143+
flags = p.flags; # Use the flags from `plan.json`
144+
components = getComponents cabal2nix.components hsPkgs p;
145+
package = cabal2nix.package // {
146+
identifier = { name = p.pkg-name; version = p.pkg-version; id = p.id; };
147+
isProject = true;
148+
setup-depends = []; # The correct setup depends will be in `components.setup.depends`
149+
};
150+
};
151+
}) plan-json.install-plan);
152+
});
153+
modules = [
154+
{ inherit plan-json; }
155+
(import ../modules/install-plan/non-reinstallable.nix)
156+
(import ../modules/install-plan/override-package-by-name.nix)
157+
(import ../modules/install-plan/planned.nix { inherit getComponents; })
158+
(import ../modules/install-plan/redirect.nix)
159+
];
160+
}

0 commit comments

Comments
 (0)