1# https://nim-lang.github.io/Nim/packaging.html
2# https://nim-lang.org/docs/nimc.html
3
4{ lib, callPackage, buildPackages, stdenv, fetchurl, fetchgit
5, makeWrapper, openssl, pcre, readline, boehmgc, sqlite, Security
6, nim-unwrapped-2, nim-unwrapped-1, nim }:
7
8let
9 parseCpu = platform:
10 with platform;
11 # Derive a Nim CPU identifier
12 if isAarch32 then
13 "arm"
14 else if isAarch64 then
15 "arm64"
16 else if isAlpha then
17 "alpha"
18 else if isAvr then
19 "avr"
20 else if isMips && is32bit then
21 "mips"
22 else if isMips && is64bit then
23 "mips64"
24 else if isMsp430 then
25 "msp430"
26 else if isPower && is32bit then
27 "powerpc"
28 else if isPower && is64bit then
29 "powerpc64"
30 else if isRiscV && is64bit then
31 "riscv64"
32 else if isSparc then
33 "sparc"
34 else if isx86_32 then
35 "i386"
36 else if isx86_64 then
37 "amd64"
38 else
39 abort "no Nim CPU support known for ${config}";
40
41 parseOs = platform:
42 with platform;
43 # Derive a Nim OS identifier
44 if isAndroid then
45 "Android"
46 else if isDarwin then
47 "MacOSX"
48 else if isFreeBSD then
49 "FreeBSD"
50 else if isGenode then
51 "Genode"
52 else if isLinux then
53 "Linux"
54 else if isNetBSD then
55 "NetBSD"
56 else if isNone then
57 "Standalone"
58 else if isOpenBSD then
59 "OpenBSD"
60 else if isWindows then
61 "Windows"
62 else if isiOS then
63 "iOS"
64 else
65 abort "no Nim OS support known for ${config}";
66
67 parsePlatform = p: {
68 cpu = parseCpu p;
69 os = parseOs p;
70 };
71
72 nimHost = parsePlatform stdenv.hostPlatform;
73 nimTarget = parsePlatform stdenv.targetPlatform;
74
75in {
76
77 nim-unwrapped-2 = stdenv.mkDerivation (finalAttrs: {
78 pname = "nim-unwrapped";
79 version = "2.0.8";
80 strictDeps = true;
81
82 src = fetchurl {
83 url = "https://nim-lang.org/download/nim-${finalAttrs.version}.tar.xz";
84 hash = "sha256-VwLahEcA0xKdtzFwtcYGrb37h+grgWwNkRB+ogpl3xY=";
85 };
86
87 buildInputs = [ boehmgc openssl pcre readline sqlite ]
88 ++ lib.optional stdenv.isDarwin Security;
89
90 patches = [
91 ./NIM_CONFIG_DIR.patch
92 # Override compiler configuration via an environmental variable
93
94 ./nixbuild.patch
95 # Load libraries at runtime by absolute path
96
97 ./extra-mangling.patch
98 # Mangle store paths of modules to prevent runtime dependence.
99
100 ./openssl.patch
101 # dlopen is widely used by Python, Ruby, Perl, ... what you're really telling me here is that your OS is fundamentally broken. That might be news for you, but it isn't for me.
102 ];
103
104 configurePhase = let
105 bootstrapCompiler = stdenv.mkDerivation {
106 pname = "nim-bootstrap";
107 inherit (finalAttrs) version src preBuild;
108 enableParallelBuilding = true;
109 installPhase = ''
110 runHook preInstall
111 install -Dt $out/bin bin/nim
112 runHook postInstall
113 '';
114 };
115 in ''
116 runHook preConfigure
117 cp ${bootstrapCompiler}/bin/nim bin/
118 echo 'define:nixbuild' >> config/nim.cfg
119 runHook postConfigure
120 '';
121
122 kochArgs = [
123 "--cpu:${nimHost.cpu}"
124 "--os:${nimHost.os}"
125 "-d:release"
126 "-d:useGnuReadline"
127 ] ++ lib.optional (stdenv.isDarwin || stdenv.isLinux) "-d:nativeStacktrace";
128
129 preBuild = lib.optionalString (stdenv.isDarwin && stdenv.isAarch64) ''
130 substituteInPlace makefile \
131 --replace "aarch64" "arm64"
132 '';
133
134 buildPhase = ''
135 runHook preBuild
136 local HOME=$TMPDIR
137 ./bin/nim c --parallelBuild:$NIX_BUILD_CORES koch
138 ./koch boot $kochArgs --parallelBuild:$NIX_BUILD_CORES
139 ./koch toolsNoExternal $kochArgs --parallelBuild:$NIX_BUILD_CORES
140 ./bin/nim js -d:release tools/dochack/dochack.nim
141 runHook postBuild
142 '';
143
144 installPhase = ''
145 runHook preInstall
146 install -Dt $out/bin bin/*
147 ln -sf $out/nim/bin/nim $out/bin/nim
148 ln -sf $out/nim/lib $out/lib
149 ./install.sh $out
150 cp -a tools dist $out/nim/
151 runHook postInstall
152 '';
153
154 meta = with lib; {
155 description = "Statically typed, imperative programming language";
156 homepage = "https://nim-lang.org/";
157 license = licenses.mit;
158 mainProgram = "nim";
159 maintainers = with maintainers; [ ehmry ];
160 };
161 });
162
163 nim-unwrapped-1 = nim-unwrapped-2.overrideAttrs (finalAttrs: prevAttrs: {
164 version = "1.6.20";
165 src = fetchurl {
166 url = "https://nim-lang.org/download/nim-${finalAttrs.version}.tar.xz";
167 hash = "sha256-/+0EdQTR/K9hDw3Xzz4Ce+kaKSsMnFEWFQTC87mE/7k=";
168 };
169
170 patches = [
171 ./NIM_CONFIG_DIR.patch
172 # Override compiler configuration via an environmental variable
173
174 ./nixbuild.patch
175 # Load libraries at runtime by absolute path
176
177 ./extra-mangling.patch
178 # Mangle store paths of modules to prevent runtime dependence.
179 ] ++ lib.optional (!stdenv.hostPlatform.isWindows) ./toLocation.patch;
180 });
181
182} // (let
183 wrapNim = { nim', patches }:
184 let targetPlatformConfig = stdenv.targetPlatform.config;
185 in stdenv.mkDerivation (finalAttrs: {
186 name = "${targetPlatformConfig}-nim-wrapper-${nim'.version}";
187 inherit (nim') version;
188 preferLocalBuild = true;
189 strictDeps = true;
190
191 nativeBuildInputs = [ makeWrapper ];
192
193 # Needed for any nim package that uses the standard library's
194 # 'std/sysrand' module.
195 depsTargetTargetPropagated = lib.optional stdenv.isDarwin Security;
196
197 inherit patches;
198
199 unpackPhase = ''
200 runHook preUnpack
201 tar xf ${nim'.src} nim-$version/config
202 cd nim-$version
203 runHook postUnpack
204 '';
205
206 dontConfigure = true;
207
208 buildPhase =
209 # Configure the Nim compiler to use $CC and $CXX as backends
210 # The compiler is configured by two configuration files, each with
211 # a different DSL. The order of evaluation matters and that order
212 # is not documented, so duplicate the configuration across both files.
213 ''
214 runHook preBuild
215 cat >> config/config.nims << WTF
216
217 switch("os", "${nimTarget.os}")
218 switch("cpu", "${nimTarget.cpu}")
219 switch("define", "nixbuild")
220
221 # Configure the compiler using the $CC set by Nix at build time
222 import strutils
223 let cc = getEnv"CC"
224 if cc.contains("gcc"):
225 switch("cc", "gcc")
226 elif cc.contains("clang"):
227 switch("cc", "clang")
228 WTF
229
230 mv config/nim.cfg config/nim.cfg.old
231 cat > config/nim.cfg << WTF
232 os = "${nimTarget.os}"
233 cpu = "${nimTarget.cpu}"
234 define:"nixbuild"
235 WTF
236
237 cat >> config/nim.cfg < config/nim.cfg.old
238 rm config/nim.cfg.old
239
240 cat >> config/nim.cfg << WTF
241
242 clang.cpp.exe %= "\$CXX"
243 clang.cpp.linkerexe %= "\$CXX"
244 clang.exe %= "\$CC"
245 clang.linkerexe %= "\$CC"
246 gcc.cpp.exe %= "\$CXX"
247 gcc.cpp.linkerexe %= "\$CXX"
248 gcc.exe %= "\$CC"
249 gcc.linkerexe %= "\$CC"
250 WTF
251
252 runHook postBuild
253 '';
254
255 wrapperArgs = lib.optionals (!(stdenv.isDarwin && stdenv.isAarch64)) [
256 "--prefix PATH : ${lib.makeBinPath [ buildPackages.gdb ]}:${
257 placeholder "out"
258 }/bin"
259 # Used by nim-gdb
260
261 "--prefix LD_LIBRARY_PATH : ${lib.makeLibraryPath [ openssl pcre ]}"
262 # These libraries may be referred to by the standard library.
263 # This is broken for cross-compilation because the package
264 # set will be shifted back by nativeBuildInputs.
265
266 "--set NIM_CONFIG_PATH ${placeholder "out"}/etc/nim"
267 # Use the custom configuration
268 ];
269
270 installPhase = ''
271 runHook preInstall
272
273 mkdir -p $out/bin $out/etc
274
275 cp -r config $out/etc/nim
276
277 for binpath in ${nim'}/bin/nim?*; do
278 local binname=`basename $binpath`
279 makeWrapper \
280 $binpath $out/bin/${targetPlatformConfig}-$binname \
281 $wrapperArgs
282 ln -s $out/bin/${targetPlatformConfig}-$binname $out/bin/$binname
283 done
284
285 makeWrapper \
286 ${nim'}/nim/bin/nim $out/bin/${targetPlatformConfig}-nim \
287 --set-default CC $(command -v $CC) \
288 --set-default CXX $(command -v $CXX) \
289 $wrapperArgs
290 ln -s $out/bin/${targetPlatformConfig}-nim $out/bin/nim
291
292 makeWrapper \
293 ${nim'}/bin/testament $out/bin/${targetPlatformConfig}-testament \
294 $wrapperArgs
295 ln -s $out/bin/${targetPlatformConfig}-testament $out/bin/testament
296
297 '' + ''
298 runHook postInstall
299 '';
300
301 passthru = { nim = nim'; };
302
303 meta = nim'.meta // {
304 description = nim'.meta.description
305 + " (${targetPlatformConfig} wrapper)";
306 platforms = with lib.platforms; unix ++ genode ++ windows;
307 };
308 });
309in {
310
311 nim2 = wrapNim {
312 nim' = buildPackages.nim-unwrapped-2;
313 patches = [ ./nim2.cfg.patch ];
314 };
315
316 nim1 = wrapNim {
317 nim' = buildPackages.nim-unwrapped-1;
318 patches = [ ./nim.cfg.patch ];
319 };
320
321})