-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlib.nix
170 lines (153 loc) · 4.93 KB
/
lib.nix
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
with builtins;
rec {
dotCabal = dir: let
base = baseNameOf dir;
in {
"${base}" = dir + "/${base}.cabal";
};
cabalPackages = dir: let
# Here we "parse" a cabal.project file with hacky regexes. It doesn't
# handle globs or URLs, but conceivably this could be added.
files = let
entries = let
path = dir + "/cabal.project";
contents = readFile path;
regex = ".*([\n]|^)packages[ ]*:((([ ]*[^ \n]+)*)(\n[ ]+([^ \n]+[ ]*)*)*).*";
matches = match regex contents;
lines = split "[\n ]+" (elemAt matches 1);
in filter (line: isString line && line != "") lines;
toFile = entry: let
path = dir + "/${entry}";
in if util.isDir path
then path + "/${baseNameOf path}.cabal"
else path;
in map toFile entries;
cabalDotProject = let
toPackage = cabal: {
name = replaceStrings [".cabal"] [""] (baseNameOf cabal);
value = cabal;
};
in listToAttrs (map toPackage files);
packages = if util.findParent "cabal.project" dir == null
then dotCabal dir
else cabalDotProject;
in packages;
options = f: dir: {dev ? null, ghc ? null, hoogle ? null, ...}@args: let
defaults = {
dev = true;
ghc = null;
hoogle = true;
};
settings = if pathExists (dir + "/options.nix")
then import (dir + "/options.nix")
else {};
opts = defaults // settings // args;
in f dir opts;
config = dir: opts: let
preconfig = if pathExists (dir + "/config.nix")
then import (dir + "/config.nix")
else {};
overrides = if pathExists (dir + "/overrides.nix")
then import (dir + "/overrides.nix")
else _: _: _: {};
projects = pkgs: let
gitignoreSrc = pkgs.fetchFromGitHub {
owner = "hercules-ci";
repo = "gitignore";
rev = "7415c4feb127845553943a3856cbc5cb967ee5e0";
sha256 = "1zd1ylgkndbb5szji32ivfhwh04mr1sbgrnvbrqpmfb67g2g3r9i";
};
inherit (import gitignoreSrc { inherit (pkgs) lib; }) gitignoreFilter;
in self: super: let
go = name: package: let
src = dirOf package;
in self.callCabal2nix name (pkgs.lib.cleanSourceWith {
inherit name src;
filter = gitignoreFilter src;
}) {};
in mapAttrs go (cabalPackages dir);
in preconfig // {
packageOverrides = oldpkgs: let
pkgs = (preconfig.packageOverrides or (p: p)) oldpkgs;
projects' = projects pkgs;
compose = old: {
overrides = pkgs.lib.composeExtensions
(pkgs.lib.composeExtensions (old.overrides or (_: _: {})) projects')
(overrides pkgs);
};
in if opts.ghc == null then {
haskellPackages = pkgs.haskellPackages.override compose;
}
else {
haskell = pkgs.haskell // {
packages = pkgs.haskell.packages // {
"${opts.ghc}" = pkgs.haskell.packages."${opts.ghc}".override compose;
};
};
};
};
packages = dir: opts: let
nixpkgs = fromJSON (readFile (dir + "/nixpkgs.json"));
src = fetchTarball {
url = "https://github.com/NixOS/nixpkgs/archive/${nixpkgs.rev}.tar.gz";
inherit (nixpkgs) sha256;
};
in import src {
config = config dir opts;
};
haskellPackages = dir: opts: let
pkgs = packages dir opts;
in if opts.ghc == null
then pkgs.haskellPackages
else pkgs.haskell.packages."${opts.ghc}";
metarelease = cabals: options (dir: opts: let
pkgs = packages dir opts;
hspkgs = haskellPackages dir opts;
go = name: let
package = getAttr name hspkgs;
in [
{
name = name;
value = package;
}
# This would be nice to have but doesn't do anything useful yet AFAICT
#{
# name = "${name}-static";
# value = pkgs.haskell.lib.justStaticExecutables package;
#}
];
in listToAttrs (concatMap go (attrNames cabals)));
release = dir: metarelease (cabalPackages dir) dir;
subrelease = dir: subdir: metarelease (dotCabal subdir) dir;
metashell = cabals: options (dir: opts: let
pkgs = packages dir opts;
hspkgs = haskellPackages dir opts;
project = p: map (n: getAttr n p) (attrNames cabals);
shell = (hspkgs.shellFor {
packages = project;
withHoogle = opts.hoogle;
}).overrideAttrs (old: {
nativeBuildInputs = old.nativeBuildInputs ++ (with hspkgs; [
ghcid cabal-install
]);
});
full = pkgs.mkShell {
buildInputs = [
(hspkgs.ghcWithPackages project)
];
};
in if opts.dev then shell else full);
shell = dir: metashell (cabalPackages dir) dir;
subshell = dir: subdir: metashell (dotCabal subdir) dir;
util = rec {
isDir = path: pathExists path
&& getAttr (baseNameOf path) (readDir (dirOf path)) == "directory";
findParent = file: let
loop = dir: if pathExists (dir + "/${file}")
then dir
else let parent = /. + dirOf dir; in if parent == /.
then null
else loop parent;
in loop;
};
}