@@ -21,7 +21,7 @@ let self =
2121 libffi ? null
2222
2323, # we don't need LLVM for x86, aarch64, or ghcjs
24- useLLVM ? with stdenv . targetPlatform ; ! ( isx86 || isAarch64 || isGhcjs )
24+ useLLVM ? with stdenv . targetPlatform ; ! ( isx86 || isAarch64 || isGhcjs || isWasm )
2525, # LLVM is conceptually a run-time-only dependency, but for
2626 # non-x86, we need LLVM to bootstrap later stages, so it becomes a
2727 # build-time dependency too.
@@ -39,7 +39,7 @@ let self =
3939
4040, # Whether to build dynamic libs for the standard library (on the target
4141 # platform). Static libs are always built.
42- enableShared ? ! haskell-nix . haskellLib . isCrossTarget && ! stdenv . targetPlatform . isStatic
42+ enableShared ? ! haskell-nix . haskellLib . isCrossTarget && ! stdenv . targetPlatform . isStatic || stdenv . targetPlatform . isWasm
4343
4444, enableLibraryProfiling ? true
4545
@@ -86,7 +86,16 @@ let self =
8686# extra values we want to have available as passthru values.
8787, extra-passthru ? { }
8888
89- , hadrianEvalPackages ? buildPackages
89+ # For running IFDs (used to evaluate build plans of tools involved in building GHC).
90+ #
91+ # Currently used for:
92+ # * hadrian
93+ # * libffi-wasm
94+ # * cabal (if we start using `cabal` to build GHC)
95+ #
96+ # We use this instead of `buildPackages` so that plan evaluation
97+ # can work on platforms other than the `buildPlatform`.
98+ , ghcEvalPackages ? buildPackages
9099} @args :
91100
92101assert ! ( enableIntegerSimple || enableNativeBignum ) -> gmp != null ;
104113 inherit ( haskell-nix . haskellLib ) isCrossTarget ;
105114
106115 ghc = if bootPkgs . ghc . isHaskellNixCompiler or false
107- then bootPkgs . ghc . override { inherit hadrianEvalPackages ; }
116+ then bootPkgs . ghc . override { inherit ghcEvalPackages ; }
108117 else bootPkgs . ghc ;
109118
110119 ghcHasNativeBignum = builtins . compareVersions ghc-version "9.0" >= 0 ;
@@ -119,14 +128,52 @@ let
119128 INTEGER_LIBRARY = ${ if enableIntegerSimple then "integer-simple" else "integer-gmp" }
120129 '' ;
121130
131+ nodejs = buildPackages . nodejs_24 ;
132+
133+ libffi-wasm = buildPackages . runCommand "libffi-wasm" {
134+ nativeBuildInputs = [
135+ ( buildPackages . haskell-nix . tool "ghc912" "libffi-wasm" {
136+ src = buildPackages . haskell-nix . sources . libffi-wasm ;
137+ evalPackages = ghcEvalPackages ;
138+ } )
139+ targetPackages . buildPackages . llvmPackages . clang
140+ targetPackages . buildPackages . llvmPackages . llvm
141+ targetPackages . buildPackages . binaryen
142+ ] ;
143+ outputs = [ "out" "dev" ] ;
144+ NIX_NO_SELF_RPATH = true ;
145+ } ''
146+ mkdir cbits
147+ cp ${ buildPackages . haskell-nix . sources . libffi-wasm } /cbits/* cbits/
148+ libffi-wasm
149+ wasm32-unknown-wasi-clang -Wall -Wextra -mcpu=mvp -Oz -DNDEBUG -Icbits -c cbits/ffi.c -o cbits/ffi.o
150+ wasm32-unknown-wasi-clang -Wall -Wextra -mcpu=mvp -Oz -DNDEBUG -Icbits -c cbits/ffi_call.c -o cbits/ffi_call.o
151+ wasm32-unknown-wasi-clang -Wall -Wextra -mcpu=mvp -Oz -DNDEBUG -Icbits -c cbits/ffi_closure.c -o cbits/ffi_closure.o
152+
153+ mkdir -p $dev/include
154+ cp cbits/*.h $dev/include
155+ mkdir -p $out/lib
156+ llvm-ar -r $out/lib/libffi.a cbits/*.o
157+
158+ wasm32-unknown-wasi-clang -Wall -Wextra -mcpu=mvp -Oz -DNDEBUG -Icbits -fPIC -fvisibility=default -shared -Wl,--keep-section=target_features,--strip-debug cbits/*.c -o libffi.so
159+ wasm-opt --low-memory-unused --converge --debuginfo --flatten --rereloop --gufa -O4 -Oz libffi.so -o $out/lib/libffi.so
160+ '' ;
161+
162+ lib-wasm = buildPackages . symlinkJoin {
163+ name = "lib-wasm" ;
164+ paths = [ targetPackages . wasilibc libffi-wasm ] ;
165+ } ;
166+
122167 # TODO check if this possible fix for segfaults works or not.
123168 targetLibffi =
124169 # on native platforms targetPlatform.{libffi, gmp} do not exist; thus fall back
125170 # to the non-targetPlatform version in those cases.
126171 let targetLibffi = targetPackages . libffi or libffi ; in
127172 # we need to set `dontDisableStatic` for musl for libffi to work.
128173 if stdenv . targetPlatform . isMusl
129- then targetLibffi . overrideAttrs ( _old : { dontDisableStatic = true ; } )
174+ then targetLibffi . overrideAttrs ( _old : { dontDisableStatic = true ; } )
175+ else if stdenv . targetPlatform . isWasm
176+ then libffi-wasm
130177 else targetLibffi ;
131178
132179 targetGmp = targetPackages . gmp or gmp ;
@@ -197,14 +244,16 @@ let
197244 # `--with` flags for libraries needed for RTS linker
198245 configureFlags = [
199246 "--datadir=$doc/share/doc/ghc"
200- ] ++ lib . optionals ( ! targetPlatform . isGhcjs && ! targetPlatform . isAndroid ) [ "--with-curses-includes=${ targetPackages . ncurses . dev } /include" "--with-curses-libraries=${ targetPackages . ncurses . out } /lib"
201- ] ++ lib . optionals ( targetLibffi != null && ! targetPlatform . isGhcjs ) [ "--with-system-libffi" "--with-ffi-includes=${ targetLibffi . dev } /include" "--with-ffi-libraries=${ targetLibffi . out } /lib"
202- ] ++ lib . optionals ( ! enableIntegerSimple && ! targetPlatform . isGhcjs ) [
203- "--with-gmp-includes=${ targetGmp . dev } /include" "--with-gmp-libraries=${ targetGmp . out } /lib"
247+ ] ++ lib . optionals ( ! targetPlatform . isGhcjs && ! targetPlatform . isWasm && ! targetPlatform . isAndroid ) [ "--with-curses-includes=${ lib . getDev targetPackages . ncurses } /include" "--with-curses-libraries=${ lib . getLib targetPackages . ncurses } /lib"
248+ ] ++ lib . optionals ( targetLibffi != null && ! targetPlatform . isGhcjs && ! targetPlatform . isWasm ) [ "--with-system-libffi" "--with-ffi-includes=${ lib . getDev targetLibffi } /include" "--with-ffi-libraries=${ lib . getLib targetLibffi } /lib"
249+ ] ++ lib . optionals ( targetPlatform . isWasm ) [
250+ "--with-system-libffi"
251+ ] ++ lib . optionals ( ! enableIntegerSimple && ! targetPlatform . isGhcjs && ! targetPlatform . isWasm ) [
252+ "--with-gmp-includes=${ lib . getDev targetGmp } /include" "--with-gmp-libraries=${ lib . getLib targetGmp } /lib"
204253 ] ++ lib . optionals ( targetPlatform == hostPlatform && hostPlatform . libc != "glibc" && ! targetPlatform . isWindows ) [
205- "--with-iconv-includes=${ libiconv } /include" "--with-iconv-libraries=${ libiconv } /lib"
206- ] ++ lib . optionals ( targetPlatform != hostPlatform && ! targetPlatform . isGhcjs ) [
207- "--with-iconv-includes=${ targetIconv } /include" "--with-iconv-libraries=${ targetIconv } /lib"
254+ "--with-iconv-includes=${ lib . getDev libiconv } /include" "--with-iconv-libraries=${ lib . getLib libiconv } /lib"
255+ ] ++ lib . optionals ( targetPlatform != hostPlatform && ! targetPlatform . isGhcjs && ! targetPlatform . isWasm ) [
256+ "--with-iconv-includes=${ lib . getDev targetIconv } /include" "--with-iconv-libraries=${ lib . getLib targetIconv } /lib"
208257 ] ++ lib . optionals ( targetPlatform != hostPlatform ) [
209258 "--enable-bootstrap-with-devel-snapshot"
210259 ] ++ lib . optionals ( disableLargeAddressSpace ) [
@@ -236,10 +285,10 @@ let
236285 ;
237286
238287 # Splicer will pull out correct variations
239- libDeps = platform : lib . optional ( enableTerminfo && ! targetPlatform . isGhcjs && ! targetPlatform . isAndroid ) [ targetPackages . ncurses targetPackages . ncurses . dev ]
288+ libDeps = platform : lib . optionals ( enableTerminfo && ! targetPlatform . isGhcjs && ! targetPlatform . isWasm && ! targetPlatform . isAndroid ) [ ( lib . getLib targetPackages . ncurses ) ( lib . getDev targetPackages . ncurses ) ]
240289 ++ lib . optional ( ! targetPlatform . isGhcjs ) targetLibffi
241- ++ lib . optional ( ! enableIntegerSimple && ! targetPlatform . isGhcjs ) gmp
242- ++ lib . optional ( platform . libc != "glibc" && ! targetPlatform . isWindows ) libiconv
290+ ++ lib . optional ( ! enableIntegerSimple && ! targetPlatform . isGhcjs && ! targetPlatform . isWasm ) gmp
291+ ++ lib . optional ( platform . libc != "glibc" && ! targetPlatform . isWindows && ! targetPlatform . isWasm ) libiconv
243292 ++ lib . optional ( enableNUMA && platform . isLinux && ! platform . isAarch32 && ! platform . isAndroid ) numactl
244293 ++ lib . optional enableDWARF ( lib . getLib elfutils ) ;
245294
276325 inherit compiler-nix-name ;
277326 name = "hadrian" ;
278327 compilerSelection = p : p . haskell . compiler ;
279- evalPackages = hadrianEvalPackages ;
328+ evalPackages = ghcEvalPackages ;
280329 modules = [ {
281330 reinstallableLibGhc = false ;
282331 # Apply the patches in a way that does not require using something
@@ -327,12 +376,12 @@ let
327376 # For build flavours and flavour transformers
328377 # see https://gitlab.haskell.org/ghc/ghc/blob/master/hadrian/doc/flavours.md
329378 hadrianArgs = "--flavour=${
330- ( if targetPlatform . isGhcjs then "quick" else "default" )
379+ ( if targetPlatform . isGhcjs || targetPlatform . isWasm then "quick" else "default" )
331380 + lib . optionalString ( ! enableShared ) "+no_dynamic_libs+no_dynamic_ghc"
332381 + lib . optionalString useLLVM "+llvm"
333382 + lib . optionalString enableDWARF "+debug_info"
334- + lib . optionalString ( ( enableNativeBignum && hadrianHasNativeBignumFlavour ) || targetPlatform . isGhcjs ) "+native_bignum"
335- + lib . optionalString targetPlatform . isGhcjs "+no_profiled_libs"
383+ + lib . optionalString ( ( enableNativeBignum && hadrianHasNativeBignumFlavour ) || targetPlatform . isGhcjs || targetPlatform . isWasm ) "+native_bignum"
384+ + lib . optionalString ( targetPlatform . isGhcjs || targetPlatform . isWasm ) "+no_profiled_libs"
336385 } --docs=no-sphinx -j --verbose"
337386 # This is needed to prevent $GCC from emitting out of line atomics.
338387 # Those would then result in __aarch64_ldadd1_sync and others being referenced, which
344393 + lib . optionalString ( ! hostPlatform . isAarch64 && targetPlatform . isLinux && targetPlatform . isAarch64 )
345394 " '*.rts.ghc.c.opts += -optc-mno-outline-atomics'"
346395 # PIC breaks GHC annotations on windows (see test/annotations for a test case)
347- + lib . optionalString ( enableRelocatedStaticLibs && ! targetPlatform . isWindows )
396+ + lib . optionalString ( enableRelocatedStaticLibs && ! targetPlatform . isWindows && ! targetPlatform . isWasm )
348397 " '*.*.ghc.*.opts += -fPIC' '*.*.cc.*.opts += -fPIC'"
398+ # C options for wasm
399+ + lib . optionalString targetPlatform . isWasm (
400+ " 'stage1.*.ghc.*.opts += -optc-Wno-error=int-conversion -optc-O3 -optc-mcpu=lime1 -optc-mreference-types -optc-msimd128 -optc-mtail-call -optc-DXXH_NO_XXH3'"
401+ + " 'stage1.*.ghc.cpp.opts += -optc-fno-exceptions'" )
349402 # `-fexternal-dynamic-refs` causes `undefined reference` errors when building GHC cross compiler for windows
350403 + lib . optionalString ( enableRelocatedStaticLibs && targetPlatform . isx86_64 && ! targetPlatform . isWindows )
351404 " '*.*.ghc.*.opts += -fexternal-dynamic-refs'"
@@ -450,9 +503,28 @@ haskell-nix.haskellLib.makeCompilerDeps (stdenv.mkDerivation (rec {
450503 fi
451504 mv config.sub.ghcjs config.sub
452505 '' )
506+ + lib . optionalString ( targetPlatform . isWasm ) ''
507+ export CC="${ targetCC } /bin/${ targetCC . targetPrefix } cc"
508+ export CXX="${ targetCC } /bin/${ targetCC . targetPrefix } c++"
509+ export LD="${ buildPackages . llvmPackages . lld } /bin/wasm-ld"
510+ export AS="${ targetCC . bintools . bintools } /bin/${ targetCC . bintools . targetPrefix } as"
511+ export AR="${ targetCC . bintools . bintools } /bin/${ targetCC . bintools . targetPrefix } ar"
512+ export NM="${ targetCC . bintools . bintools } /bin/${ targetCC . bintools . targetPrefix } nm"
513+ export RANLIB="${ targetCC . bintools . bintools } /bin/${ targetCC . bintools . targetPrefix } ranlib"
514+ export READELF="${ targetCC . bintools . bintools } /bin/${ targetCC . bintools . targetPrefix } readelf"
515+ export STRIP="${ bintoolsFor . strip } /bin/${ bintoolsFor . strip . targetPrefix } strip"
516+ export NIX_CFLAGS_COMPILE_FOR_BUILD+=" -I${ lib . getDev libffi } /include -L${ lib . getLib libffi } /lib"
517+ export NIX_CFLAGS_COMPILE_FOR_TARGET+=" -I${ lib . getDev targetLibffi } /include -L${ lib . getLib targetLibffi } /lib"
518+ ${ if ghc-version == "9.12.2"
519+ then ''
520+ substituteInPlace compiler/GHC.hs --replace-fail "panic \"corrupted wasi-sdk installation\"" "pure \"${ targetPackages . wasilibc } \""
521+ '' else ''
522+ substituteInPlace compiler/GHC.hs --replace-fail "last <\$> Loader.getGccSearchDirectory logger dflags \"libraries\"" "pure \"${ targetPackages . wasilibc } \""
523+ '' }
524+ ''
453525 # GHC is a bit confused on its cross terminology, as these would normally be
454526 # the *host* tools.
455- + lib . optionalString ( ! targetPlatform . isGhcjs ) ( ''
527+ + lib . optionalString ( ! targetPlatform . isGhcjs && ! targetPlatform . isWasm ) ( ''
456528 export CC="${ targetCC } /bin/${ targetCC . targetPrefix } cc"
457529 export CXX="${ targetCC } /bin/${ targetCC . targetPrefix } c++"
458530 ''
@@ -534,7 +606,34 @@ haskell-nix.haskellLib.makeCompilerDeps (stdenv.mkDerivation (rec {
534606 configurePlatforms = [ "build" "host" ] ++ lib . optional ( ! targetPlatform . isGhcjs ) "target" ;
535607
536608 enableParallelBuilding = true ;
537- postPatch = "patchShebangs ." ;
609+ postPatch = ''
610+ patchShebangs .
611+ '' + lib . optionalString ( targetPlatform . isWasm ) ''
612+ substituteInPlace utils/jsffi/dyld.mjs \
613+ --replace \
614+ "${ nodejs } /bin/node --disable-warning=ExperimentalWarning ${
615+ if builtins . compareVersions ghc-version "9.13" < 0
616+ then "--experimental-wasm-type-reflection"
617+ else "--max-old-space-size=65536" } --no-turbo-fast-api-calls --wasm-lazy-validation" \
618+ "${ buildPackages . writeShellScriptBin "node" ''
619+ SCRIPT=$1
620+ shift
621+ LIB_WASM=$1
622+ shift
623+ exec ${ nodejs } /bin/node \
624+ --disable-warning=ExperimentalWarning \
625+ ${
626+ if builtins . compareVersions ghc-version "9.13" < 0
627+ then "--experimental-wasm-type-reflection"
628+ else "--max-old-space-size=65536" } \
629+ --no-turbo-fast-api-calls \
630+ --wasm-lazy-validation \
631+ "$SCRIPT" \
632+ "${ lib-wasm } /lib" \
633+ "$@"
634+ ''
635+ } /bin/node"
636+ '' ;
538637
539638 outputs = [ "out" "doc" "generated" ] ;
540639
@@ -548,15 +647,18 @@ haskell-nix.haskellLib.makeCompilerDeps (stdenv.mkDerivation (rec {
548647 perl autoconf automake m4 python3 sphinx
549648 ghc bootPkgs . alex bootPkgs . happy bootPkgs . hscolour
550649 ] ++ lib . optional ( patches != [ ] ) autoreconfHook
551- ++ lib . optional useLdLld llvmPackages . bintools ;
650+ ++ lib . optional useLdLld llvmPackages . bintools
651+ ++ lib . optional ( targetPlatform . isWasm ) nodejs ;
552652
553653 # For building runtime libs
554654 depsBuildTarget = toolsForTarget ;
555655
556656 buildInputs = [ perl bash ] ++ ( libDeps hostPlatform ) ;
557657
558658 depsTargetTarget = lib . optionals ( ! targetPlatform . isGhcjs ) ( map lib . getDev ( libDeps targetPlatform ) ) ;
559- depsTargetTargetPropagated = lib . optionals ( ! targetPlatform . isGhcjs ) ( map ( lib . getOutput "out" ) ( libDeps targetPlatform ) ) ;
659+ depsTargetTargetPropagated = lib . optionals ( ! targetPlatform . isGhcjs ) ( map ( lib . getOutput "out" ) ( libDeps targetPlatform ) )
660+ # Needs to be propagated for `ffi.h`
661+ ++ lib . optional targetPlatform . isWasm ( lib . getDev targetLibffi ) ;
560662
561663 # required, because otherwise all symbols from HSffi.o are stripped, and
562664 # that in turn causes GHCi to abort
@@ -794,7 +896,7 @@ haskell-nix.haskellLib.makeCompilerDeps (stdenv.mkDerivation (rec {
794896 disableLargeAddressSpace = true ;
795897 } ) ;
796898 } // extra-passthru // {
797- buildGHC = extra-passthru . buildGHC . override { inherit hadrianEvalPackages ; } ;
899+ buildGHC = extra-passthru . buildGHC . override { inherit ghcEvalPackages ; } ;
798900 } ;
799901
800902 meta = {
0 commit comments