Skip to content
Snippets Groups Projects
Commit fb56966b authored by Armando Ramirez's avatar Armando Ramirez :mahjong:
Browse files

2023-06-17 haskell.nix

parent 166dd5f6
No related branches found
No related tags found
1 merge request!252023-06-17 haskell.nix
Showing
with 36 additions and 149 deletions
......@@ -17,6 +17,6 @@ If you are a Haskell gamedev interested in developing on Linux and releasing to
Feel free to cut an issue to this repository if you run into trouble. Even if your issue is upstream, I would love to help figure it out. This is bleeding edge tech after all!
If you want to contribute, just trying `macaroni.nix` out with your Haskell game is a good start. Trying to get it working with other gamedev libraries (some notable ones are Vulkan, raylib, and GPipe) and figuring out what overlays and `haskell.nix` changes we need is even better - we want as broad of support as we can get.
If you want to contribute, just trying `macaroni.nix` out with your Haskell game is a good start. Trying to get it working with other gamedev libraries (some notable ones are Vulkan, sokol, and GPipe) and figuring out what overlays and `haskell.nix` changes we need is even better - we want as broad of support as we can get.
[`niv` has pretty good support for using local sources for testing stuff out.](https://github.com/nmattia/niv#can-i-use-local-packages) And you can easily [override the pinned `haskell.nix` and `hackage.nix` when you import `macaroni.nix`.](https://gitlab.com/macaroni.dev/macaroni.nix/-/blob/master/default.nix#L2-3) If you run into trouble, feel free to cut an issue for the errors your game is hitting and we can work together to improve `macaroni.nix` for everyone.
......@@ -4,6 +4,6 @@
`macaroni.nix` pins `haskell.nix` and `hackage.nix` itself. This is mostly because cross-compilation is often broken as upstream dependencies change. If you wish, you can override them by passing `haskellNixSrc` / `hackageNixSrc` when importing `macaroni.nix`.
It would be good to upstream the changes we make in our overlays, but they are so small that it doesn't feel like there's a big rush.
It would be good to upstream the changes we make in our overlays to `nixpkgs`, but for now they're staying in-tree. The spirit of `macaroni.nix` is that it's the frontline for Haskell gamedev x-compilation in a world of changing GHCs, `haskell.nix`, and `nixpkgs`. Because those projects don't focus on Haskell gamedev, they sometimes introduce issues for it. Whereas `macaroni.nix`'s primary goal is the gamedev.
While `macaroni.nix` is focused on Windows x-compilation today, the long-term goal is to support all gamedev target platforms: Windows, Mac, Linux, iOS, Android, ARM (RPi), JS/Wasm, even consoles. In an ideal world, a Haskell gamedev will effortlessly release their game to all platforms simply by building different Nix targets. Focus on building your game - not your build!
......@@ -31,7 +31,7 @@ in pkgs.haskell-nix.cabalProject { # macaroni.nix is currently only tested with
src = ./.;
};
# Specify the GHC version to use.
compiler-nix-name = "ghc925"; # macaroni.nix is currently only tested on GHC 9.2.5
compiler-nix-name = "ghc928"; # macaroni.nix is currently only tested on GHC 9.2.8
# This is necessary for x-compilation
# See https://github.com/input-output-hk/haskell.nix/issues/1666
......
......@@ -16,7 +16,7 @@ The following native libraries have been properly packaged for Windows:
## Haskell
`macaroni.nix` has only been tested with GHC 9.2.4, although it should work more or less with others. **NOTE: On 9.2.4, you will want to set `exactDeps = false` in your `shell.nix` if you are using `source-repository-package`s due to [this upstream issue](
`macaroni.nix` has only been tested with GHC 9.2.8, although it should work more or less with others. **NOTE: On 9.2.8, you will want to set `exactDeps = false` in your `shell.nix` if you are using `source-repository-package`s due to [this upstream issue](
https://github.com/input-output-hk/haskell.nix/issues/1608).**
As a rule, pure Haskell libraries have no trouble cross-compiling.
......@@ -24,6 +24,7 @@ As a rule, pure Haskell libraries have no trouble cross-compiling.
The following Haskell gamedev libraries have been cross-compiled successfully with `macaroni.nix`:
- [`sdl2`](https://hackage.haskell.org/package/sdl2)
- [`sdl2-ttf`)(https://hackage.haskell.org/package/sdl2-ttf)
- [`OpenAL`](https://hackage.haskell.org/package/OpenAL) and [`ALUT`](https://hackage.haskell.org/package/ALUT)
- [`gloss`](https://hackage.haskell.org/package/gloss)
- And transitively [`GLUT`](https://hackage.haskell.org/package/GLUT) and [`OpenGL`](https://hackage.haskell.org/package/OpenGL)
......@@ -58,17 +59,9 @@ source-repository-package
```
The following Haskell libraries do **not** currently work:
- [`sdl2-ttf`](https://hackage.haskell.org/package/sdl2-ttf) - The mingw build will hang during Template Haskell.
## Known Issues
You can browse [the `support` label](https://gitlab.com/macaroni.dev/macaroni.nix/-/issues/?label_name%5B%5D=support) for a complete list of known issues. The most like issues you'll run into are:
You can browse [the `support` label](https://gitlab.com/macaroni.dev/macaroni.nix/-/issues/?label_name%5B%5D=support) for a complete list of known issues. The most likely issues you'll run into are:
- Template Haskell that requires DLL loading will hang indefinitely. [[Upstream Issue]](https://github.com/input-output-hk/haskell.nix/issues/1098)
- There is a hacky workaround that will allow you to use indiscriminately TH in [`examples/sdl2-th-splicing`](examples/sdl2-th-splicing)
- The hack will work okay for your game, but for libraries (notably `sdl2-ttf`), there isn't an easy fix.
- It does seem to be a Windows-specific issue (due to Windows vs Posix path handling).
- `hsc2hs` is extremely slow when cross-compiling. [[Upstream Issue]](https://github.com/input-output-hk/haskell.nix/issues/36)
- Not all native libraries get their DLLs automatically bundled. Notably, `zlib` and `SDL2_ttf`.
......@@ -7,6 +7,6 @@ in pkgs.haskell-nix.project {
subDir = "examples/ALUT-minimal";
};
compiler-nix-name = "ghc925";
compiler-nix-name = "ghc928";
evalPackages = pkgs;
}
......@@ -7,6 +7,6 @@ in pkgs.haskell-nix.project {
subDir = "examples/gloss-minimal";
};
compiler-nix-name = "ghc925";
compiler-nix-name = "ghc928";
evalPackages = pkgs;
}
......@@ -9,7 +9,7 @@ main :: IO ()
main
= display
(InWindow
"Hello World" -- window title
"gloss-minimal" -- window title
(400, 150) -- window size
(10, 10)) -- window position
white -- background color
......@@ -19,4 +19,4 @@ picture :: Picture
picture
= Translate (-170) (-20) -- shift the text to the middle of the window
$ Scale 0.5 0.5 -- display it half the original size
$ Text "Hello World" -- text to display
$ Text "Hello World!" -- text to display
......@@ -7,6 +7,6 @@ in pkgs.haskell-nix.cabalProject {
subDir = "examples/h-raylib-minimal";
};
compiler-nix-name = "ghc925";
compiler-nix-name = "ghc928";
evalPackages = pkgs;
}
......@@ -7,6 +7,6 @@ in pkgs.haskell-nix.cabalProject {
subDir = "examples/sdl-gpu-minimal";
};
compiler-nix-name = "ghc925";
compiler-nix-name = "ghc928";
evalPackages = pkgs;
}
......@@ -7,6 +7,6 @@ in pkgs.haskell-nix.project {
subDir = "examples/sdl2-minimal";
};
compiler-nix-name = "ghc925";
compiler-nix-name = "ghc928";
evalPackages = pkgs;
}
......@@ -7,6 +7,6 @@ in pkgs.haskell-nix.project {
subDir = "examples/sdl2-pkgconfig-minimal";
};
compiler-nix-name = "ghc925";
compiler-nix-name = "ghc928";
evalPackages = pkgs;
}
build:
nix-build -A sdl2-th-splicing.components.exes.sdl2-th-splicing
.PHONY: build
windows:
git diff --quiet src
nix-shell --run "cabal clean"
nix-shell --run "cabal build --ghc-options='-ddump-splices -ddump-to-file'"
nix-shell --run "./splice-for-windows.sh"
nix-build -A projectCross.mingwW64.hsPkgs.sdl2-th-splicing.components.exes.sdl2-th-splicing || git checkout -- src
git checkout -- src
.PHONY: windows
windows-run: windows
nix-shell -p wineWowPackages.stable --run 'WINE_PREFIX=~/.macaroni.nix.wine64 wine64 ./result/bin/sdl2-th-splicing.exe'
.PHONY: windows-run
# TH Splicing
Because TH will not cross-compile to Windows when native libraries are imported (a common situation in gamedev), we need to use a workaround. This is a minimal example of the workaround that we have been using.
The workaround has some moving parts:
We adopt a convention for Template Haskell: Place all TH calls below a comment of `-- TH_CODE`. This will flag to the script where to put your generated code:
```haskell
-- TH_CODE
makePrisms ''SDL.EventPayload
makeLensesWith rules ''SDL.Event
makeLensesWith rules ''SDL.KeyboardEventData
```
You may need to add extra imports - any type or function referenced by the generated code must be imported for this to work. If you forget one, the splices will include the fully-qualified name and your build will fail to find it. An example error is:
```
src/SDLOptics.hs:591:54: error:
Not in scope: type constructor or class ‘GHC.Int.Int32’
No module named ‘GHC.Int’ is imported.
|
591 | _MouseWheelEventPos :: Lens' MouseWheelEventData (V2 GHC.Int.Int32)
```
The fix to this would be to `import Data.Int`. I find it useful to create a module that re-exports common TH imports and import it wherever I use TH.
The `splice-for-windows.sh` script does all the heavy lifting. It detects which modules use Template Haskell, finds the corresponding dumped splices, and uses bash scripting to substitute them in. Note that it has some dependencies itself (`rg` and `ghc` must be in scope).
The provided `Makefile` shows how to use this script:
```
# clean to ensure cabal generates your splices
cabal clean
# Build with additional options to dump splices
cabal build --ghc-options='-ddump-splices -ddump-to-file'
# Run the splicing script
./splice-for-windows.sh
# Now you can build for Windows! Note that your git repo will be dirty due to splice-for-windows.sh
nix-build -A projectCross.mingwW64.hsPkgs.sdl2-th-splicing.components.exes.sdl2-th-splicing
```
If you want to build and run this example yourself:
```
# You must be in a nix-shell!
$ make windows
$ nix-shell -p wineWowPackages.stable --run 'WINE_PREFIX=~/.wine64 wine64 ./result/bin/sdl2-th-splicing.exe
```
## Caveats
**NOTE: It is very easy to accidentally clobber your code and check the splices into git. I recommend writing some automation of your own around the Windows splicing & release process with `bash` and `make`. For instance, ensure your git repo is in a clean state with `git diff --quiet`. And `git checkout --` after the build succeeds.**
This hack runs TH on the host platform (e.g. Linux) and not the target (e.g. Windows). For many libraries (`optics`, `apecs`, etc), this shouldn't matter. But it does technically open you up to issues if the generated code depends on a characteristic of your target platform.
#! /usr/bin/env bash
#
set -e
readarray -t mods <<< $(rg TH_CODE --files-with-matches --type hs | cut -d. -f1)
echo "Found TH_CODE modules: = ${mods[@]}"
ghc_version=$(ghc --numeric-version)
for p in ${mods[@]}; do
# NOTE: This hardcodes ghc version
raw_splices=$(cat ./dist-newstyle/build/x86_64-linux/ghc-$ghc_version/sdl2-th-splicing-0.1.0.0/x/sdl2-th-splicing/build/sdl2-th-splicing/sdl2-th-splicing-tmp/$p.dump-splices)
# Remove the annotation between "Splicing" and the arrow
# NOTE: You gotta quote $raw_splices here due to newlines
splices=$(echo "$raw_splices" | sed -e '/Splicing/,/====>/d' | cut -c 5-)
# Remove the TemplateHaskell flag so we don't run it
sed -i '/LANGUAGE TemplateHaskell/d' $p.hs
# TH_CODE indicate that the rest of the file is TH and meant to be
# replaced with splices
sed -i '/TH_CODE/q' $p.hs
# Again, gotta quote $splices here
echo "$splices" >> $p.hs
done
-- To workaround TH x-compilation issues via splicing, we sometimes
-- need some modules in scope that we don't need otherwise
--
-- This module provides a bunch of optics imports. You may need more depending
-- on what your TH is doing. The sign that you need more imports will be missing
-- imports of fully-qualified references. For instance:
--
-- src/SDLOptics.hs:591:54: error:
-- Not in scope: type constructor or class ‘GHC.Int.Int32’
-- No module named ‘GHC.Int’ is imported.
-- |
-- 591 | _MouseWheelEventPos :: Lens' MouseWheelEventData (V2 GHC.Int.Int32)
module SDLOptics.Import (module X) where
import Optics.Iso as X
import Optics.Label as X
import Optics.Internal.Optic.Types as X
import Optics.Internal.Magic as X
import Optics.Lens as X
import Optics.Prism as X
import Optics.AffineTraversal as X
build:
nix-build -A sdl2-th.components.exes.sdl2-th
.PHONY: build
windows:
nix-build -A projectCross.mingwW64.hsPkgs.sdl2-th.components.exes.sdl2-th
.PHONY: windows
windows-run: windows
nix-shell -p wineWowPackages.stable --run 'WINE_PREFIX=~/.macaroni.nix.wine64 wine64 ./result/bin/sdl2-th.exe'
.PHONY: windows-run
# TH
Template Haskell used to not work when x-compiled to Windows. The iserv process would hang. It was possible to work around it with a very hacky bash script that used TH splices.
[It recently got fixed.](https://github.com/input-output-hk/haskell.nix/pull/1974) This example hangs around as a sanity check that TH continues to work.
......@@ -2,11 +2,11 @@ let
pkgs = import ./../.. {};
in pkgs.haskell-nix.project {
src = pkgs.haskell-nix.haskellLib.cleanGit {
name = "sdl2-th-splicing";
name = "sdl2-th";
src = ./../..;
subDir = "examples/sdl2-th-splicing";
subDir = "examples/sdl2-th";
};
compiler-nix-name = "ghc925";
compiler-nix-name = "ghc928";
evalPackages = pkgs;
}
cabal-version: 2.2
name: sdl2-th-splicing
name: sdl2-th
version: 0.1.0.0
author: Armando Ramirez
maintainer: armando.m.ramirez@gmail.com
build-type: Simple
executable sdl2-th-splicing
executable sdl2-th
hs-source-dirs: src
main-is: Main.hs
ghc-options: -threaded -rtsopts -with-rtsopts=-N -Wall
other-modules:
SDLOptics
SDLOptics.Rules
SDLOptics.Import
build-depends: base
, sdl2
, text
......
File moved
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment