Documentation of the haskell code for Defect Process, a 2d hack n' slash game on Steam. The full source code is available on GitHub.

[Brief Overview] | [Cross-platform Notes] | [Additional Links]

This page contains notes on the build process for cross-platform distribution, and other platform specific details. The source code of the game itself is identical across all platforms.
The per-platform notes described below are used for the production builds on Steam, the public GitHub repository uses a simpler configuration that works on all platforms but doesn't handle redistribution.

GHC defaults to using GMP which is licensed under LGPL. When statically linked this requires either releasing the full source or altering the build to avoid that situation in the first place, the latter approach is used here.

Windows

Dynamic linking is unsupported with GHC on Windows which means we have to use a version of GHC that swaps out GMP for integer-simple. See Haskell without GMP for instructions on how to specify this. Can ignore the part about manually compiling GHC, for recent releases Windows integer-simple binaries are provided (e.g. GHC 8.1.0.7). For GHC 9.x there's ghc-bignum which may simplify some of the steps.

Process Explorer is used to list out which DLLs the game executable uses, those .dll files are copied into the same directory as the .exe.

DPI awareness is enabled with:
mt.exe -manifest defect-process.exe.manifest -outputresource:defect-process.exe

The icon image is set with:
ResourceHacker.exe -open defect-process.exe -save defect-process.exe -action addoverwrite -res icon.ico -mask ICONGROUP,MAINICON,

Linux

This uses dynamic linking, which is enabled by adding -dynamic to ghc-options.
ldd defect-process lists the library files used, which are copied into the local ./lib if not already provided by the Steam Linux Runtime (e.g. SDL2 will be preinstalled).

Loading library files from the local ./lib directory is done by adding the following lines to package.yaml:
ld-options:
    - -Wl,-rpath=$ORIGIN/lib
and running patchelf --set-rpath '$$ORIGIN/lib' defect-process.

Mac

This uses dynamic linking, which is enabled by adding -dynamic to ghc-options.
otool -L defect-process lists the dylib files used, which are copied into the local ./lib if not already provided by the standard macOS environment (e.g. libSystem).

Loading dylib files from the local ./lib directory is done by running:
install_name_tool -add_rpath @executable_path/lib defect-process

For every lib*.dylib copied into ./lib run otool -L on that file to check for which dylib paths for that dylib itself need updating:
# Updating dylib paths for lib/libSDL2_image-2.0.0.dylib
install_name_tool -change /usr/lib/libz.1.dylib @executable_path/lib/libz.1.dylib lib/libSDL2_image-2.0.0.dylib
install_name_tool -change /usr/local/opt/jpeg/lib/libjpeg.9.dylib @executable_path/lib/libjpeg.9.dylib lib/libSDL2_image-2.0.0.dylib
...
The otool/install_name_tool steps need to be done recursively for every dependency/dependency of dependency/etc. Depending on the permissions of the .dylib file, install_name_tool may need to be run with sudo.

LC_ID_DYLIB needs to be set for the SDL2 libraries by running:
sudo install_name_tool -id @rpath/libSDL2-2.0.0.dylib lib/libSDL2-2.0.0.dylib
sudo install_name_tool -id @rpath/libSDL2_image-2.0.0.dylib lib/libSDL2_image-2.0.0.dylib
sudo install_name_tool -id @rpath/libSDL2_ttf-2.0.0.dylib lib/libSDL2_ttf-2.0.0.dylib
The CLI version of cabal-macosx is used to put the binary and assets into an application bundle. Copying Info.plist into the bundle enables DPI awareness and setting the icon image.

Steam requires app notarization, but this game appears to be grandfathered out of that requirement. This otherwise requires signing up for the Apple Developer Program and going through additional steps, see Releasing Steam Games on Mac Is a Monster Pain for instructions.