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