@@ -21,7 +21,7 @@ let self =
21
21
libffi ? null
22
22
23
23
, # 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 )
25
25
, # LLVM is conceptually a run-time-only dependency, but for
26
26
# non-x86, we need LLVM to bootstrap later stages, so it becomes a
27
27
# build-time dependency too.
@@ -39,7 +39,7 @@ let self =
39
39
40
40
, # Whether to build dynamic libs for the standard library (on the target
41
41
# 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
43
43
44
44
, enableLibraryProfiling ? true
45
45
@@ -86,7 +86,16 @@ let self =
86
86
# extra values we want to have available as passthru values.
87
87
, extra-passthru ? { }
88
88
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
90
99
} @args :
91
100
92
101
assert ! ( enableIntegerSimple || enableNativeBignum ) -> gmp != null ;
104
113
inherit ( haskell-nix . haskellLib ) isCrossTarget ;
105
114
106
115
ghc = if bootPkgs . ghc . isHaskellNixCompiler or false
107
- then bootPkgs . ghc . override { inherit hadrianEvalPackages ; }
116
+ then bootPkgs . ghc . override { inherit ghcEvalPackages ; }
108
117
else bootPkgs . ghc ;
109
118
110
119
ghcHasNativeBignum = builtins . compareVersions ghc-version "9.0" >= 0 ;
@@ -119,14 +128,52 @@ let
119
128
INTEGER_LIBRARY = ${ if enableIntegerSimple then "integer-simple" else "integer-gmp" }
120
129
'' ;
121
130
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
+
122
167
# TODO check if this possible fix for segfaults works or not.
123
168
targetLibffi =
124
169
# on native platforms targetPlatform.{libffi, gmp} do not exist; thus fall back
125
170
# to the non-targetPlatform version in those cases.
126
171
let targetLibffi = targetPackages . libffi or libffi ; in
127
172
# we need to set `dontDisableStatic` for musl for libffi to work.
128
173
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
130
177
else targetLibffi ;
131
178
132
179
targetGmp = targetPackages . gmp or gmp ;
@@ -197,14 +244,16 @@ let
197
244
# `--with` flags for libraries needed for RTS linker
198
245
configureFlags = [
199
246
"--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"
204
253
] ++ 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"
208
257
] ++ lib . optionals ( targetPlatform != hostPlatform ) [
209
258
"--enable-bootstrap-with-devel-snapshot"
210
259
] ++ lib . optionals ( disableLargeAddressSpace ) [
@@ -236,10 +285,10 @@ let
236
285
;
237
286
238
287
# 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 ) ]
240
289
++ 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
243
292
++ lib . optional ( enableNUMA && platform . isLinux && ! platform . isAarch32 && ! platform . isAndroid ) numactl
244
293
++ lib . optional enableDWARF ( lib . getLib elfutils ) ;
245
294
276
325
inherit compiler-nix-name ;
277
326
name = "hadrian" ;
278
327
compilerSelection = p : p . haskell . compiler ;
279
- evalPackages = hadrianEvalPackages ;
328
+ evalPackages = ghcEvalPackages ;
280
329
modules = [ {
281
330
reinstallableLibGhc = false ;
282
331
# Apply the patches in a way that does not require using something
@@ -327,12 +376,12 @@ let
327
376
# For build flavours and flavour transformers
328
377
# see https://gitlab.haskell.org/ghc/ghc/blob/master/hadrian/doc/flavours.md
329
378
hadrianArgs = "--flavour=${
330
- ( if targetPlatform . isGhcjs then "quick" else "default" )
379
+ ( if targetPlatform . isGhcjs || targetPlatform . isWasm then "quick" else "default" )
331
380
+ lib . optionalString ( ! enableShared ) "+no_dynamic_libs+no_dynamic_ghc"
332
381
+ lib . optionalString useLLVM "+llvm"
333
382
+ 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"
336
385
} --docs=no-sphinx -j --verbose"
337
386
# This is needed to prevent $GCC from emitting out of line atomics.
338
387
# Those would then result in __aarch64_ldadd1_sync and others being referenced, which
344
393
+ lib . optionalString ( ! hostPlatform . isAarch64 && targetPlatform . isLinux && targetPlatform . isAarch64 )
345
394
" '*.rts.ghc.c.opts += -optc-mno-outline-atomics'"
346
395
# 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 )
348
397
" '*.*.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'" )
349
402
# `-fexternal-dynamic-refs` causes `undefined reference` errors when building GHC cross compiler for windows
350
403
+ lib . optionalString ( enableRelocatedStaticLibs && targetPlatform . isx86_64 && ! targetPlatform . isWindows )
351
404
" '*.*.ghc.*.opts += -fexternal-dynamic-refs'"
@@ -450,9 +503,28 @@ haskell-nix.haskellLib.makeCompilerDeps (stdenv.mkDerivation (rec {
450
503
fi
451
504
mv config.sub.ghcjs config.sub
452
505
'' )
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
+ ''
453
525
# GHC is a bit confused on its cross terminology, as these would normally be
454
526
# the *host* tools.
455
- + lib . optionalString ( ! targetPlatform . isGhcjs ) ( ''
527
+ + lib . optionalString ( ! targetPlatform . isGhcjs && ! targetPlatform . isWasm ) ( ''
456
528
export CC="${ targetCC } /bin/${ targetCC . targetPrefix } cc"
457
529
export CXX="${ targetCC } /bin/${ targetCC . targetPrefix } c++"
458
530
''
@@ -534,7 +606,34 @@ haskell-nix.haskellLib.makeCompilerDeps (stdenv.mkDerivation (rec {
534
606
configurePlatforms = [ "build" "host" ] ++ lib . optional ( ! targetPlatform . isGhcjs ) "target" ;
535
607
536
608
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
+ '' ;
538
637
539
638
outputs = [ "out" "doc" "generated" ] ;
540
639
@@ -548,15 +647,18 @@ haskell-nix.haskellLib.makeCompilerDeps (stdenv.mkDerivation (rec {
548
647
perl autoconf automake m4 python3 sphinx
549
648
ghc bootPkgs . alex bootPkgs . happy bootPkgs . hscolour
550
649
] ++ lib . optional ( patches != [ ] ) autoreconfHook
551
- ++ lib . optional useLdLld llvmPackages . bintools ;
650
+ ++ lib . optional useLdLld llvmPackages . bintools
651
+ ++ lib . optional ( targetPlatform . isWasm ) nodejs ;
552
652
553
653
# For building runtime libs
554
654
depsBuildTarget = toolsForTarget ;
555
655
556
656
buildInputs = [ perl bash ] ++ ( libDeps hostPlatform ) ;
557
657
558
658
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 ) ;
560
662
561
663
# required, because otherwise all symbols from HSffi.o are stripped, and
562
664
# that in turn causes GHCi to abort
@@ -794,7 +896,7 @@ haskell-nix.haskellLib.makeCompilerDeps (stdenv.mkDerivation (rec {
794
896
disableLargeAddressSpace = true ;
795
897
} ) ;
796
898
} // extra-passthru // {
797
- buildGHC = extra-passthru . buildGHC . override { inherit hadrianEvalPackages ; } ;
899
+ buildGHC = extra-passthru . buildGHC . override { inherit ghcEvalPackages ; } ;
798
900
} ;
799
901
800
902
meta = {
0 commit comments