1{
2 stdenv,
3 lib,
4 crystal,
5 pcre2,
6 shards,
7 git,
8 pkg-config,
9 which,
10 linkFarm,
11 fetchgit,
12 fetchFromGitHub,
13 installShellFiles,
14 removeReferencesTo,
15}:
16
17{
18 # Some projects do not include a lock file, so you can pass one
19 lockFile ? null,
20 # Generate shards.nix with `nix-shell -p crystal2nix --run crystal2nix` in the projects root
21 shardsFile ? null,
22 # We support different builders. To make things more straight forward, make it
23 # user selectable instead of trying to autodetect
24 format ? "make",
25 installManPages ? true,
26 # Specify binaries to build in the form { foo.src = "src/foo.cr"; }
27 # The default `crystal build` options can be overridden with { foo.options = [ "--optionname" ]; }
28 crystalBinaries ? { },
29 enableParallelBuilding ? true,
30 # Copy all shards dependencies instead of symlinking and add write permissions
31 # to make environment more local-like
32 copyShardDeps ? false,
33 ...
34}@args:
35
36assert (
37 builtins.elem format [
38 "make"
39 "crystal"
40 "shards"
41 ]
42);
43let
44 mkDerivationArgs = builtins.removeAttrs args [
45 "format"
46 "installManPages"
47 "lockFile"
48 "shardsFile"
49 "crystalBinaries"
50 ];
51
52 crystalLib = linkFarm "crystal-lib" (
53 lib.mapAttrsToList (name: value: {
54 inherit name;
55 path = if (builtins.hasAttr "url" value) then fetchgit value else fetchFromGitHub value;
56 }) (import shardsFile)
57 );
58
59 # We no longer use --no-debug in accordance with upstream's recommendation
60 defaultOptions = [
61 "--release"
62 "--progress"
63 "--verbose"
64 ];
65
66 buildDirectly = shardsFile == null || crystalBinaries != { };
67
68 mkCrystalBuildArgs =
69 bin: attrs:
70 lib.concatStringsSep " " (
71 [
72 "crystal"
73 "build"
74 ]
75 ++ lib.optionals enableParallelBuilding [
76 "--threads"
77 "$NIX_BUILD_CORES"
78 ]
79 ++ [
80 "-o"
81 bin
82 (attrs.src or (throw "No source file for crystal binary ${bin} provided"))
83 (lib.concatStringsSep " " (attrs.options or defaultOptions))
84 ]
85 );
86
87in
88stdenv.mkDerivation (
89 mkDerivationArgs
90 // {
91
92 configurePhase =
93 args.configurePhase or (lib.concatStringsSep "\n" (
94 [
95 "runHook preConfigure"
96 ]
97 ++ lib.optional (lockFile != null) "cp ${lockFile} ./shard.lock"
98 ++ lib.optionals (shardsFile != null) [
99 "test -e lib || mkdir lib"
100 (
101 if copyShardDeps then
102 "for d in ${crystalLib}/*; do cp -r $d/ lib/; done; chmod -R +w lib/"
103 else
104 "for d in ${crystalLib}/*; do ln -s $d lib/; done"
105 )
106 "cp shard.lock lib/.shards.info"
107 ]
108 ++ [ "runHook postConfigure" ]
109 ));
110
111 CRFLAGS = lib.concatStringsSep " " defaultOptions;
112
113 PREFIX = placeholder "out";
114
115 inherit enableParallelBuilding;
116 strictDeps = true;
117 buildInputs =
118 args.buildInputs or [ ]
119 ++ [ crystal ]
120 ++ lib.optional (lib.versionAtLeast crystal.version "1.8") pcre2;
121
122 nativeBuildInputs =
123 args.nativeBuildInputs or [ ]
124 ++ [
125 crystal
126 git
127 installShellFiles
128 removeReferencesTo
129 pkg-config
130 which
131 ]
132 ++ lib.optional (format != "crystal") shards;
133
134 buildPhase =
135 args.buildPhase or (lib.concatStringsSep "\n" (
136 [
137 "runHook preBuild"
138 ]
139 ++ lib.optional (format == "make") "make \${buildTargets:-build} $makeFlags"
140 ++ lib.optionals (format == "crystal") (lib.mapAttrsToList mkCrystalBuildArgs crystalBinaries)
141 ++
142 lib.optional (format == "shards")
143 "shards build --local --production ${lib.concatStringsSep " " (args.options or defaultOptions)}"
144 ++ [ "runHook postBuild" ]
145 ));
146
147 installPhase =
148 args.installPhase or (lib.concatStringsSep "\n" (
149 [
150 "runHook preInstall"
151 ]
152 ++ lib.optional (format == "make") "make \${installTargets:-install} $installFlags"
153 ++ lib.optionals (format == "crystal") (
154 map (bin: ''
155 install -Dm555 ${
156 lib.escapeShellArgs [
157 bin
158 "${placeholder "out"}/bin/${bin}"
159 ]
160 }
161 '') (lib.attrNames crystalBinaries)
162 )
163 ++ lib.optional (format == "shards") "install -Dm555 bin/* -t $out/bin"
164 ++ [
165 ''
166 for f in README* *.md LICENSE; do
167 test -f $f && install -Dm444 $f -t $out/share/doc/${args.pname}
168 done
169 ''
170 ]
171 ++ (lib.optional installManPages ''
172 if [ -d man ]; then
173 installManPage man/*.?
174 fi
175 '')
176 ++ [
177 "remove-references-to -t ${lib.getLib crystal} $out/bin/*"
178 "runHook postInstall"
179 ]
180 ));
181
182 doCheck = args.doCheck or true;
183
184 checkPhase =
185 args.checkPhase or (lib.concatStringsSep "\n" (
186 [
187 "runHook preCheck"
188 ]
189 ++ lib.optional (format == "make") "make \${checkTarget:-test} $checkFlags"
190 ++ lib.optional (format != "make") "crystal \${checkTarget:-spec} $checkFlags"
191 ++ [ "runHook postCheck" ]
192 ));
193
194 doInstallCheck = args.doInstallCheck or true;
195
196 installCheckPhase =
197 args.installCheckPhase or ''
198 for f in $out/bin/*; do
199 if [ $f == $out/bin/*.dwarf ]; then
200 continue
201 fi
202 $f --help > /dev/null
203 done
204 '';
205
206 meta = args.meta or { } // {
207 platforms = args.meta.platforms or crystal.meta.platforms;
208 };
209 }
210)