nix: fix dedicated servers
[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 dedicated sdl; do
233                         f=darkplaces-$exe
234                         rpath=$(patchelf --print-rpath $f)
235                         rpath=''${rpath:+$rpath:}${lib.makeLibraryPath runtimeInputs}
236                         patchelf --set-rpath $rpath $f
237                         cp $f $out/bin/xonotic-linux64-$exe
238                     done
239                 '';
240             };
241         in result;
242
243         gmqcc = mkDerivation rec {
244             name = "gmqcc-${version}";
245             version = "xonotic-${VERSION}";
246
247             src = srcs."gmqcc";
248
249             nativeBuildInputs = [
250                 cmake
251             ];
252
253             installPhase = ''
254                 mkdir -p $out/bin
255                 cp gmqcc $out/bin
256             '';
257         };
258
259         netradiant = mkDerivation rec {
260             name = "netradiant-${version}";
261             version = VERSION;
262
263             src = srcs."netradiant";
264
265             nativeBuildInputs = with pkgs; [
266                 cmake
267                 git
268             ];
269
270             buildInputs = with pkgs; [
271                 pkgconfig
272                 glib
273                 pcre
274                 libxml2
275                 ncurses
276                 libjpeg
277                 libpng
278                 minizip
279
280                 mesa
281
282                 xorg.libXt
283                 xorg.libXmu
284                 xorg.libSM
285                 xorg.libICE
286                 xorg.libpthreadstubs
287                 xorg.libXdmcp
288
289                 gnome2.gtk
290                 gnome2.gtkglext
291                 gnome3.gtk
292             ];
293         };
294
295         xonotic-data = mkDerivation rec {
296             name = "xonotic-data-${version}";
297             version = "xonotic-${VERSION}";
298
299             src = srcs."data/xonotic-data";
300
301             phases = [ "installPhase" ];
302             installPhase = ''
303                 mkdir $out
304                 cp -r $src/. $out
305                 chmod -R +w $out
306                 find $out -depth -type d -empty -exec rmdir {} \;
307             '';
308         };
309
310         xonotic-data-code = mkDerivation rec {
311             name = "xonotic-data-code-${version}";
312             version = "xonotic-${VERSION}";
313
314             src = srcs."data/xonotic-data/qcsrc";
315
316             env = {
317                 QCC = "${gmqcc}/bin/gmqcc";
318             };
319
320             nativeBuildInputs = with pkgs; [
321                 cmake
322                 git
323             ];
324
325             installPhase = ''
326                 mkdir $out
327                 cp -r $src/. $out
328                 chmod -R +w $out
329                 cp {menu,progs,csprogs}.{dat,lno} $out
330                 find $out -depth -type d -empty -exec rmdir {} \;
331             '';
332         };
333
334         # todo: build
335         xonotic-maps = mkDerivation rec {
336             name = "xonotic-maps-${version}";
337             version = "xonotic-${VERSION}";
338
339             src = srcs."data/xonotic-maps";
340
341             phases = [ "installPhase" ];
342             installPhase = ''
343                 mkdir $out
344                 cp -r $src/. $out
345             '';
346
347             passthru.dance = mkDerivation rec {
348                 name = "dance";
349                 version = "xonotic-${VERSION}";
350
351                 src = pkgs.fetchurl {
352                     url = http://beta.xonotic.org/autobuild-bsp/dance-full-88c416b8c11bdcecfdb889af2a2b97b4c0e2b8de-319ee7234504199da56f07ce25185f6d6cb889cd.pk3;
353                     sha256 = "1jgdg4mz56kbxcy3mwn4h5qlf3ahm1cmarp9l70fz9nfn6cnaknq";
354                 };
355
356                 phases = [ "installPhase" ];
357
358                 installPhase = ''
359                     mkdir -p $out
360                     cd $out
361                     ${pkgs.unzip}/bin/unzip $src
362                 '';
363             };
364         };
365
366         xonotic-music = mkDerivation rec {
367             name = "xonotic-music-${version}";
368             version = "xonotic-${VERSION}";
369
370             src = srcs."data/xonotic-music";
371
372             phases = [ "installPhase" ];
373             installPhase = ''
374                 mkdir $out
375                 cp -r $src/. $out
376             '';
377
378             passthru.pk3args = "-0";
379         };
380
381         xonotic-nexcompat = mkDerivation rec {
382             name = "xonotic-nexcompat-${version}";
383             version = "xonotic-${VERSION}";
384
385             src = srcs."data/xonotic-nexcompat";
386
387             phases = [ "installPhase" ];
388             installPhase = ''
389                 mkdir $out
390                 cp -r $src/. $out
391             '';
392         };
393
394         xonotic = mkDerivation rec {
395             name = "xonotic-${version}";
396             version = VERSION;
397
398             src = srcs."xonotic";
399
400             env = {
401                 XON_NO_DAEMON = "1";
402             };
403
404             passthru.paks = {
405                 inherit
406                     font-dejavu
407                     font-nimbussansl
408                     font-unifont
409                     font-xolonium
410                     xonotic-data
411                     xonotic-data-code
412                     xonotic-maps
413                     xonotic-music
414                     xonotic-nexcompat
415                 ;
416                 inherit (xonotic-maps)
417                     dance
418                 ;
419             };
420
421             phases = [ "installPhase" ];
422
423             installPhase = ''
424                 mkdir $out
425                 cp -r $src/. $out
426                 cp ${darkplaces}/bin/* $out
427
428                 mkdir -p $out/data
429                 ${lib.concatStringsSep "\n" (lib.mapAttrsToList (k: v:
430                     # "cp ${pk3 v} $out/data/${k}.pk3"
431                     "ln -s ${v} $out/data/${k}.pk3dir"
432                 ) passthru.paks)}
433
434                 mkdir -p $out/mapping
435                 ln -s ${netradiant} $out/mapping/${netradiant.name}
436             '';
437         };
438
439         dockerImage = let
440             main = pkgs.dockerTools.buildImage {
441                 name = "xonotic";
442                 tag = VERSION;
443                 contents = mkDerivation {
444                     name = "xonotic-init";
445                     phases = [ "installPhase" ];
446                     installPhase = ''
447                         mkdir -p $out
448                         cat > $out/init <<EOF
449                         #!${stdenv.shell}
450                         ${pkgs.coreutils}/bin/ls -l /data
451                         exec ${darkplaces}/bin/xonotic-linux64-dedicated "\''${@}"
452                         EOF
453                         chmod +x $out/init
454                     '';
455                 };
456                 config.Entrypoint = "/init";
457             };
458             unpackImage = { name, from, to }: pkgs.dockerTools.buildImage {
459                 name = "xonotic_${name}";
460                 tag = VERSION;
461                 contents = mkDerivation {
462                     name = "xonotic-${name}-init";
463                     phases = [ "installPhase" ];
464                     installPhase = ''
465                         mkdir -p $out
466                         cat > $out/init <<EOF
467                         #!${stdenv.shell}
468                         ${pkgs.coreutils}/bin/cp -r ${from} /data/${to}
469                         EOF
470                         chmod +x $out/init
471                     '';
472                 };
473                 config.Entrypoint = "/init";
474             };
475         in { main = main; }
476             // (lib.mapAttrs (k: v: unpackImage { name = k; from = pk3 v; to = "${k}.pk3"; }) xonotic.paks)
477         ;
478     };
479
480     cleanSourceFilter = name: type: let
481         baseName = baseNameOf (toString name);
482         result = (lib.cleanSourceFilter name type)
483             && !(lib.hasSuffix ".nix" baseName)
484             && !(type == "directory" && baseName == ".git")
485             && !(type == "directory" && baseName == ".idea")
486             && !(type == "directory" && (lib.hasPrefix "cmake-build-" baseName))
487         ;
488     in result;
489
490     localFilesCustom = src: filter:
491         builtins.filterSource (name: type: (cleanSourceFilter name type) && (filter name type)) src
492     ;
493
494     localFiles = src: localFilesCustom src (name: type: true);
495
496     stdenv = if (cc == null) then pkgs.stdenv
497             else pkgs.overrideCC pkgs.stdenv pkgs."${cc}";
498
499     mkDerivation = {env ? {}, shellHook ? "", runtimeInputs ? [], ...}@args: stdenv.mkDerivation ({}
500         // { enableParallelBuilding = true; }
501         // (removeAttrs args ["env" "shellHook" "runtimeInputs"])
502         // env
503         // {
504             shellHook = ''
505                 ${shellHook}
506                 ${lib.concatStringsSep "\n" (lib.mapAttrsToList (n: v: "export ${n}=${v}") env)}
507                 export LD_LIBRARY_PATH=''${LD_LIBRARY_PATH:+$LD_LIBRARY_PATH:}${lib.makeLibraryPath runtimeInputs}
508             '';
509         }
510     );
511
512     shell = let inputs = (lib.mapAttrsToList (k: v: v) targets); in stdenv.mkDerivation (rec {
513         name = "xonotic-shell";
514         nativeBuildInputs = builtins.map (it: it.nativeBuildInputs) (builtins.filter (it: it?nativeBuildInputs) inputs);
515         buildInputs = builtins.map (it: it.buildInputs) (builtins.filter (it: it?buildInputs) inputs);
516         shellHook = builtins.map (it: it.shellHook) (builtins.filter (it: it?shellHook) inputs);
517     });
518 in { inherit shell; } // targets