diff --git a/pkgs/development/compilers/ghc/common-hadrian.nix b/pkgs/development/compilers/ghc/common-hadrian.nix index e32880fb02b7c..3e95275520c3a 100644 --- a/pkgs/development/compilers/ghc/common-hadrian.nix +++ b/pkgs/development/compilers/ghc/common-hadrian.nix @@ -340,7 +340,14 @@ let ] ++ lib.optionals targetPlatform.useAndroidPrebuilt [ "*.*.ghc.c.opts += -optc-std=gnu99" - ]; + ] + # Inform GHC that we can't load dynamic libraries which forces iserv-proxy to load static libraries + ++ + lib.optionals + (targetPlatform.isAndroid || (targetPlatform.isMusl && hostPlatform != targetPlatform)) + [ + "*.ghc.cabal.configure.opts += --flags=-dynamic-system-linker" + ]; # Splicer will pull out correct variations libDeps = @@ -632,7 +639,7 @@ stdenv.mkDerivation ( "--with-ffi-includes=${targetLibs.libffi.dev}/include" "--with-ffi-libraries=${targetLibs.libffi.out}/lib" ] - ++ lib.optionals (targetPlatform == hostPlatform && !enableNativeBignum) [ + ++ lib.optionals (!enableNativeBignum) [ "--with-gmp-includes=${targetLibs.gmp.dev}/include" "--with-gmp-libraries=${targetLibs.gmp.out}/lib" ] diff --git a/pkgs/development/haskell-modules/configuration-cross.nix b/pkgs/development/haskell-modules/configuration-cross.nix new file mode 100644 index 0000000000000..8226fcc03d7b0 --- /dev/null +++ b/pkgs/development/haskell-modules/configuration-cross.nix @@ -0,0 +1,84 @@ +{ pkgs, haskellLib }: + +let + inherit (pkgs) lib; +in + +with haskellLib; + +self: super: { + # Avoids a cycle by disabling use of the external interpreter by the packages that are dependencies of iserv-proxy + # These in particular can't rely on template haskell for cross-compilation anyway as they can't rely on iserv-proxy + inherit + ( + let + breakCycle = overrideCabal { + doCheck = false; # test packages themselves like to rely on TH for discovering test cases + enableExternalInterpreter = false; + }; + in + lib.mapAttrs (_: breakCycle) { inherit (super) iserv-proxy libiserv network; } + ) + iserv-proxy + libiserv + network + ; + + # qemu: uncaught target signal 4 (Illegal instruction) - core dumped + th-expand-syns = dontCheck super.th-expand-syns; + + # test/Unicode/CharSpec.hs:206:21: + # 1) Unicode.Char.Case toUpper + # predicate failed on: '\411' + unicode-data = dontCheck super.unicode-data; + + # syntax error: unexpected word (expecting ")") + jsaddle-warp = dontCheck super.jsaddle-warp; + + # test-pandoc: line 0: syntax error: unterminated quoted string + pandoc = dontCheck super.pandoc; + + # Tests take a long time or maybe hang + bitvec = dontCheck super.bitvec; + crypton = dontCheck super.crypton; + crypton-x509-validation = dontCheck super.crypton-x509-validation; + tls = dontCheck super.tls; + + # Test suite rootCleanup: FAIL + reflex = dontCheck super.reflex; + + # Couldn't find a target code interpreter. Try with -fexternal-interpreter + bsb-http-chunked = dontCheck super.bsb-http-chunked; + doctest-parallel = dontCheck super.doctest-parallel; + foldl = dontCheck super.foldl; + + # https://gitlab.haskell.org/ghc/ghc/-/issues/14335 + # : error: Plugins require -fno-external-interpreter + infer-license = dontCheck super.infer-license; + inspection-testing = dontCheck super.inspection-testing; + large-records = dontCheck super.large-records; + vector = dontCheck super.vector; + + # could not execute: hspec-discover + here = dontCheck super.here; + hspec-wai = dontCheck super.hspec-wai; + http-date = dontCheck super.http-date; + http-types = dontCheck super.http-types; + unliftio = dontCheck super.unliftio; + word8 = dontCheck super.word8; + yaml = dontCheck super.yaml; + + # posix_spawnp: invalid argument (Exec format error) + file-lock = dontCheck super.file-lock; + safe-exceptions = dontCheck super.safe-exceptions; + warp = dontCheck super.warp; + zip-archive = dontCheck super.zip-archive; + + # Mmap.hsc: In function ‘_hsc2hs_test13’: + # Mmap.hsc:54:20: error: storage size of ‘test_array’ isn’t constant + hashable = dontCheck super.hashable; + + # hGetLine: end of file + doctest = dontCheck super.doctest; + xml-conduit = dontCheck super.xml-conduit; +} diff --git a/pkgs/development/haskell-modules/configuration-ghc-9.6.x.nix b/pkgs/development/haskell-modules/configuration-ghc-9.6.x.nix index 41b81b6ba15f0..87ae4253eb29c 100644 --- a/pkgs/development/haskell-modules/configuration-ghc-9.6.x.nix +++ b/pkgs/development/haskell-modules/configuration-ghc-9.6.x.nix @@ -38,7 +38,6 @@ in haskeline = null; hpc = null; integer-gmp = null; - libiserv = null; mtl = null; parsec = null; pretty = null; @@ -60,6 +59,11 @@ in xhtml = null; Win32 = null; + # Was only ever released for a few exact versions of ghc + libiserv = unmarkBroken (doJailbreak super.libiserv); + + iserv-proxy = addBuildDepends [ self.libiserv ] super.iserv-proxy; + # Becomes a core package in GHC >= 9.8 semaphore-compat = doDistribute self.semaphore-compat_1_0_0; diff --git a/pkgs/development/haskell-modules/configuration-ghcjs-9.x.nix b/pkgs/development/haskell-modules/configuration-ghcjs-9.x.nix index 3d2268de643f1..c6b0462ccc6a6 100644 --- a/pkgs/development/haskell-modules/configuration-ghcjs-9.x.nix +++ b/pkgs/development/haskell-modules/configuration-ghcjs-9.x.nix @@ -7,18 +7,29 @@ in with haskellLib; # cabal2nix doesn't properly add dependencies conditional on arch(javascript) +self: super: { -(self: super: { - ghcjs-base = addBuildDepends (with self; [ - aeson - attoparsec - dlist - hashable - primitive - scientific - unordered-containers - vector - ]) super.ghcjs-base; + ghcjs-base = lib.pipe super.ghcjs-base [ + dontCheck # : does not exist: test/compat.js + (addBuildDepends ( + with self; + [ + HUnit + aeson + attoparsec + dlist + hashable + primitive + quickcheck-unicode + scientific + test-framework + test-framework-hunit + test-framework-quickcheck2 + unordered-containers + vector + ] + )) + ]; ghcjs-dom = addBuildDepend self.ghcjs-dom-javascript super.ghcjs-dom; ghcjs-dom-javascript = addBuildDepend self.ghcjs-base super.ghcjs-dom-javascript; @@ -67,4 +78,63 @@ with haskellLib; # See https://gitlab.haskell.org/ghc/ghc/-/issues/26019#note_621324, without this flag the build OOMs SHA = haskellLib.appendConfigureFlag "--ghc-option=-fignore-interface-pragmas" super.SHA; -}) + + # Tests are extremely slow + scientific = dontCheck super.scientific; + + # Tests hang + logict = dontCheck super.logict; + + # error: no C compiler provided for this platform + servant-lucid = dontCheck super.servant-lucid; + + # javascript-unknown-ghcjs-ghc-9.12.2: could not execute: hspec-discover + http-types = dontCheck super.http-types; + + # *** Failed! Exception: 'createPipeInternal: unsupported operation (Operation is not supported)' (after 1 test): + QuickCheck = dontCheck super.QuickCheck; + + # SyntaxError: Invalid or unexpected token + syb = dontCheck super.syb; + + # uncaught exception in Haskell thread: ReferenceError: h$XXH3_64bits_withSeed is not defined + hashable = dontCheck super.hashable; + + # uncaught exception in Haskell thread: ReferenceError: h$hs_text_short_is_valid_utf8 is not defined + text-short = dontCheck super.text-short; + + # uncaught exception in Haskell main thread: ReferenceError: h$clock_getres is not defined + time-compat = dontCheck super.time-compat; + + # uncaught exception in Haskell main thread: ReferenceError: h$getNumberOfProcessors is not defined + hedgehog = dontCheck super.hedgehog; + + # uncaught exception in Haskell main thread: ReferenceError: h$readlink is not defined + adjunctions = dontCheck super.adjunctions; + aeson-gadt-th = dontCheck super.aeson-gadt-th; + aeson-qq = dontCheck super.aeson-qq; + base-orphans = dontCheck super.base-orphans; + bifunctors = dontCheck super.bifunctors; + constraints = dontCheck super.constraints; + distributive = dontCheck super.distributive; + doctest = dontCheck super.doctest; + generic-deriving = dontCheck super.generic-deriving; + hspec-discover = dontCheck super.hspec-discover; + hspec-hedgehog = dontCheck super.hspec-hedgehog; + http-api-data = dontCheck super.http-api-data; + invariant = dontCheck super.invariant; + lucid = dontCheck super.lucid; + markdown-unlit = dontCheck super.markdown-unlit; + newtype-generics = dontCheck super.newtype-generics; + records-sop = dontCheck super.records-sop; + reflection = dontCheck super.reflection; + reflex = dontCheck super.reflex; + resourcet = dontCheck super.resourcet; + safe-exceptions = dontCheck super.safe-exceptions; + servant = dontCheck super.servant; + should-not-typecheck = dontCheck super.should-not-typecheck; + stringbuilder = dontCheck super.stringbuilder; + th-compat = dontCheck super.th-compat; + th-orphans = dontCheck super.th-orphans; + zenc = dontCheck super.zenc; +} diff --git a/pkgs/development/haskell-modules/configuration-nix.nix b/pkgs/development/haskell-modules/configuration-nix.nix index 526a96d59fc3b..b6bc5e9a37ef9 100644 --- a/pkgs/development/haskell-modules/configuration-nix.nix +++ b/pkgs/development/haskell-modules/configuration-nix.nix @@ -1259,9 +1259,6 @@ builtins.intersectAttrs super { ''; }) (addBuildTool pkgs.buildPackages.makeWrapper super.cut-the-crap); - # Compiling the readme throws errors and has no purpose in nixpkgs - aeson-gadt-th = disableCabalFlag "build-readme" super.aeson-gadt-th; - # Fix compilation of Setup.hs by removing the module declaration. # See: https://github.com/tippenein/guid/issues/1 guid = overrideCabal (drv: { diff --git a/pkgs/development/haskell-modules/default.nix b/pkgs/development/haskell-modules/default.nix index 08ee6219edff8..95a2610de6982 100644 --- a/pkgs/development/haskell-modules/default.nix +++ b/pkgs/development/haskell-modules/default.nix @@ -16,6 +16,7 @@ configurationArm ? import ./configuration-arm.nix, configurationDarwin ? import ./configuration-darwin.nix, configurationWindows ? import ./configuration-windows.nix, + configurationCross ? import ./configuration-cross.nix, configurationJS ? import ./configuration-ghcjs-9.x.nix, }: @@ -46,6 +47,9 @@ let ++ lib.optionals stdenv.hostPlatform.isWindows [ (configurationWindows { inherit pkgs haskellLib; }) ] + ++ lib.optionals (stdenv.buildPlatform != stdenv.hostPlatform) [ + (configurationCross { inherit pkgs haskellLib; }) + ] ++ lib.optionals stdenv.hostPlatform.isGhcjs [ (configurationJS { inherit pkgs haskellLib; }) ]; diff --git a/pkgs/development/haskell-modules/generic-builder.nix b/pkgs/development/haskell-modules/generic-builder.nix index 5cebe4aec5d0d..7ecda0d0c66ed 100644 --- a/pkgs/development/haskell-modules/generic-builder.nix +++ b/pkgs/development/haskell-modules/generic-builder.nix @@ -10,12 +10,73 @@ runCommandCC, ghcWithHoogle, ghcWithPackages, + haskellLib, + iserv-proxy, nodejs, + writeShellScriptBin, }: let isCross = stdenv.buildPlatform != stdenv.hostPlatform; + crossSupport = rec { + emulator = stdenv.hostPlatform.emulator buildPackages; + + hasBuiltinTH = stdenv.hostPlatform.isGhcjs; + + canProxyTH = + lib.versionAtLeast ghc.version "9.6" && stdenv.hostPlatform.emulatorAvailable buildPackages; + + # Many suites use Template Haskell for test discovery, including QuickCheck + canCheck = hasBuiltinTH || canProxyTH; + + # stdenv.make-derivation sets `doCheck = false` when the build platform can't directly execute the host platform + # which means the `checkPhase` ends up disabled for cross and we need to sneak it back in somehow + # TODO: avoid this workaround - add some sort of doCheckEmulated? + checkPhaseSidecar = doCheck: drv: anchor: lib.trim '' + ${anchor} + ${lib.optionalString (doCheck && isCross) drv.checkPhase} + ''; + + iservWrapper = + let + buildProxy = lib.getExe' iserv-proxy.build "iserv-proxy"; + + wrapperScript = + enableProfiling: + let + overrides = haskellLib.overrideCabal { + enableLibraryProfiling = enableProfiling; + enableExecutableProfiling = enableProfiling; + }; + hostProxy = lib.getExe' (overrides iserv-proxy.host) "iserv-proxy-interpreter"; + in + buildPackages.writeShellScriptBin ("iserv-wrapper" + lib.optionalString enableProfiling "-prof") '' + set -euo pipefail + PORT=$((5000 + $RANDOM % 5000)) + (>&2 echo "---> Starting interpreter on port $PORT") + ${emulator} ${hostProxy} tmp $PORT & + RISERV_PID="$!" + trap "kill $RISERV_PID" EXIT # Needs cleanup when building without sandbox + ${buildProxy} $@ 127.0.0.1 "$PORT" + (>&2 echo "---> killing interpreter...") + ''; + + # GHC will add `-prof` to the external interpreter when doing a profiled build. + # Since a single derivation can build with both profiling and non-profiling versions + # we need both versions made available + both = buildPackages.symlinkJoin { + name = "iserv-wrapper-both"; + paths = builtins.map wrapperScript [ + false + true + ]; + }; + + in + "${both}/bin/iserv-wrapper"; + }; + # Pass the "wrong" C compiler rather than none at all so packages that just # use the C preproccessor still work, see # https://github.com/haskell/cabal/issues/6466 for details. @@ -67,7 +128,7 @@ in buildFlags ? [ ], haddockFlags ? [ ], description ? null, - doCheck ? !isCross, + doCheck ? isCross -> crossSupport.canCheck, doBenchmark ? false, doHoogle ? true, doHaddockQuickjump ? doHoogle, @@ -205,10 +266,15 @@ in # of `meta.pkgConfigModules`. This option defaults to false for now, since # this metadata is far from complete in nixpkgs. __onlyPropagateKnownPkgConfigModules ? false, + + enableExternalInterpreter ? isCross && crossSupport.canProxyTH, }@args: assert editedCabalFile != null -> revision != null; +# We only use iserv-proxy for the external interpreter +assert enableExternalInterpreter -> crossSupport.canProxyTH; + # --enable-static does not work on windows. This is a bug in GHC. # --enable-static will pass -staticlib to ghc, which only works for mach-o and elf. assert stdenv.hostPlatform.isWindows -> enableStaticLibraries == false; @@ -298,7 +364,15 @@ let "--hsc2hs-option=--cross-compile" (optionalString enableHsc2hsViaAsm "--hsc2hs-option=--via-asm") ] - ++ optional (allPkgconfigDepends != [ ]) "--with-pkg-config=${pkg-config.targetPrefix}pkg-config"; + ++ optional (allPkgconfigDepends != [ ]) "--with-pkg-config=${pkg-config.targetPrefix}pkg-config" + + ++ optionals enableExternalInterpreter ( + map (opt: "--ghc-option=${opt}") [ + "-fexternal-interpreter" + "-pgmi" + crossSupport.iservWrapper + ] + ); makeGhcOptions = opts: lib.concatStringsSep " " (map (opt: "--ghc-option=${opt}") opts); @@ -541,7 +615,7 @@ let export GHC_PACKAGE_PATH="''${NIX_GHC_PACKAGE_PATH_FOR_TEST}" fi - exec "$@" + exec ${if (isCross && crossSupport.canCheck) then "node" else crossSupport.emulator} "$@" ''; testTargetsString = @@ -770,7 +844,7 @@ lib.fix ( '' + '' ${setupCommand} build ${buildTarget}${buildFlagsString} - runHook postBuild + ${crossSupport.checkPhaseSidecar doCheck drv "runHook postBuild"} ''; inherit doCheck; diff --git a/pkgs/development/haskell-modules/make-package-set.nix b/pkgs/development/haskell-modules/make-package-set.nix index 36d0fe878970a..74369bf2ea67f 100644 --- a/pkgs/development/haskell-modules/make-package-set.nix +++ b/pkgs/development/haskell-modules/make-package-set.nix @@ -49,7 +49,7 @@ let inherit (haskellLib) overrideCabal; mkDerivationImpl = pkgs.callPackage ./generic-builder.nix { - inherit stdenv; + inherit stdenv haskellLib; nodejs = buildPackages.nodejs-slim; inherit (self) buildHaskellPackages @@ -57,6 +57,10 @@ let ghcWithHoogle ghcWithPackages ; + iserv-proxy = { + build = buildHaskellPackages.iserv-proxy; + host = self.iserv-proxy; + }; inherit (self.buildHaskellPackages) jailbreak-cabal; hscolour = overrideCabal (drv: { isLibrary = false; diff --git a/pkgs/development/haskell-modules/non-hackage-packages.nix b/pkgs/development/haskell-modules/non-hackage-packages.nix index e0cd3a7ac2e9a..c976743b3b6bf 100644 --- a/pkgs/development/haskell-modules/non-hackage-packages.nix +++ b/pkgs/development/haskell-modules/non-hackage-packages.nix @@ -41,6 +41,8 @@ self: super: ghc-settings-edit = self.callPackage ../tools/haskell/ghc-settings-edit { }; + iserv-proxy = self.callPackage ../tools/haskell/iserv-proxy { }; + # Upstream won't upload vaultenv to Hackage: # https://github.com/channable/vaultenv/issues/1 krank:ignore-line vaultenv = self.callPackage ../tools/haskell/vaultenv { }; diff --git a/pkgs/development/tools/haskell/iserv-proxy/default.nix b/pkgs/development/tools/haskell/iserv-proxy/default.nix new file mode 100644 index 0000000000000..0c322e50dc189 --- /dev/null +++ b/pkgs/development/tools/haskell/iserv-proxy/default.nix @@ -0,0 +1,53 @@ +{ + mkDerivation, + fetchFromGitHub, + array, + base, + binary, + bytestring, + containers, + deepseq, + directory, + filepath, + ghci, + lib, + network, +}: +mkDerivation { + pname = "iserv-proxy"; + version = "9.3-unstable-2025-06-21"; + + # https://github.com/stable-haskell/iserv-proxy/pull/1 + src = fetchFromGitHub { + owner = "stable-haskell"; + repo = "iserv-proxy"; + rev = "a53c57c9a8d22a66a2f0c4c969e806da03f08c28"; + hash = "sha256-WaswH0Y+Fmupvv8AkIlQBlUy/IdD3Inx9PDuE+5iRYY="; + }; + + isLibrary = true; + isExecutable = true; + libraryHaskellDepends = [ + array + base + binary + bytestring + containers + deepseq + directory + filepath + ghci + network + ]; + executableHaskellDepends = [ + base + binary + bytestring + directory + filepath + ghci + network + ]; + description = "iserv allows GHC to delegate Template Haskell computations"; + license = lib.licenses.bsd3; +} diff --git a/pkgs/top-level/release-haskell.nix b/pkgs/top-level/release-haskell.nix index 2fc2fd113afcd..6f2db4602b7f6 100644 --- a/pkgs/top-level/release-haskell.nix +++ b/pkgs/top-level/release-haskell.nix @@ -520,7 +520,26 @@ let ghc948 ; }; - }; + } + // + removePlatforms + [ + # Testing cross from x86_64-linux + "aarch64-darwin" + "aarch64-linux" + "x86_64-darwin" + ] + { + haskell.packages.ghc912 = { + inherit (packagePlatforms pkgs.pkgsCross.aarch64-multiplatform.haskell.packages.ghc912) + ghc + hello + microlens + miso + reflex-dom + ; + }; + }; }; }) (versionedCompilerJobs {