Clone of https://github.com/NixOS/nixpkgs.git (to stress-test knotserver)
at devShellTools-shell 305 lines 8.6 kB view raw
1{ 2 stdenv, 3 lib, 4 buildEnv, 5 replaceVars, 6 makeWrapper, 7 runCommand, 8 coreutils, 9 gawk, 10 dwarf-fortress, 11 dwarf-therapist, 12 SDL2_mixer, 13 enableDFHack ? false, 14 dfhack, 15 enableSoundSense ? false, 16 soundSense, 17 jre, 18 expect, 19 xvfb-run, 20 writeText, 21 enableStoneSense ? false, 22 enableTWBT ? false, 23 twbt, 24 themes ? { }, 25 theme ? null, 26 extraPackages ? [ ], 27 # General config options: 28 enableIntro ? true, 29 enableTruetype ? null, # defaults to 24, see init.txt 30 enableFPS ? false, 31 enableTextMode ? false, 32 enableSound ? true, 33 # An attribute set of settings to override in data/init/*.txt. 34 # For example, `init.FOO = true;` is translated to `[FOO:YES]` in init.txt 35 settings ? { }, 36# TODO world-gen.txt, interface.txt require special logic 37}: 38 39let 40 dfhack' = dfhack.override { 41 inherit enableStoneSense; 42 }; 43 44 isAtLeast50 = dwarf-fortress.baseVersion >= 50; 45 46 # If TWBT is null or the dfVersion is wrong, it isn't supported (for example, on version 50). 47 enableTWBT' = enableTWBT && twbt != null && (twbt.dfVersion or null) == dwarf-fortress.version; 48 49 ptheme = if builtins.isString theme then builtins.getAttr theme themes else theme; 50 51 baseEnv = buildEnv { 52 name = "dwarf-fortress-base-env-${dwarf-fortress.dfVersion}"; 53 54 # These are in inverse order for first packages to override the next ones. 55 paths = 56 extraPackages 57 ++ lib.optional (theme != null) ptheme 58 ++ lib.optional enableDFHack dfhack' 59 ++ lib.optional enableSoundSense soundSense 60 ++ lib.optionals enableTWBT' [ 61 twbt.lib 62 twbt.art 63 ] 64 ++ [ dwarf-fortress ]; 65 66 ignoreCollisions = true; 67 }; 68 69 settings' = lib.recursiveUpdate { 70 init = { 71 PRINT_MODE = 72 if enableTextMode then 73 "TEXT" 74 else if enableTWBT' then 75 "TWBT" 76 else if stdenv.hostPlatform.isDarwin then 77 "STANDARD" # https://www.bay12games.com/dwarves/mantisbt/view.php?id=11680 78 else 79 null; 80 INTRO = enableIntro; 81 TRUETYPE = enableTruetype; 82 FPS = enableFPS; 83 SOUND = enableSound; 84 }; 85 } settings; 86 87 forEach = attrs: f: lib.concatStrings (lib.mapAttrsToList f attrs); 88 89 toTxt = 90 v: 91 if lib.isBool v then 92 if v then "YES" else "NO" 93 else if lib.isInt v then 94 toString v 95 else if lib.isString v then 96 v 97 else 98 throw "dwarf-fortress: unsupported configuration value ${toString v}"; 99 100 config = 101 runCommand "dwarf-fortress-config" 102 { 103 nativeBuildInputs = [ 104 gawk 105 makeWrapper 106 ]; 107 } 108 ( 109 '' 110 mkdir -p $out/data/init 111 112 edit_setting() { 113 v=''${v//'&'/'\&'} 114 if [ -f "$out/$file" ]; then 115 if ! gawk -i inplace -v RS='\r?\n' ' 116 { n += sub("\\[" ENVIRON["k"] ":[^]]*\\]", "[" ENVIRON["k"] ":" ENVIRON["v"] "]"); print } 117 END { exit(!n) } 118 ' "$out/$file"; then 119 echo "error: no setting named '$k' in $out/$file" >&2 120 exit 1 121 fi 122 else 123 echo "warning: no file $out/$file; cannot edit" >&2 124 fi 125 } 126 '' 127 + forEach settings' ( 128 file: kv: 129 '' 130 file=data/init/${lib.escapeShellArg file}.txt 131 if [ -f "${baseEnv}/$file" ]; then 132 cp "${baseEnv}/$file" "$out/$file" 133 else 134 echo "warning: no file ${baseEnv}/$file; cannot copy" >&2 135 fi 136 '' 137 + forEach kv ( 138 k: v: 139 lib.optionalString (v != null) '' 140 export k=${lib.escapeShellArg k} v=${lib.escapeShellArg (toTxt v)} 141 edit_setting 142 '' 143 ) 144 ) 145 + lib.optionalString enableDFHack '' 146 mkdir -p $out/hack 147 148 # Patch the MD5 149 orig_md5=$(< "${dwarf-fortress}/hash.md5.orig") 150 patched_md5=$(< "${dwarf-fortress}/hash.md5") 151 input_file="${dfhack'}/hack/symbols.xml" 152 output_file="$out/hack/symbols.xml" 153 154 echo "[DFHack Wrapper] Fixing Dwarf Fortress MD5:" 155 echo " Input: $input_file" 156 echo " Search: $orig_md5" 157 echo " Output: $output_file" 158 echo " Replace: $patched_md5" 159 160 substitute "$input_file" "$output_file" --replace-fail "$orig_md5" "$patched_md5" 161 '' 162 ); 163 164 # This is a separate environment because the config files to modify may come 165 # from any of the paths in baseEnv. 166 env = buildEnv { 167 name = "dwarf-fortress-env-${dwarf-fortress.dfVersion}"; 168 paths = [ 169 config 170 baseEnv 171 ]; 172 ignoreCollisions = true; 173 }; 174in 175 176lib.throwIf (enableTWBT' && !enableDFHack) "dwarf-fortress: TWBT requires DFHack to be enabled" 177 lib.throwIf 178 (enableStoneSense && !enableDFHack) 179 "dwarf-fortress: StoneSense requires DFHack to be enabled" 180 lib.throwIf 181 (enableTextMode && enableTWBT') 182 "dwarf-fortress: text mode and TWBT are mutually exclusive" 183 184 stdenv.mkDerivation 185 { 186 pname = "dwarf-fortress"; 187 version = dwarf-fortress.dfVersion; 188 189 dfInit = replaceVars ./dwarf-fortress-init.in { 190 inherit env; 191 stdenv_shell = "${stdenv.shell}"; 192 cp = "${coreutils}/bin/cp"; 193 rm = "${coreutils}/bin/rm"; 194 ln = "${coreutils}/bin/ln"; 195 cat = "${coreutils}/bin/cat"; 196 mkdir = "${coreutils}/bin/mkdir"; 197 printf = "${coreutils}/bin/printf"; 198 uname = "${coreutils}/bin/uname"; 199 SDL2_mixer = "${SDL2_mixer}/lib/libSDL2_mixer.so"; 200 }; 201 202 runDF = ./dwarf-fortress.in; 203 runSoundSense = ./soundSense.in; 204 205 passthru = { 206 inherit 207 dwarf-fortress 208 dwarf-therapist 209 twbt 210 env 211 ; 212 dfhack = dfhack'; 213 }; 214 215 dontUnpack = true; 216 dontBuild = true; 217 preferLocalBuild = true; 218 installPhase = '' 219 mkdir -p $out/bin 220 221 substitute $runDF $out/bin/dwarf-fortress \ 222 --subst-var-by stdenv_shell ${stdenv.shell} \ 223 --subst-var-by dfExe ${dwarf-fortress.exe} \ 224 --subst-var dfInit 225 chmod 755 $out/bin/dwarf-fortress 226 '' 227 + lib.optionalString enableDFHack '' 228 substitute $runDF $out/bin/dfhack \ 229 --subst-var-by stdenv_shell ${stdenv.shell} \ 230 --subst-var-by dfExe dfhack \ 231 --subst-var dfInit 232 chmod 755 $out/bin/dfhack 233 '' 234 + lib.optionalString enableSoundSense '' 235 substitute $runSoundSense $out/bin/soundsense \ 236 --subst-var-by stdenv_shell ${stdenv.shell} \ 237 --subst-var-by jre ${jre} \ 238 --subst-var dfInit 239 chmod 755 $out/bin/soundsense 240 ''; 241 242 doInstallCheck = stdenv.hostPlatform.isLinux; 243 nativeInstallCheckInputs = lib.optionals stdenv.hostPlatform.isLinux [ 244 expect 245 xvfb-run 246 ]; 247 248 installCheckPhase = 249 let 250 commonExpectStatements = '' 251 expect "Loading bindings from data/init/interface.txt" 252 ''; 253 dfHackExpectScript = writeText "dfhack-test.exp" ( 254 '' 255 spawn env NIXPKGS_DF_OPTS=debug xvfb-run $env(out)/bin/dfhack 256 '' 257 + commonExpectStatements 258 + '' 259 expect "DFHack is ready. Have a nice day!" 260 expect "DFHack version ${dfhack'.version}" 261 expect "\[DFHack\]#" 262 send -- "lua print(os.getenv('out'))\r" 263 expect "$env(out)" 264 # Don't send 'die' here; just exit. Some versions of dfhack crash on exit. 265 exit 0 266 '' 267 ); 268 vanillaExpectScript = 269 fmod: 270 writeText "vanilla-test.exp" ( 271 '' 272 spawn env NIXPKGS_DF_OPTS=debug,${lib.optionalString fmod "fmod"} xvfb-run $env(out)/bin/dwarf-fortress 273 '' 274 + commonExpectStatements 275 + '' 276 exit 0 277 '' 278 ); 279 in 280 '' 281 export HOME="$(mktemp -dt dwarf-fortress.XXXXXX)" 282 '' 283 + lib.optionalString enableDFHack '' 284 expect ${dfHackExpectScript} 285 df_home="$(find ~ -name "df_*" | head -n1)" 286 test -f "$df_home/dfhack" 287 '' 288 + lib.optionalString isAtLeast50 '' 289 expect ${vanillaExpectScript true} 290 df_home="$(find ~ -name "df_*" | head -n1)" 291 test ! -f "$df_home/dfhack" 292 test -f "$df_home/libfmod_plugin.so" 293 '' 294 + '' 295 expect ${vanillaExpectScript false} 296 df_home="$(find ~ -name "df_*" | head -n1)" 297 test ! -f "$df_home/dfhack" 298 test ! -f "$df_home/libfmod_plugin.so" 299 '' 300 + '' 301 test -d "$df_home/data" 302 ''; 303 304 inherit (dwarf-fortress) meta; 305 }