#!/usr/bin/env bash # Compila Dungeon Crawl Stone Soup 0.34.1 do fredygames.com (Linley Henzell e a equipe de desenvolvimento do # DCSS, GPL-2.0-or-later; trechos BSD, MIT, CC0 e Boost listados no LICENSE do jogo) em WebAssembly, na # versão de console (texto), sobre a camada curses-web/ (sem mudá-la), com o Emscripten 6.0.10. Sem tiles, # sons nem fontes do jogo. Este script, o patch e o cw-wide.h fazem parte do código-fonte publicado junto do # jogo. # # source /emsdk_env.sh # emsdk install 6.0.10 && emsdk activate 6.0.10 # src/FredGames.Bundler/Builds/build-crawl.sh # # Precisa também de make, perl, python3 e um compilador C++ do sistema: o Makefile do jogo gera os # cabeçalhos (espécies, classes, monstros, artefatos, listas de tiles) com scripts em Python (PyYAML 6.0.2, # o pacote em Python puro do tarball do PyPI, conferido pelo SHA-256 e usado pelo PYTHONPATH, sem instalar) # e Perl, e compila no sistema o tilegen, que só gera as listas de tiles (rltiles/tiledef-*.cc) que o jogo # de console também liga. # # Fonte: stone_soup-0.34.1.tar.xz (release oficial no GitHub crawl/crawl), conferido pelo SHA-256 e guardado # em artifacts/cache/crawl/ (o site hospeda o mesmo arquivo); na pasta de fontes do site, o script acha tudo # ao lado dele. # # O que muda: # - crawl-fredygames.patch: crash.cc não usa execinfo.h (backtrace) no Emscripten, como já não usa no # Android e nos BSDs; # - cw-wide.h (arquivo novo, incluído no lugar de por CURSES_INCLUDE_FILE): as chamadas de curses # "wide" (cchar_t, get_wch, mvadd_wchnstr, mvin_wch, addnwstr...) que a interface de console do jogo # (libunix.cc) usa, escritas sobre a curses-web, que guarda um ponto de código Unicode por célula; # - compflag.h (as opções de compilação que o jogo mostra na tela de versão) é escrito por este script; # - DATA_DIR_PATH=/crawl/ (os dados, no crawl.data) e SAVE_DIR_PATH=/crawl-saves/ (saves, morgue, opções, # banco de descrições), que a página guarda no IndexedDB; # - Lua 5.4 e SQLite do contrib/ do próprio tarball, compilados aqui em C; a zlib é a porta do Emscripten. # - Pilha de 8 MB (o padrão de 64 KB do Emscripten estoura na geração das fases e corrompe a memória). # # Saída: artifacts/build/crawl-0.34.1/crawl.{js,wasm,data}, que o bundle.yaml confere pelo SHA-256. set -euo pipefail VERSION=0.34.1 SRC_SHA256=473b9cdc16be0b537ac11e43c6c77db4b290000e4a17f72a842eba59c6b7be2a PYYAML_SHA256=d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e EMSCRIPTEN=6.0.10 here="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" root="$(git -C "$here" rev-parse --show-toplevel 2>/dev/null || pwd)" out="$root/artifacts/build/crawl-$VERSION" cache="$root/artifacts/cache/crawl" if ! emcc --version 2>/dev/null | head -1 | grep -q " $EMSCRIPTEN "; then echo "Precisa do Emscripten $EMSCRIPTEN no PATH (source /emsdk_env.sh)." >&2 exit 1 fi # Arquivo auxiliar: em crawl/ (repositório) ou ao lado do script (pasta de fontes do site). aux() { if [ -f "$here/crawl/$1" ]; then echo "$here/crawl/$1"; else echo "$here/$1"; fi } if [ -d "$here/curses-web" ]; then CW_SRC="$here/curses-web"; else CW_SRC="$here"; fi fetch() { local name="$1" url="$2" sha="$3" file if [ -f "$here/$name" ]; then file="$here/$name" else file="$cache/$name" if [ ! -f "$file" ]; then mkdir -p "$cache" curl -fsSL -o "$file.part" "$url" mv "$file.part" "$file" fi fi echo "$sha $file" | shasum -a 256 -c - >&2 echo "$file" } tarball="$(fetch "stone_soup-$VERSION.tar.xz" \ "https://github.com/crawl/crawl/releases/download/$VERSION/stone_soup-$VERSION.tar.xz" "$SRC_SHA256")" pyyaml="$(fetch pyyaml-6.0.2.tar.gz \ "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz" \ "$PYYAML_SHA256")" rm -rf "$out" mkdir -p "$out/work" "$out/obj" "$out/cobj" tar xJf "$tarball" -C "$out/work" tar xzf "$pyyaml" -C "$out/work" S="$out/work/stone_soup-$VERSION/source" patch -d "$S/.." -p1 --no-backup-if-mismatch -s < "$(aux crawl-fredygames.patch)" mkdir -p "$out/work/inc" cp "$(aux cw-wide.h)" "$out/work/inc/" . "$CW_SRC/flags.sh" if [ "$CW_SRC" = "$here" ]; then CW_DIR="$here"; fi # ---- Cabeçalhos gerados pelo Makefile do jogo (Python/Perl e o tilegen do sistema) ---- GEN="art-enum.h config.h mon-mst.h species-type.h job-type.h form-data.h art-data.h mi-enum.h build.h dat/dlua/tags.lua cmd-name.h species-data.h aptitudes.h species-groups.h mon-data.h job-data.h job-groups.h" # GIT_CEILING_DIRECTORIES: a pasta de trabalho fica dentro do repositório do site, e o "git describe" do # Makefile não pode achá-lo (a versão vem de util/release_ver). ( cd "$S" && GIT_CEILING_DIRECTORIES="$out/work" PYTHONPATH="$out/work/pyyaml-6.0.2/lib" make -j8 NO_PKGCONFIG=y TILES= CXX=em++ CC=emcc \ AR=emar RANLIB=emranlib PYTHON=python3 $GEN ) > "$out/gen.log" 2>&1 || { tail -30 "$out/gen.log" >&2; exit 1; } cp "$S"/prebuilt/levcomp.* "$S/util/" cat > "$S/compflag.h" <<'H' #ifndef __included_crawl_compile_flags_h #define __included_crawl_compile_flags_h #define CRAWL_CFLAGS "em++ -std=c++17 -O2 -fexceptions (fredygames.com browser build)" #define CRAWL_LDFLAGS "-O2 -fexceptions -sASYNCIFY" #define CRAWL_HOST "wasm32-unknown-emscripten" #define CRAWL_ARCH "wasm32-unknown-emscripten" #endif H # ---- O jogo: os objetos do Makefile (OBJECTS, versão de console) + main.o, version.o e o compilador de fases ---- printf 'print-objects: ; @echo $(OBJECTS)\n' > "$out/work/print-objects.mk" objects="$(cd "$S" && GIT_CEILING_DIRECTORIES="$out/work" make -s --no-print-directory NO_PKGCONFIG=y TILES= \ CXX=em++ CC=emcc -f Makefile -f "$out/work/print-objects.mk" print-objects)" objects="$objects main.o version.o util/levcomp.tab.o util/levcomp.lex.o" FLAGS=(-std=c++17 -O2 -fexceptions -w -ffile-prefix-map="$S/=" -Iutil -I. -I"$out/work/inc" -I"$CW_DIR" -sUSE_ZLIB=1 -DWIZARD -DASSERTS -DCLUA_BINDINGS -DDATA_DIR_PATH='"/crawl/"' -DSAVE_DIR_PATH='"/crawl-saves/"' -DCURSES_INCLUDE_FILE='"cw-wide.h"' -I"$S/contrib/lua/src" -I"$S/contrib/sqlite") n=0 for o in $objects; do ( cd "$S" && em++ "${FLAGS[@]}" -c "${o%.o}.cc" -o "$out/obj/$(echo "$o" | tr / _)" ) & n=$((n + 1)); if [ "$n" -ge 8 ]; then wait; n=0; fi done wait for o in $objects; do [ -f "$out/obj/$(echo "$o" | tr / _)" ] || { echo "Falhou: $o" >&2; exit 1; }; done # Lua e SQLite do contrib/ (em C), e a curses-web for f in "$S"/contrib/lua/src/*.c; do b="$(basename "$f" .c)"; [ "$b" = lua ] || [ "$b" = luac ] && continue emcc -O2 -w -ffile-prefix-map="$S/=" -DLUA_COMPAT_5_2 -c "$f" -o "$out/cobj/lua-$b.o" & done emcc -O2 -w -ffile-prefix-map="$S/=" -DSQLITE_THREADSAFE=0 -DSQLITE_OMIT_LOAD_EXTENSION \ -c "$S/contrib/sqlite/sqlite3.c" -o "$out/cobj/sqlite3.o" & emcc "${CW_CFLAGS[@]}" -ffile-prefix-map="$CW_DIR/=" -c "$CW_DIR/curses.c" -o "$out/cobj/curses.o" & wait # ---- Dados (o que o "make install-data" instala, só em inglês) ---- st="$out/stage" mkdir -p "$st/dat/clua" "$st/dat/descript" "$st/docs/develop/levels" "$st/settings" ( cd "$S" && cp -R dat/des dat/dlua dat/database dat/defaults dat/dist_bones "$st/dat/" \ && cp dat/clua/*.lua "$st/dat/clua/" && cp dat/descript/*.txt "$st/dat/descript/" \ && { echo "-- Autogenerated list of maps to load and compile:" find dat -name '*.des' | LC_ALL=C sort | sed s:dat/:: | while read -r x; do echo "dgn.load_des_file('$x')"; done } > "$st/dat/dlua/loadmaps.lua" \ && cp ../docs/*.txt ../docs/*.md "$st/docs/" && cp ../docs/develop/*.txt "$st/docs/develop/" \ && cp ../docs/develop/levels/*.txt "$st/docs/develop/levels/" && cp -R ../settings/. "$st/settings/" ) # Liga de dentro da pasta de saída, com caminhos relativos: o carregador do .data guarda o nome que a pasta # tem aqui. ( cd "$out" em++ -O2 -fexceptions $(LC_ALL=C ls obj/*.o cobj/*.o) -sUSE_ZLIB=1 "${CW_LDFLAGS[@]}" \ -sSTACK_SIZE=8MB -sASYNCIFY_STACK_SIZE=1048576 -sINITIAL_MEMORY=128MB \ --preload-file stage@/crawl -o crawl.js ) rm -rf "$out/work" "$out/obj" "$out/cobj" "$st" shasum -a 256 "$out/crawl.js" "$out/crawl.wasm" "$out/crawl.data"