-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathflake.nix
338 lines (292 loc) · 12.6 KB
/
flake.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
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
{
description = "Nix interop for the Effekt programming language";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
flake-utils.url = "github:numtide/flake-utils";
sbt-derivation = {
url = "github:zaninime/sbt-derivation";
inputs.nixpkgs.follows = "nixpkgs";
inputs.flake-utils.follows = "flake-utils";
};
};
outputs = { self, nixpkgs, flake-utils, sbt-derivation }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = import nixpkgs { inherit system; };
# Load Effekt versions and their corresponding SHA256 hashes from 'releases.json'
# If you want to add a new release version, just add it there.
effektVersions = builtins.fromJSON (builtins.readFile ./releases.json);
# Gets the newest version from 'effektVersions'
latestVersion = builtins.head (builtins.sort (a: b: builtins.compareVersions a b > 0) (builtins.attrNames effektVersions));
# Available backends for Effekt, depending on the current 'system'
effektBackends = {
js = {
name = "js";
buildInputs = [pkgs.nodejs];
};
js-web = {
name = "js-web";
buildInputs = [pkgs.nodejs]; # TODO: For tests, we currently use 'js'
};
llvm = {
name = "llvm";
buildInputs = [pkgs.llvm pkgs.libuv pkgs.clang]; # GCC is also usable here
};
chez-callcc = {
name = "chez-callcc";
buildInputs = [pkgs.chez];
};
chez-monadic = {
name = "chez-monadic";
buildInputs = [pkgs.chez];
};
};
# Meta information about the Effekt programming language
effektMeta = {
mainProgram = "effekt";
description = "A language with lexical effect handlers and lightweight effect polymorphism";
homepage = "https://effekt-lang.org/";
license = pkgs.lib.licenses.mit;
};
# Creates an Effekt derivation from a prebuilt GitHub release
buildEffektRelease = {
version,
sha256,
backends ? [effektBackends.js]
}:
assert backends != []; # Ensure at least one backend is specified
pkgs.stdenv.mkDerivation {
pname = "effekt";
inherit version;
src = pkgs.fetchurl {
url = "https://github.com/effekt-lang/effekt/releases/download/v${version}/effekt.tgz";
inherit sha256;
};
nativeBuildInputs = [pkgs.makeWrapper];
buildInputs = [pkgs.jre] ++ pkgs.lib.concatMap (b: b.buildInputs) backends;
installPhase = ''
mkdir -p $out/bin $out/lib
mv bin/effekt $out/lib/effekt.jar
mv libraries $out/libraries
makeWrapper ${pkgs.jre}/bin/java $out/bin/effekt \
--add-flags "-jar $out/lib/effekt.jar" \
--prefix PATH : ${pkgs.lib.makeBinPath (pkgs.lib.concatMap (b: b.buildInputs) backends)}
'';
meta = effektMeta;
};
# Creates an Effekt derivation by building Effekt from (some) source
buildEffektFromSource = {
src,
version,
depsSha256, # SHA256 of the Scala dependencies
backends ? [effektBackends.js],
}:
assert backends != []; # Ensure at least one backend is specified
sbt-derivation.lib.mkSbtDerivation {
inherit pkgs;
pname = "effekt";
inherit version;
inherit src;
nativeBuildInputs = [pkgs.nodejs pkgs.maven pkgs.makeWrapper pkgs.gnused];
buildInputs = [pkgs.jre] ++ pkgs.lib.concatMap (b: b.buildInputs) backends;
inherit depsSha256;
depsArchivalStrategy = "copy";
depsWarmupCommand = ''
echo "Warming up: getting compiler bridge thingy"
sbt scalaCompilerBridgeBinaryJar
echo "Warming up: updating"
sbt update
echo "Warming up: FINISHED"
'';
# Change the version in build.sbt
prePatch = ''
sed -i 's/lazy val effektVersion = "[^"]*"/lazy val effektVersion = "${version}"/' build.sbt
'';
buildPhase = ''
export MAVEN_OPTS="-Dmaven.repo.local=$out/.m2/repository"
sbt assembleBinary
'';
installPhase = ''
mkdir -p $out/bin $out/lib
mv bin/effekt $out/lib/effekt.jar
mv libraries $out/libraries
makeWrapper ${pkgs.jre}/bin/java $out/bin/effekt \
--add-flags "-jar $out/lib/effekt.jar" \
--prefix PATH : ${pkgs.lib.makeBinPath (pkgs.lib.concatMap (b: b.buildInputs) backends)}
'';
meta = effektMeta;
};
# Builds an Effekt package
buildEffektPackage =
{
pname, # package name
version, # package version
src, # source of the package
main, # (relative) path to the entrypoint
tests ? [], # (relative) paths to the tests
effekt ? null, # the explicit Effekt derivation to use: uses latest release if not set
effektVersion ? latestVersion, # the Effekt version to use
backends ? [effektBackends.js], # Effekt backends to use -- first backend is the "default" one
buildInputs ? [], # other build inputs required for the package
}:
assert backends != []; # Ensure at least one backend is specified
let
defaultBackend = builtins.head backends;
effektBuild = if effekt != null then effekt else buildEffektRelease {
version = effektVersion;
sha256 = effektVersions.${effektVersion};
inherit backends;
};
in
pkgs.stdenv.mkDerivation {
inherit pname version src;
nativeBuildInputs = [effektBuild pkgs.gnused];
buildInputs = buildInputs ++ pkgs.lib.concatMap (b: b.buildInputs) backends;
# TODO: consider removing the 'js-web'-related hacks...
buildPhase = ''
mkdir -p out
${pkgs.lib.concatMapStrings (backend: ''
echo "Building with backend ${backend.name} file ${src}/${main}"
echo "Current directory: $(pwd)"
echo "Contents of current directory:"
ls -R
effekt --build --backend ${backend.name} ${src}/${main}
echo "Contents of out directory:"
ls -R out/
if [ "${backend.name}" = "js-web" ]; then
echo "Moving .js and .html for js-web backend"
mv "out/$(basename ${src}/${main} .effekt).js" out/${pname}.js
mv "out/$(basename ${src}/${main} .effekt).html" out/${pname}.html
sed -i 's/src="main.js"/src="${pname}.js"/' out/${pname}.html
else
mv out/$(basename ${src}/${main} .effekt) out/${pname}-${backend.name}
fi
'') backends}
'';
# NOTE: Should we already do this in 'buildPhase'?
installPhase = ''
mkdir -p $out/bin
cp -r out/* $out/bin/
if [ "${defaultBackend.name}" != "js-web" ]; then
ln -s $out/bin/${pname}-${defaultBackend.name} $out/bin/${pname}
fi
'';
# NOTE: Should this be in 'buildPhase' directly?
fixupPhase = ''
patchShebangs $out/bin
'';
# NOTE: This currently duplicates the building logic somewhat.
checkPhase = pkgs.lib.concatMapStrings (test:
pkgs.lib.concatMapStrings (backend:
let
backendForCheck = if backend == effektBackends.js-web then effektBackends.js else backend;
in ''
mkdir -p $TMPDIR/testout
echo "Building test ${test} with backend ${backendForCheck.name}"
effekt --build --backend ${backendForCheck.name} --out $TMPDIR/testout ${src}/${test}
echo "Patching the shebangs of the test:"
patchShebangs $TMPDIR/testout
echo "Running the test:"
$TMPDIR/testout/$(basename ${test} .effekt)
rm -rf $TMPDIR/testout
''
) backends
) tests;
doCheck = tests != [];
# Entry point is the program called ${pname}
meta.mainProgram = pname;
};
# Creates a dev-shell for an Effekt package / version & backends
mkDevShell = {
effekt ? null,
effektVersion ? latestVersion,
backends ? [effektBackends.js]
}:
let
effektBuild = if effekt != null then effekt else buildEffektRelease {
version = effektVersion;
sha256 = effektVersions.${effektVersion};
inherit backends;
};
in
pkgs.mkShell {
buildInputs = [effektBuild] ++ pkgs.lib.concatMap (b: b.buildInputs) backends;
};
# Development shell for Effekt compiler development
compilerDevShell = pkgs.mkShell {
buildInputs = with pkgs; [
sbt jre maven scala_3
] ++ pkgs.lib.concatMap (b: b.buildInputs) (builtins.attrValues effektBackends);
};
# Automatically generated packages for all 'effektVersions' with all backends
autoPackages = pkgs.lib.mapAttrs' (version: _:
pkgs.lib.nameValuePair "effekt_${builtins.replaceStrings ["."] ["_"] version}" (
buildEffektRelease {
inherit version;
sha256 = effektVersions.${version};
backends = builtins.attrValues effektBackends;
}
)
) effektVersions;
# Automatically generated devshells for all 'effektVersions' with all backends
autoDevShells = pkgs.lib.mapAttrs' (version: _:
pkgs.lib.nameValuePair "effekt_${builtins.replaceStrings ["."] ["_"] version}" (
mkDevShell {
effektVersion = version;
backends = builtins.attrValues effektBackends;
}
)
) effektVersions;
# Quick alias for the latest pre-built Effekt derivation
latestEffekt = autoPackages."effekt_${builtins.replaceStrings ["."] ["_"] latestVersion}";
# Helpful function to get an Effekt package given version and backends
getEffekt =
{
version ? null, # Version as a string (leave null for the latest version)
backends ? [effektBackends.js] # Supported backends
}:
assert backends != []; # Ensure at least one backend is specified
let
selectedVersion = if version == null then latestVersion else version;
sha256 = effektVersions.${selectedVersion} or null;
in
if sha256 == null
then throw "Unsupported Effekt version: ${selectedVersion}"
else buildEffektRelease {
inherit backends;
version = selectedVersion;
inherit sha256;
};
in {
# Helper functions and types for external use
lib = {
inherit buildEffektRelease buildEffektFromSource buildEffektPackage getEffekt mkDevShell effektBackends;
};
# Automatically generated packages + latest version (as default)
packages = autoPackages // {
default = latestEffekt;
};
# Development shells
devShells = autoDevShells // {
default = mkDevShell {
effektVersion = latestVersion;
backends = builtins.attrValues effektBackends;
};
compilerDev = compilerDevShell;
};
# Ready-to-run applications
apps = {
default = flake-utils.lib.mkApp {
drv = latestEffekt;
name = "effekt";
};
} // builtins.mapAttrs (name: pkg:
flake-utils.lib.mkApp {
drv = pkg;
name = "effekt";
}
) autoPackages;
checks = { };
}
);
}