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