ちょーひさしぶりに技術メモ。
ついにGHC本体にJS backendが実装!!
ただしstackもnixもあまり対応していないので、cabalだけですべて行う必要あり。
stackに関してはまだ調整すればなんとかなるかも??
(もうcabalだけでよくないか?stackもnixもいるのかなあ、、)
WASM
# basic setup
ghcup config add-release-channel https://raw.githubusercontent.com/haskell/ghcup-metadata/develop/ghcup-cross-0.0.8.yaml
ghcup install cabal 3.12
git clone https://gitlab.haskell.org/ghc/ghc-wasm-meta.git
cd ghc-wasm-meta/
git checkout 18666510d272edaebdd27827b24bea563ebb6fb9
export SKIP_GHC=yes
./setup.sh
source ~/.ghc-wasm/env
ghcup install ghc --set wasm32-wasi-9.8.1 -- --host=x86_64-linux --with-intree-gmp --with-system-libffi
# build
echo 'main = putStrLn "hello world"' > hello.hs
wasm32-wasi-ghc hello.hs -o hello.wasm
wasmtime ./hello.wasm
GHCJS
# load channel
ghcup config add-release-channel https://raw.githubusercontent.com/haskell/ghcup-metadata/develop/ghcup-cross-0.0.8.yaml
ghcup install cabal 3.12
# install emsdk
git clone https://github.com/emscripten-core/emsdk.git
cd emsdk
source ./emsdk_env.sh # add to .bashrc
# specific set of versions for 9.10.0
emsdk install 3.1.57
emsdk activate 3.1.57
emconfigure ghcup install ghc --set javascript-unknown-ghcjs-9.10.0
# let's build
echo 'main = putStrLn "hello world"' > hello.hs
javascript-unknown-ghcjs-ghc -fforce-recomp hello.hs
./hello
### DO NOT use emconfigure with stack/cabal
# for cabal, works out of the box
cabal build --with-ghc javascript-unknown-ghcjs-ghc-9.10 --with-ghc-pkg javascript-unknown-ghcjs-ghc-pkg-9.10
# for stack, not working very well. also there is no stackage for 9.10
# Add following:
###############################################
configure-options:
$everything:
- --with-ld
- emcc
- --with-gcc
- emcc
- --with-ghc
- javascript-unknown-ghcjs-ghc-9.10.0
- --with-ghc-pkg
- javascript-unknown-ghcjs-ghc-pkg-9.10.0
###############################################
stack build
cabalはうまく行くけどstackがだめな感じだね、、、、、なんかversion mismatchでこける、、cabal.projectとかであれこれやれば大丈夫なのかなあ。。てか、nixでもstackでもなく、自作スクリプトとかでstackとかnix/flakeのデータベースをもとにcabal.projectを自動生成すれば万事解決な気がしてきた。ビルド失敗するたびに、cabal.projectにallow-newer書いていくとか。
ghcjsならふつうにTHも動くし申し分ない
たぶんjsaddleとかも大丈夫っぽい
早く対応してくれるといいのだが、、、
cabal build するときにemconfigureは使わないこと。アウトプットに謎の#!が挿入されだめになってしまう。
パッケージマネージャ作るんだったら、自分で頑張ってnix書くしか無いのかなあ、、、
環境作って、ghcを全部置き換えるだけだから簡単かなあ??よくわからないけど、、でもあれかー、cabalがやってたこと全部nixでやり直さないといけないからむずそう、、
nixでやるにしても、cabalを直接叩いたほうがいいのかなあ、、それってべつにnixじゃなくてよくね?ってなるけど、、いうてpackage.cabal有効活用すればreproductivityも担保できるのでは。
nixのあれこれ
まったくうまく動かない謎のやつ
nix-build "<nixpkgs>" -A pkgsCross.ghcjs.haskell.packages.ghc96.reflex-dom
キャッシュ用テンプレ
nix-build --option extra-binary-caches https://nixcache.reflex-frp.org --option binary-cache-public-keys "cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY= ryantrinkle.com-1:JJiAKaRv9mWgpVAz8dwewnZe0AzzEAzPkagE9SP5NWI="
なんかリストするやつ
nix-env -f "<nixpkgs>" -qaP -A haskell.compiler
ghc本体をビルドする環境をつくるためのコマンド。ただしプロジェクトのビルド機能はないっぽい
nix develop git+https://gitlab.haskell.org/ghc/ghc.nix#js-cross --extra-experimental-features nix-command --extra-experimental-features flakes
なんかghcjsのビルド環境自体は、まだnixファイルすら作られてないかんじだね、、
全部自作するしか無いのか、、
wasmのほうがghcjs よりも簡単そう。ghc-wasm-metaが用意されてるので。コンパイラじゃなくてアプリのほうをビルドできるようのnixファイルが用意されてる(ただwasmだとTHが使えないらしい?ので、、、、あとhaskellでもrustでも経験上、wasmは現段階だとなぜかロード時間がめっちゃ遅くて(でもEasyRPGとか、emscriptenのプロジェクトは結構速い)、ghcjsのほうがなんとなく速そう。)
nix shell gitlab:ghc/ghc-wasm-meta?host=gitlab.haskell.org --extra-experimental-features nix-command --extra-experimental-features flakes
cabal build --with-ghc=wasm32-wasi-ghc --with-ghc-pkg=wasm32-wasi-ghc-pkg
wasmはロードにめっちゃ時間かかる印象しか無い、、、
emscriptenも内部でwasm使ってるはずなんだけどね、
emscriptenが優秀なのかV8が優秀なのか、GHC JS backendが優秀なのか、けっこう爆速だよね
やっぱnix じゃなくてstackが一番かなあ、、、やろうとおもえばけっこう変えることは可能だし
参考
https://nixos.wiki/wiki/Haskell
やってるひとたち
Wasm build: https://github.com/tweag/ghc-wasm-reflex-examples(ちなみにここのcabal.projectを使ってwasmでなくghcjs版も普通にビルドできた。ロードも一瞬だしghcjsのほうが良さそう)
Reflex使いたい人は、こんな感じのやつを作ればok.
cabal.project
packages: .
with-compiler: javascript-unknown-ghcjs-ghc-9.10
with-hc-pkg: javascript-unknown-ghcjs-ghc-pkg-9.10
source-repository-package
type: git
location: https://github.com/amesgen/reflex-dom
tag: e43e0525d643f656a0a5b0f10e13e2a04712cd4e
subdir: reflex-dom-core reflex-dom
tmp.cabal
cabal-version: 3.0
-- The cabal-version field refers to the version of the .cabal specification,
-- and can be different from the cabal-install (the tool) version and the
-- Cabal (the library) version you are using. As such, the Cabal (the library)
-- version used must be equal or greater than the version stated in this field.
-- Starting from the specification version 2.2, the cabal-version field must be
-- the first thing in the cabal file.
-- Initial package description 'tmp' generated by
-- 'cabal init'. For further documentation, see:
-- http://haskell.org/cabal/users-guide/
--
-- The name of the package.
name: tmp
-- The package version.
-- See the Haskell package versioning policy (PVP) for standards
-- guiding when and how versions should be incremented.
-- https://pvp.haskell.org
-- PVP summary: +-+------- breaking API changes
-- | | +----- non-breaking API additions
-- | | | +--- code changes with no API change
version: 0.1.0.0
-- A short (one-line) description of the package.
-- synopsis:
-- A longer description of the package.
-- description:
-- The license under which the package is released.
license: BSD-3-Clause
-- The file containing the license text.
license-file: LICENSE
-- The package author(s).
author: hogehoge
-- An email address to which users can send suggestions, bug reports, and patches.
maintainer: fumufumu@example.com
-- A copyright notice.
-- copyright:
build-type: Simple
-- Extra doc files to be distributed with the package, such as a CHANGELOG or a README.
extra-doc-files: CHANGELOG.md
-- Extra source files to be distributed with the package, such as examples, or a tutorial module.
-- extra-source-files:
common warnings
ghc-options: -Wall
executable tmp
-- Import common warning flags.
import: warnings
-- .hs or .lhs file containing the Main module.
main-is: Main.hs
-- Modules included in this executable, other than Main.
-- other-modules:
-- LANGUAGE extensions used by modules in this package.
-- other-extensions:
-- Other library packages from which modules are imported.
build-depends: base ^>= 4.20.0.0,
template-haskell,
matrix,
string-qq,
ghcjs-dom,
reflex-dom
-- Directories containing source files.
hs-source-dirs: app
-- Base language which the package is written in.
default-language: Haskell2010
Reflex-dom/GHCJS-dom 9.10 有志によるCompatibility Patch (ghcjs-domは0.9.9.2ですでにマージされ修正済みっぽいが、、reflexのほうはまだみたい。): https://github.com/amesgen/reflex-dom https://github.com/ymeister/ghcjs-dom.git
(追記)Ormolu Live: https://github.com/tweag/ormolu/tree/master/ormolu-live
こちらもnixを使ってはいるものの、ビルド自体はcabal build
で行われていて、パッケージマネージャとしては使えていない模様、、、