-
Notifications
You must be signed in to change notification settings - Fork 25
/
Copy pathbuild.nix
156 lines (147 loc) · 5 KB
/
build.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
{ runCommand
, lib
, callPackage
, stdenv
, rsync
, symlinkJoin
, writeScript
, bash
, coreutils
}:
with (callPackage ./modules.nix {});
with (callPackage ./lib.nix {});
with (callPackage ./module-spec.nix {});
rec {
# Returns an attribute set where the keys are all the built module names and
# the values are the paths to the object files.
# mainModSpec: a "main" module
buildMain = ghcWith: mainModSpec: mainModName:
buildModulesRec ghcWith
# XXX: the main modules need special handling regarding the object name
{ "${mainModSpec.moduleName}" =
"${buildModule ghcWith mainModSpec}/${mainModName}.o";}
mainModSpec.moduleImports;
# returns a attrset where the keys are the module names and the values are
# the modules' object file path
buildLibrary = ghcWith: modSpecs:
buildModulesRec ghcWith {} modSpecs;
linkMainModule =
{ ghcWith
, moduleSpec # The module to build
, name # The name to give the executable
, mainModName
}:
let
objAttrs = buildMain ghcWith moduleSpec mainModName;
objList = lib.attrsets.mapAttrsToList (x: y: y) objAttrs;
deps = allTransitiveDeps [moduleSpec];
ghc = ghcWith deps;
ghcOptsArgs = lib.strings.escapeShellArgs moduleSpec.moduleGhcOpts;
packageList = map (p: "-package ${p}") deps;
relExePath = "bin/${name}";
drv = runCommand name {}
''
mkdir -p $out/bin
${ghc}/bin/ghc \
${lib.strings.escapeShellArgs packageList} \
${lib.strings.escapeShellArgs objList} \
${ghcOptsArgs} \
-o $out/${relExePath}
'';
in
{
out = drv;
relExePath = relExePath;
};
# Build the given modules (recursively) using the given accumulator to keep
# track of which modules have been built already
# XXX: doesn't work if several modules in the DAG have the same name
buildModulesRec = ghcWith: empty: modSpecs:
foldDAG
{ f = mod:
{ "${mod.moduleName}" =
"${buildModule ghcWith mod}/${moduleToObject mod.moduleName}";
};
elemLabel = mod: mod.moduleName;
elemChildren = mod: mod.moduleImports;
reduce = a: b: a // b;
empty = empty;
}
modSpecs;
buildModule = ghcWith: modSpec:
let
ghc = ghcWith deps;
deps = allTransitiveDeps [modSpec];
exts = modSpec.moduleExtensions;
ghcOpts = modSpec.moduleGhcOpts ++ (map (x: "-X${x}") exts);
ghcOptsArgs = lib.strings.escapeShellArgs ghcOpts;
objectName = modSpec.moduleName;
builtDeps = map (buildModule ghcWith) (allTransitiveImports [modSpec]);
depsDirs = map (x: x + "/") builtDeps;
base = modSpec.moduleBase;
makeSymtree =
if lib.lists.length depsDirs >= 1
# TODO: symlink instead of copy
then "rsync -r ${lib.strings.escapeShellArgs depsDirs} ."
else "";
makeSymModule =
# TODO: symlink instead of copy
"rsync -r ${singleOutModule base modSpec.moduleName}/ .";
pred = file: path: type:
let
topLevel = (builtins.toString base) + "/";
actual = (lib.strings.removePrefix topLevel path);
expected = file;
in
(expected == actual) ||
(type == "directory" && (lib.strings.hasPrefix actual expected));
extraFiles = builtins.filterSource
(p: t:
lib.lists.length
(
let
topLevel = (builtins.toString base) + "/";
actual = lib.strings.removePrefix topLevel p;
in
lib.filter (expected:
(expected == actual) ||
(t == "directory" && (lib.strings.hasPrefix actual expected))
)
modSpec.moduleFiles
) >= 1
) base;
src = symlinkJoin
{ name = "extra-files";
paths = [ extraFiles ] ++ modSpec.moduleDirectories;
};
in builtins.derivation
{ name = objectName;
system = stdenv.system;
imports = map (mmm: mmm.moduleName) modSpec.moduleImports;
PATH = lib.makeBinPath [ coreutils rsync ghc ];
builder = writeScript "${objectName}-builder"
''
#!${bash}/bin/bash
set -e
echo "Building module ${modSpec.moduleName}"
echo "Local imports are:"
for foo in $imports; do
echo " - $foo"
done
cp -r ${src}/. ./
mkdir -p $out
echo "Creating dependencies symtree for module ${modSpec.moduleName}"
${makeSymtree}
echo "Creating module symlink for module ${modSpec.moduleName}"
${makeSymModule}
echo "Compiling module ${modSpec.moduleName}"
# Set a tmpdir we have control over, otherwise GHC fails, not sure why
mkdir -p tmp
ghc -tmpdir tmp/ ${moduleToFile modSpec.moduleName} -c \
-outputdir $out \
${ghcOptsArgs} \
2>&1
echo "Done building module ${modSpec.moduleName}"
'';
};
}