1{
2 stdenv,
3 callPackage,
4 fetchFromGitHub,
5 fetchurl,
6 lib,
7 replaceVars,
8 # Dependencies
9 boehmgc,
10 coreutils,
11 git,
12 gmp,
13 hostname,
14 libevent,
15 libiconv,
16 libxml2,
17 libyaml,
18 libffi,
19 llvmPackages_19,
20 llvmPackages_20,
21 llvmPackages_21,
22 makeWrapper,
23 openssl,
24 pcre2,
25 pkg-config,
26 installShellFiles,
27 readline,
28 tzdata,
29 which,
30 zlib,
31}:
32
33# We need to keep around at least the latest version released with a stable
34# NixOS
35let
36 archs = {
37 x86_64-linux = "linux-x86_64";
38 i686-linux = "linux-i686";
39 x86_64-darwin = "darwin-universal";
40 aarch64-darwin = "darwin-universal";
41 aarch64-linux = "linux-aarch64";
42 };
43
44 arch = archs.${stdenv.system} or (throw "system ${stdenv.system} not supported");
45
46 nativeCheckInputs = [
47 git
48 gmp
49 openssl
50 readline
51 libxml2
52 libyaml
53 libffi
54 ];
55
56 binaryUrl =
57 version: rel:
58 if arch == archs.aarch64-linux then
59 "https://dev.alpinelinux.org/archive/crystal/crystal-${version}-aarch64-alpine-linux-musl.tar.gz"
60 else
61 "https://github.com/crystal-lang/crystal/releases/download/${version}/crystal-${version}-${toString rel}-${arch}.tar.gz";
62
63 genericBinary =
64 {
65 version,
66 sha256s,
67 rel ? 1,
68 }:
69 stdenv.mkDerivation rec {
70 pname = "crystal-binary";
71 inherit version;
72
73 src = fetchurl {
74 url = binaryUrl version rel;
75 sha256 = sha256s.${stdenv.system};
76 };
77
78 buildCommand = ''
79 mkdir -p $out
80 tar --strip-components=1 -C $out -xf ${src}
81 patchShebangs $out/bin/crystal
82 '';
83
84 meta.platforms = lib.attrNames sha256s;
85 };
86
87 generic =
88 {
89 version,
90 sha256,
91 binary,
92 llvmPackages,
93 doCheck ? true,
94 extraBuildInputs ? [ ],
95 buildFlags ? [
96 "all"
97 "docs"
98 "release=1"
99 ],
100 }:
101 stdenv.mkDerivation (finalAttrs: {
102 pname = "crystal";
103 inherit buildFlags doCheck version;
104
105 src = fetchFromGitHub {
106 owner = "crystal-lang";
107 repo = "crystal";
108 rev = version;
109 inherit sha256;
110 };
111
112 patches = [
113 (replaceVars ./tzdata.patch {
114 inherit tzdata;
115 })
116 ];
117
118 outputs = [
119 "out"
120 "lib"
121 "bin"
122 ];
123
124 postPatch = ''
125 export TMP=$(mktemp -d)
126 export HOME=$TMP
127 export TMPDIR=$TMP
128 mkdir -p $HOME/test
129
130 # Add dependency of crystal to docs to avoid issue on flag changes between releases
131 # https://github.com/crystal-lang/crystal/pull/8792#issuecomment-614004782
132 substituteInPlace Makefile \
133 --replace 'docs: ## Generate standard library documentation' 'docs: crystal ## Generate standard library documentation'
134
135 mkdir -p $TMP/crystal
136
137 substituteInPlace spec/std/file_spec.cr \
138 --replace '/bin/ls' '${coreutils}/bin/ls' \
139 --replace '/usr/share' "$TMP/crystal" \
140 --replace '/usr' "$TMP" \
141 --replace '/tmp' "$TMP"
142
143 substituteInPlace spec/std/process_spec.cr \
144 --replace '/bin/cat' '${coreutils}/bin/cat' \
145 --replace '/bin/ls' '${coreutils}/bin/ls' \
146 --replace '/usr/bin/env' '${coreutils}/bin/env' \
147 --replace '"env"' '"${coreutils}/bin/env"' \
148 --replace '/usr' "$TMP" \
149 --replace '/tmp' "$TMP"
150
151 substituteInPlace spec/std/system_spec.cr \
152 --replace '`hostname`' '`${hostname}/bin/hostname`'
153
154 # See https://github.com/crystal-lang/crystal/issues/8629
155 substituteInPlace spec/std/socket/udp_socket_spec.cr \
156 --replace 'it "joins and transmits to multicast groups"' 'pending "joins and transmits to multicast groups"'
157
158 ''
159 + lib.optionalString (stdenv.cc.isClang && (stdenv.cc.libcxx != null)) ''
160 # Darwin links against libc++ not libstdc++. Newer versions of clang (12+) require
161 # libc++abi to be linked explicitly (see https://github.com/NixOS/nixpkgs/issues/166205).
162 substituteInPlace src/llvm/lib_llvm.cr \
163 --replace '@[Link("stdc++")]' '@[Link("c++")]'
164 '';
165
166 # Defaults are 4
167 preBuild = ''
168 export CRYSTAL_WORKERS=$NIX_BUILD_CORES
169 export threads=$NIX_BUILD_CORES
170 export CRYSTAL_CACHE_DIR=$TMP
171 export MACOSX_DEPLOYMENT_TARGET=10.11
172 export SOURCE_DATE_EPOCH="$(<src/SOURCE_DATE_EPOCH)"
173 '';
174
175 strictDeps = true;
176 nativeBuildInputs = [
177 binary
178 makeWrapper
179 which
180 pkg-config
181 llvmPackages.llvm
182 installShellFiles
183 ];
184 buildInputs = [
185 boehmgc
186 pcre2
187 libevent
188 libyaml
189 zlib
190 libxml2
191 openssl
192 ]
193 ++ extraBuildInputs
194 ++ lib.optionals stdenv.hostPlatform.isDarwin [ libiconv ];
195
196 makeFlags = [
197 "CRYSTAL_CONFIG_VERSION=${version}"
198 "progress=1"
199 ];
200
201 LLVM_CONFIG = "${llvmPackages.llvm.dev}/bin/llvm-config";
202
203 FLAGS = [
204 "--single-module" # needed for deterministic builds
205 ];
206
207 # This makes sure we don't keep depending on the previous version of
208 # crystal used to build this one.
209 CRYSTAL_LIBRARY_PATH = "${placeholder "lib"}/crystal";
210
211 # We *have* to add `which` to the PATH or crystal is unable to build
212 # stuff later if which is not available.
213 installPhase = ''
214 runHook preInstall
215
216 install -Dm755 .build/crystal $bin/bin/crystal
217 wrapProgram $bin/bin/crystal \
218 --suffix PATH : ${
219 lib.makeBinPath [
220 pkg-config
221 llvmPackages.clang
222 which
223 ]
224 } \
225 --suffix CRYSTAL_PATH : lib:$lib/crystal \
226 --suffix PKG_CONFIG_PATH : ${
227 lib.makeSearchPathOutput "dev" "lib/pkgconfig" finalAttrs.buildInputs
228 } \
229 --suffix CRYSTAL_LIBRARY_PATH : ${lib.makeLibraryPath finalAttrs.buildInputs}
230 install -dm755 $lib/crystal
231 cp -r src/* $lib/crystal/
232
233 install -dm755 $out/share/doc/crystal/api
234 cp -r docs/* $out/share/doc/crystal/api/
235 cp -r samples $out/share/doc/crystal/
236
237 installShellCompletion --cmd ${finalAttrs.meta.mainProgram} etc/completion.*
238
239 installManPage man/crystal.1
240
241 install -Dm644 -t $out/share/licenses/crystal LICENSE README.md
242
243 mkdir -p $out
244 ln -s $bin/bin $out/bin
245 ln -s $bin/share/bash-completion $out/share/bash-completion
246 ln -s $bin/share/zsh $out/share/zsh
247 ln -s $bin/share/fish $out/share/fish
248 ln -s $lib $out/lib
249
250 runHook postInstall
251 '';
252
253 enableParallelBuilding = true;
254
255 dontStrip = true;
256
257 checkTarget = "compiler_spec";
258
259 preCheck = ''
260 export LIBRARY_PATH=${lib.makeLibraryPath nativeCheckInputs}:$LIBRARY_PATH
261 export PATH=${lib.makeBinPath nativeCheckInputs}:$PATH
262 '';
263
264 passthru.buildBinary = binary;
265 passthru.buildCrystalPackage = callPackage ./build-package.nix {
266 crystal = finalAttrs.finalPackage;
267 };
268 passthru.llvmPackages = llvmPackages;
269
270 meta = with lib; {
271 inherit (binary.meta) platforms;
272 description = "Compiled language with Ruby like syntax and type inference";
273 mainProgram = "crystal";
274 homepage = "https://crystal-lang.org/";
275 license = licenses.asl20;
276 maintainers = with maintainers; [
277 david50407
278 manveru
279 peterhoeg
280 donovanglover
281 ];
282 };
283 });
284in
285rec {
286 binaryCrystal_1_10 = genericBinary {
287 version = "1.10.1";
288 sha256s = {
289 x86_64-linux = "sha256-F0LjdV02U9G6B8ApHxClF/o5KvhxMNukSX7Z2CwSNIs=";
290 aarch64-darwin = "sha256-5kkObQl0VIO6zqQ8TYl0JzYyUmwfmPE9targpfwseSQ=";
291 x86_64-darwin = "sha256-5kkObQl0VIO6zqQ8TYl0JzYyUmwfmPE9targpfwseSQ=";
292 aarch64-linux = "sha256-AzFz+nrU/HJmCL1hbCKXf5ej/uypqV1GJPVLQ4J3778=";
293 };
294 };
295
296 crystal_1_14 = generic {
297 version = "1.14.1";
298 sha256 = "sha256-cQWK92BfksOW8GmoXn4BmPGJ7CLyLAeKccOffQMh5UU=";
299 binary = binaryCrystal_1_10;
300 llvmPackages = llvmPackages_19;
301 doCheck = false; # Some compiler spec problems on x86-64_linux with the .0 release
302 };
303
304 crystal_1_15 = generic {
305 version = "1.15.1";
306 sha256 = "sha256-L/Q8yZdDq/wn4kJ+zpLfi4pxznAtgjxTCbLnEiCC2K0=";
307 binary = binaryCrystal_1_10;
308 llvmPackages = llvmPackages_19;
309 doCheck = false;
310 };
311
312 crystal_1_16 = generic {
313 version = "1.16.3";
314 sha256 = "sha256-U9H1tHUMyDNicZnXzEccDki5bGXdV0B2Wu2PyCksPVI=";
315 binary = binaryCrystal_1_10;
316 llvmPackages = llvmPackages_20;
317 doCheck = false;
318 };
319
320 crystal_1_17 = generic {
321 version = "1.17.1";
322 sha256 = "sha256-+wHhozPhpIsfQy1Lw+V48zvuWCfXzT4IC9KA1AU/DLw=";
323 binary = binaryCrystal_1_10;
324 llvmPackages = llvmPackages_21;
325 doCheck = false;
326 };
327
328 crystal = crystal_1_16;
329}