nix: build docker images
[xonotic/xonotic.git] / derivation.nix
1 # nix-shell -A shell
2 # nix-build -A xonotic
3 # --argstr cc clang
4 # for it in $(nix-build -A dockerImage --no-out-link); do docker load -i $it; done
5 {
6     pkgs, lib,
7     cc ? null,
8     cmake ? pkgs.cmake_2_8,
9 }:
10 let
11     VERSION = "0.8.2";
12
13     srcs = {
14         # https://gitlab.com/xonotic/xonotic
15         "xonotic" = localFilesMain ./.;
16         "data/font-dejavu" = localFiles ./data/font-dejavu.pk3dir;
17         "data/font-nimbussansl" = localFiles ./data/font-nimbussansl.pk3dir;
18         "data/font-unifont" = localFiles ./data/font-unifont.pk3dir;
19         "data/font-xolonium" = localFiles ./data/font-xolonium.pk3dir;
20
21         # https://gitlab.com/xonotic/d0_blind_id
22         "d0_blind_id" = localFiles ./d0_blind_id;
23
24         # https://gitlab.com/xonotic/darkplaces
25         "darkplaces" = localFiles ./darkplaces;
26
27         # https://gitlab.com/xonotic/gmqcc
28         "gmqcc" = localFiles ./gmqcc;
29
30         # https://gitlab.com/xonotic/netradiant
31         "netradiant" = localFiles ./netradiant;
32
33         # https://gitlab.com/xonotic/xonotic-data.pk3dir
34         "data/xonotic-data" = localFilesCustom ./data/xonotic-data.pk3dir (name: type: type == "directory" || !(isCode name));
35         "data/xonotic-data/qcsrc" = localFilesCustom ./data/xonotic-data.pk3dir (name: type: type == "directory" || (isCode name));
36
37         # https://gitlab.com/xonotic/xonotic-maps.pk3dir
38         "data/xonotic-maps" = localFiles ./data/xonotic-maps.pk3dir;
39
40         # https://gitlab.com/xonotic/xonotic-music.pk3dir
41         "data/xonotic-music" = localFiles ./data/xonotic-music.pk3dir;
42
43         # https://gitlab.com/xonotic/xonotic-nexcompat.pk3dir
44         "data/xonotic-nexcompat" = localFiles ./data/xonotic-nexcompat.pk3dir;
45     };
46
47     localFilesMain = src: let
48         project = toString ./.;
49         cleanSourceFilterMain = name: type: let
50             baseName = baseNameOf (toString name);
51             result = (cleanSourceFilter name type)
52                 && !(name == "${project}/release")
53                 && !(name == "${project}/d0_blind_id")
54                 && !(name == "${project}/daemon")
55                 && !(name == "${project}/darkplaces")
56                 && !(name == "${project}/data")
57                 && !(name == "${project}/gmqcc")
58                 && !(name == "${project}/netradiant")
59                 && !(name == "${project}/wiki" || name == "${project}/wiki.yes")
60                 && !(name == "${project}/xonstat" || name == "${project}/xonstat.yes")
61             ;
62         in result;
63     in builtins.filterSource cleanSourceFilterMain src;
64
65     isCode = name: let
66         baseName = baseNameOf (toString name);
67         result = !(false
68             || (lib.hasSuffix ".ase" baseName)
69             || (lib.hasSuffix ".dem" baseName)
70             || (lib.hasSuffix ".dpm" baseName)
71             || (lib.hasSuffix ".framegroups" baseName)
72             || (lib.hasSuffix ".iqm" baseName)
73             || (lib.hasSuffix ".jpg" baseName)
74             || (lib.hasSuffix ".lmp" baseName)
75             || (lib.hasSuffix ".md3" baseName)
76             || (lib.hasSuffix ".mdl" baseName)
77             || (lib.hasSuffix ".obj" baseName)
78             || (lib.hasSuffix ".ogg" baseName)
79             || (lib.hasSuffix ".png" baseName)
80             || (lib.hasSuffix ".shader" baseName)
81             || (lib.hasSuffix ".skin" baseName)
82             || (lib.hasSuffix ".sounds" baseName)
83             || (lib.hasSuffix ".sp2" baseName)
84             || (lib.hasSuffix ".spr" baseName)
85             || (lib.hasSuffix ".spr32" baseName)
86             || (lib.hasSuffix ".svg" baseName)
87             || (lib.hasSuffix ".tga" baseName)
88             || (lib.hasSuffix ".wav" baseName)
89             || (lib.hasSuffix ".width" baseName)
90             || (lib.hasSuffix ".zym" baseName)
91         );
92     in result;
93
94     pk3 = drv: mkDerivation {
95         name = "${drv.name}.pk3";
96         version = drv.version;
97
98         nativeBuildInputs = with pkgs; [
99             zip
100         ];
101
102         phases = [ "installPhase" ];
103         installPhase = ''
104             (cd ${drv} && zip -r ${drv.pk3args or ""} $out .)
105         '';
106     };
107
108     targets = rec {
109         font-dejavu = mkDerivation rec {
110             name = "font-dejavu-${version}";
111             version = VERSION;
112
113             src = srcs."data/font-dejavu";
114
115             phases = [ "installPhase" ];
116             installPhase = ''
117                 cp -r $src $out
118             '';
119         };
120
121         font-nimbussansl = mkDerivation rec {
122             name = "font-nimbussansl-${version}";
123             version = VERSION;
124
125             src = srcs."data/font-nimbussansl";
126
127             phases = [ "installPhase" ];
128             installPhase = ''
129                 cp -r $src $out
130             '';
131         };
132
133         font-unifont = mkDerivation rec {
134             name = "font-unifont-${version}";
135             version = VERSION;
136
137             src = srcs."data/font-unifont";
138
139             phases = [ "installPhase" ];
140             installPhase = ''
141                 cp -r $src $out
142             '';
143         };
144
145         font-xolonium = mkDerivation rec {
146             name = "font-xolonium-${version}";
147             version = VERSION;
148
149             src = srcs."data/font-xolonium";
150
151             phases = [ "installPhase" ];
152             installPhase = ''
153                 cp -r $src $out
154             '';
155         };
156
157         d0_blind_id = mkDerivation rec {
158             name = "d0_blind_id-${version}";
159             version = "xonotic-${VERSION}";
160
161             src = srcs."d0_blind_id";
162
163             nativeBuildInputs = [
164                 cmake
165             ];
166
167             buildInputs = with pkgs; [
168                 openssl
169             ];
170
171             installPhase = ''
172                 mkdir -p $out/lib
173                 cp libd0_blind_id.so $out/lib
174
175                 mkdir -p $out/include/d0_blind_id
176                 (cd $src; cp d0_blind_id.h d0.h $out/include/d0_blind_id)
177             '';
178         };
179
180         darkplaces = let
181             unwrapped = mkDerivation rec {
182                 name = "darkplaces-unwrapped-${version}";
183                 version = "xonotic-${VERSION}";
184
185                 src = srcs."darkplaces";
186
187                 nativeBuildInputs = [
188                     cmake
189                 ];
190
191                 buildInputs = with pkgs; [
192                     SDL2
193
194                     zlib
195                     libjpeg
196                 ];
197
198                 installPhase = ''
199                     mkdir -p $out/bin
200                     cp darkplaces-{dedicated,sdl} $out/bin
201                 '';
202             };
203             result = mkDerivation rec {
204                 name = "darkplaces-${version}";
205                 version = "xonotic-${VERSION}";
206
207                 buildInputs = unwrapped.buildInputs ++ runtimeInputs;
208                 runtimeInputs = with pkgs; [
209                     d0_blind_id
210
211                     freetype
212
213                     curl
214                     zlib
215
216                     libjpeg
217                     libpng
218
219                     libogg
220                     libtheora
221                     libvorbis
222                 ];
223
224                 phases = [ "installPhase" ];
225                 installPhase = ''
226                     mkdir -p $out/bin
227
228                     cp -r ${unwrapped}/bin .
229                     chmod +w bin/*
230                     cd bin
231
232                     for exe in darkplaces-sdl; do
233                         rpath=$(patchelf --print-rpath $exe)
234                         rpath=''${rpath:+$rpath:}${lib.makeLibraryPath runtimeInputs}
235                         patchelf --set-rpath $rpath $exe
236                     done
237
238                     for exe in dedicated sdl; do
239                         cp darkplaces-$exe $out/bin/xonotic-linux64-$exe
240                     done
241                 '';
242             };
243         in result;
244
245         gmqcc = mkDerivation rec {
246             name = "gmqcc-${version}";
247             version = "xonotic-${VERSION}";
248
249             src = srcs."gmqcc";
250
251             nativeBuildInputs = [
252                 cmake
253             ];
254
255             installPhase = ''
256                 mkdir -p $out/bin
257                 cp gmqcc $out/bin
258             '';
259         };
260
261         netradiant = mkDerivation rec {
262             name = "netradiant-${version}";
263             version = VERSION;
264
265             src = srcs."netradiant";
266
267             nativeBuildInputs = with pkgs; [
268                 cmake
269                 git
270             ];
271
272             buildInputs = with pkgs; [
273                 pkgconfig
274                 glib
275                 pcre
276                 libxml2
277                 ncurses
278                 libjpeg
279                 libpng
280                 minizip
281
282                 mesa
283
284                 xorg.libXt
285                 xorg.libXmu
286                 xorg.libSM
287                 xorg.libICE
288                 xorg.libpthreadstubs
289                 xorg.libXdmcp
290
291                 gnome2.gtk
292                 gnome2.gtkglext
293                 gnome3.gtk
294             ];
295         };
296
297         xonotic-data = mkDerivation rec {
298             name = "xonotic-data-${version}";
299             version = "xonotic-${VERSION}";
300
301             src = srcs."data/xonotic-data";
302
303             phases = [ "installPhase" ];
304             installPhase = ''
305                 mkdir $out
306                 cp -r $src/. $out
307                 chmod -R +w $out
308                 find $out -depth -type d -empty -exec rmdir {} \;
309             '';
310         };
311
312         xonotic-data-code = mkDerivation rec {
313             name = "xonotic-data-code-${version}";
314             version = "xonotic-${VERSION}";
315
316             src = srcs."data/xonotic-data/qcsrc";
317
318             env = {
319                 QCC = "${gmqcc}/bin/gmqcc";
320             };
321
322             nativeBuildInputs = with pkgs; [
323                 cmake
324                 git
325             ];
326
327             installPhase = ''
328                 mkdir $out
329                 cp -r $src/. $out
330                 chmod -R +w $out
331                 cp {menu,progs,csprogs}.{dat,lno} $out
332                 find $out -depth -type d -empty -exec rmdir {} \;
333             '';
334         };
335
336         # todo: build
337         xonotic-maps = mkDerivation rec {
338             name = "xonotic-maps-${version}";
339             version = "xonotic-${VERSION}";
340
341             src = srcs."data/xonotic-maps";
342
343             phases = [ "installPhase" ];
344             installPhase = ''
345                 mkdir $out
346                 cp -r $src/. $out
347             '';
348         };
349
350         xonotic-music = mkDerivation rec {
351             name = "xonotic-music-${version}";
352             version = "xonotic-${VERSION}";
353
354             src = srcs."data/xonotic-music";
355
356             phases = [ "installPhase" ];
357             installPhase = ''
358                 mkdir $out
359                 cp -r $src/. $out
360             '';
361
362             passthru.pk3args = "-0";
363         };
364
365         xonotic-nexcompat = mkDerivation rec {
366             name = "xonotic-nexcompat-${version}";
367             version = "xonotic-${VERSION}";
368
369             src = srcs."data/xonotic-nexcompat";
370
371             phases = [ "installPhase" ];
372             installPhase = ''
373                 mkdir $out
374                 cp -r $src/. $out
375             '';
376         };
377
378         xonotic = mkDerivation rec {
379             name = "xonotic-${version}";
380             version = VERSION;
381
382             src = srcs."xonotic";
383
384             env = {
385                 XON_NO_DAEMON = "1";
386             };
387
388             passthru.paks = {
389                 inherit
390                     font-dejavu
391                     font-nimbussansl
392                     font-unifont
393                     font-xolonium
394                     xonotic-data
395                     xonotic-data-code
396                     xonotic-maps
397                     xonotic-music
398                     xonotic-nexcompat
399                 ;
400             };
401
402             phases = [ "installPhase" ];
403
404             installPhase = ''
405                 mkdir $out
406                 cp -r $src/. $out
407                 cp ${darkplaces}/bin/* $out
408
409                 mkdir -p $out/data
410                 ${lib.concatStringsSep "\n" (lib.mapAttrsToList (k: v:
411                     # "cp ${pk3 v} $out/data/${k}.pk3"
412                     "ln -s ${v} $out/data/${k}.pk3dir"
413                 ) passthru.paks)}
414
415                 mkdir -p $out/mapping
416                 ln -s ${netradiant} $out/mapping/${netradiant.name}
417             '';
418         };
419
420         dockerImage = let
421             main = pkgs.dockerTools.buildImage {
422                 name = "xonotic";
423                 tag = VERSION;
424                 contents = mkDerivation {
425                     name = "xonotic-init";
426                     phases = [ "installPhase" ];
427                     installPhase = ''
428                         mkdir -p $out
429                         cat > $out/init <<EOF
430                         #!${stdenv.shell}
431                         ${pkgs.coreutils}/bin/ls -l /data
432                         exec ${darkplaces}/bin/xonotic-linux64-dedicated
433                         EOF
434                         chmod +x $out/init
435                     '';
436                 };
437                 config.Entrypoint = "/init";
438             };
439             unpackImage = { name, from, to }: pkgs.dockerTools.buildImage {
440                 name = "xonotic_${name}";
441                 tag = VERSION;
442                 contents = mkDerivation {
443                     name = "xonotic-${name}-init";
444                     phases = [ "installPhase" ];
445                     installPhase = ''
446                         mkdir -p $out
447                         cat > $out/init <<EOF
448                         #!${stdenv.shell}
449                         ${pkgs.coreutils}/bin/cp -r ${from} /data/${to}
450                         EOF
451                         chmod +x $out/init
452                     '';
453                 };
454                 config.Entrypoint = "/init";
455             };
456         in { main = main; }
457             // (lib.mapAttrs (k: v: unpackImage { name = k; from = pk3 v; to = "${k}.pk3"; }) xonotic.paks)
458         ;
459     };
460
461     cleanSourceFilter = name: type: let
462         baseName = baseNameOf (toString name);
463         result = (lib.cleanSourceFilter name type)
464             && !(lib.hasSuffix ".nix" baseName)
465             && !(type == "directory" && baseName == ".git")
466             && !(type == "directory" && baseName == ".idea")
467             && !(type == "directory" && (lib.hasPrefix "cmake-build-" baseName))
468         ;
469     in result;
470
471     localFilesCustom = src: filter:
472         builtins.filterSource (name: type: (cleanSourceFilter name type) && (filter name type)) src
473     ;
474
475     localFiles = src: localFilesCustom src (name: type: true);
476
477     stdenv = if (cc == null) then pkgs.stdenv
478             else pkgs.overrideCC pkgs.stdenv pkgs."${cc}";
479
480     mkDerivation = {env ? {}, shellHook ? "", runtimeInputs ? [], ...}@args: stdenv.mkDerivation ({}
481         // { enableParallelBuilding = true; }
482         // (removeAttrs args ["env" "shellHook" "runtimeInputs"])
483         // env
484         // {
485             shellHook = ''
486                 ${shellHook}
487                 ${lib.concatStringsSep "\n" (lib.mapAttrsToList (n: v: "export ${n}=${v}") env)}
488                 export LD_LIBRARY_PATH=''${LD_LIBRARY_PATH:+$LD_LIBRARY_PATH:}${lib.makeLibraryPath runtimeInputs}
489             '';
490         }
491     );
492
493     shell = let inputs = (lib.mapAttrsToList (k: v: v) targets); in stdenv.mkDerivation (rec {
494         name = "xonotic-shell";
495         nativeBuildInputs = builtins.map (it: it.nativeBuildInputs) (builtins.filter (it: it?nativeBuildInputs) inputs);
496         buildInputs = builtins.map (it: it.buildInputs) (builtins.filter (it: it?buildInputs) inputs);
497         shellHook = builtins.map (it: it.shellHook) (builtins.filter (it: it?shellHook) inputs);
498     });
499 in { inherit shell; } // targets