Home / Input Output / haskell.nix
May 05, 3-4 PM (0)
May 05, 4-5 PM (0)
May 05, 5-6 PM (0)
May 05, 6-7 PM (0)
May 05, 7-8 PM (0)
May 05, 8-9 PM (0)
May 05, 9-10 PM (0)
May 05, 10-11 PM (0)
May 05, 11-12 AM (0)
May 06, 12-1 AM (1)
May 06, 1-2 AM (0)
May 06, 2-3 AM (0)
May 06, 3-4 AM (0)
May 06, 4-5 AM (0)
May 06, 5-6 AM (0)
May 06, 6-7 AM (0)
May 06, 7-8 AM (0)
May 06, 8-9 AM (0)
May 06, 9-10 AM (0)
May 06, 10-11 AM (0)
May 06, 11-12 PM (0)
May 06, 12-1 PM (0)
May 06, 1-2 PM (0)
May 06, 2-3 PM (0)
May 06, 3-4 PM (0)
May 06, 4-5 PM (0)
May 06, 5-6 PM (0)
May 06, 6-7 PM (0)
May 06, 7-8 PM (0)
May 06, 8-9 PM (0)
May 06, 9-10 PM (0)
May 06, 10-11 PM (0)
May 06, 11-12 AM (0)
May 07, 12-1 AM (1)
May 07, 1-2 AM (0)
May 07, 2-3 AM (0)
May 07, 3-4 AM (0)
May 07, 4-5 AM (0)
May 07, 5-6 AM (1)
May 07, 6-7 AM (0)
May 07, 7-8 AM (1)
May 07, 8-9 AM (0)
May 07, 9-10 AM (0)
May 07, 10-11 AM (4)
May 07, 11-12 PM (0)
May 07, 12-1 PM (0)
May 07, 1-2 PM (0)
May 07, 2-3 PM (0)
May 07, 3-4 PM (0)
May 07, 4-5 PM (0)
May 07, 5-6 PM (0)
May 07, 6-7 PM (0)
May 07, 7-8 PM (0)
May 07, 8-9 PM (0)
May 07, 9-10 PM (0)
May 07, 10-11 PM (0)
May 07, 11-12 AM (1)
May 08, 12-1 AM (2)
May 08, 1-2 AM (2)
May 08, 2-3 AM (3)
May 08, 3-4 AM (1)
May 08, 4-5 AM (1)
May 08, 5-6 AM (0)
May 08, 6-7 AM (1)
May 08, 7-8 AM (1)
May 08, 8-9 AM (2)
May 08, 9-10 AM (1)
May 08, 10-11 AM (0)
May 08, 11-12 PM (0)
May 08, 12-1 PM (0)
May 08, 1-2 PM (0)
May 08, 2-3 PM (0)
May 08, 3-4 PM (0)
May 08, 4-5 PM (0)
May 08, 5-6 PM (0)
May 08, 6-7 PM (0)
May 08, 7-8 PM (0)
May 08, 8-9 PM (0)
May 08, 9-10 PM (1)
May 08, 10-11 PM (0)
May 08, 11-12 AM (1)
May 09, 12-1 AM (1)
May 09, 1-2 AM (0)
May 09, 2-3 AM (0)
May 09, 3-4 AM (1)
May 09, 4-5 AM (1)
May 09, 5-6 AM (0)
May 09, 6-7 AM (0)
May 09, 7-8 AM (0)
May 09, 8-9 AM (0)
May 09, 9-10 AM (0)
May 09, 10-11 AM (2)
May 09, 11-12 PM (0)
May 09, 12-1 PM (0)
May 09, 1-2 PM (0)
May 09, 2-3 PM (0)
May 09, 3-4 PM (0)
May 09, 4-5 PM (0)
May 09, 5-6 PM (0)
May 09, 6-7 PM (0)
May 09, 7-8 PM (0)
May 09, 8-9 PM (0)
May 09, 9-10 PM (0)
May 09, 10-11 PM (0)
May 09, 11-12 AM (0)
May 10, 12-1 AM (3)
May 10, 1-2 AM (0)
May 10, 2-3 AM (0)
May 10, 3-4 AM (0)
May 10, 4-5 AM (0)
May 10, 5-6 AM (0)
May 10, 6-7 AM (0)
May 10, 7-8 AM (0)
May 10, 8-9 AM (1)
May 10, 9-10 AM (0)
May 10, 10-11 AM (2)
May 10, 11-12 PM (0)
May 10, 12-1 PM (0)
May 10, 1-2 PM (4)
May 10, 2-3 PM (0)
May 10, 3-4 PM (0)
May 10, 4-5 PM (0)
May 10, 5-6 PM (0)
May 10, 6-7 PM (0)
May 10, 7-8 PM (0)
May 10, 8-9 PM (3)
May 10, 9-10 PM (1)
May 10, 10-11 PM (4)
May 10, 11-12 AM (0)
May 11, 12-1 AM (2)
May 11, 1-2 AM (0)
May 11, 2-3 AM (0)
May 11, 3-4 AM (0)
May 11, 4-5 AM (0)
May 11, 5-6 AM (0)
May 11, 6-7 AM (0)
May 11, 7-8 AM (0)
May 11, 8-9 AM (0)
May 11, 9-10 AM (0)
May 11, 10-11 AM (0)
May 11, 11-12 PM (0)
May 11, 12-1 PM (1)
May 11, 1-2 PM (0)
May 11, 2-3 PM (0)
May 11, 3-4 PM (0)
May 11, 4-5 PM (0)
May 11, 5-6 PM (0)
May 11, 6-7 PM (0)
May 11, 7-8 PM (0)
May 11, 8-9 PM (0)
May 11, 9-10 PM (0)
May 11, 10-11 PM (0)
May 11, 11-12 AM (0)
May 12, 12-1 AM (0)
May 12, 1-2 AM (0)
May 12, 2-3 AM (0)
May 12, 3-4 AM (0)
May 12, 4-5 AM (2)
May 12, 5-6 AM (0)
May 12, 6-7 AM (0)
May 12, 7-8 AM (0)
May 12, 8-9 AM (7)
May 12, 9-10 AM (6)
May 12, 10-11 AM (3)
May 12, 11-12 PM (1)
May 12, 12-1 PM (0)
May 12, 1-2 PM (1)
May 12, 2-3 PM (1)
May 12, 3-4 PM (0)
72 commits this week May 05, 2026 - May 12, 2026
dummy-ghc: align --info with real GHC across more compiler/cross variants
Extend `lib/dummy-ghc.nix` and the `tests.dummy-ghc-info` filter
list so the dummy's `--info` matches the real cross/native GHC
`--info` for the variants tested in hydra:

  * aarch64-multiplatform (linux-gnu cross, Stage 1, no `_dyn`
    RTS ways) — add a cross-linux-gnu branch alongside
    android/static, keyed off cpu mismatch so libc-only crosses
    (musl64) fall through to the native branch.
  * `cross compiling` field now compares cpu + kernel rather
    than the full platform triple, matching what GHC itself
    reports (libc differences alone are not cross).
  * musl32 (i686-linux-musl) and ucrt64 (x86_64-w64-mingw32):
    `platformString` normalises `i686 -> i386` and the
    Windows vendor/kernel pair to `unknown/mingw32`, matching
    real GHC's `Target platform` / `target platform string`.
  * ghc967 native and musl64: branch the RTS-ways string on
    GHC version (9.6 uses a smaller, differently-ordered set
    than 9.8+) and on libc (the 9.6 musl bootstrap reports a
    12-way set while stock nixpkgs 9.6.7 reports 10).

Test-side: add a small set of per-version cosmetic fields to
`ignoredFields` (`GCC extra via C opts`, `LLVM clang command`,
`ld command`/flags, `dllwrap`, `touch command`,
`target RTS linker only supports shared libraries`) that don't
drive cabal-install elaboration.

Verified `tests.dummy-ghc-info` passes on linux-0 (with
`--builders ''`) for: ghc9141 native + aarch64-multiplatform,
ghc9124 musl32 + ucrt64, ghc967 native + musl64.
iserv-proxy: route android static-linking flags via cabalProjectLocal
v2 ignores the `setupBuildFlags` + `enableDebugRTS = true` mechanism
that v1 used to give the cross-compiled iserv-proxy-interpreter its
`-optl-static -optl-ldl -debug` (and `-optl-no-pie` on aarch32) at
link time.  Express the equivalent through cabal.project so plan-nix
records the flags in the slice's UnitId-relevant configure-args.

The inner `pkgs.stdenv.hostPlatform.isAndroid` guard scopes the
stanza to the cross-host evaluation only; the build-platform
iserv-proxy (`exes final.pkgsBuildBuild`) does not pick it up and
links normally.

Known issue: the resulting binary still SIGSEGVs under qemu-aarch64
at startup — see `project_v2_aarch64_android_iserv.md` in
`.claude/memory`.  Tracking separately.
iserv-proxy: drop executable-static cabal flag (match v1 exactly)
v1's iserv-proxy build only uses `-optl-static -optl-ldl` to drive
static linking; it does NOT set cabal's `executable-static`.  The
`executable-static` flag forces ghc to use a specific static link
flow that combined with `-optl-static` may have led to subtle
binary differences (the strace showed v2 segfaulting in early
RTS init while v1 mmap'd its GC arena correctly).  Drop
`executable-static` to mirror v1.
iserv-proxy: drop -debug from android ghc-options
The static aarch64-android iserv binary segfaulted under qemu
before reaching argv parsing — the strace shows it hit the
stack guard page during RTS init.  The debug RTS variant
(`-debug`) does more setup work at startup and pushed stack
usage past qemu's mapped guard.  v1 set `enableDebugRTS = true`
via the haskell.nix module option but apparently never actually
ran under qemu with the recent qemu / RTS versions — or v1's
`Setup build --ghc-option=-debug` doesn't propagate quite the
same way as `ghc-options: -debug` in cabal.project's pkgHash.

Without -debug the binary runs to args parsing under qemu, so
this is what we want.  -optl-static / -optl-ldl stays.
linux-cross: add -optc-fPIC project-wide on aarch64
v1's armv6l-linux.nix injects `--gcc-option=-fPIC` into every
package's setupBuildFlags on aarch64 (work-around for GHC #15275).
The wrapGhc consumes `linux-cross.ghcOptions` so adding
`-optc-fPIC` there makes the C bits of all cross-compiled
packages get -fPIC just like v1.  Without it the static
aarch64-android iserv binary segfaulted under qemu even with all
the cabal flags matching v1.  Also clean up the per-package
iserv-proxy block (executable-static: True is enough; static/
shared/executable-dynamic flow from there).
iserv-proxy: use cabal executable-static, drop redundant -optl-static
`executable-static: True` is cabal's first-class way to ask for a
static executable; it tells ghc to pass `-static -optl-static` at
the right points, gets dependency static-libs right, and doesn't
interact badly with `-fPIC` the way the standalone `-optl-static`
ghc-option did (which left us with a successfully-linked but
segfaulting binary under qemu-aarch64).
iserv-proxy: force static/non-shared on Android (match v1 cabal flags)
v1 invokes Setup configure with
`--disable-shared --enable-static --disable-executable-dynamic`
for iserv-proxy on android (via the project's default cabal-pkg
flags), so all iserv artifacts are static — necessary for qemu
to run the binary on the build host without Android's dynamic
loader.  v2's default cabal.project has `shared: True`,
`static: False`, `executable-dynamic: False`; adding the
explicit override for the iserv-proxy package on android gets
the same effect: static lib + static exe.  Avoids the segfault
under qemu that was hitting after `-optl-static` alone left the
linker fighting between a shared lib and static exe.
iserv-proxy: also pass -optc-fPIC on aarch64 Android (matches v1)
v1's armv6l-linux.nix `addPackageKeys` injects
`--gcc-option=-fPIC` into `setupBuildFlags` for every package on
aarch64 cross builds (work-around for GHC #15275).  For
iserv-proxy specifically this means the cbits get compiled
with -fPIC, which the qemu-emulated static aarch64-android
binary needs at runtime — without it the binary segfaults
inside qemu before iserv even initialises.  v2 ignores
`setupBuildFlags`, so route -fPIC through cabal.project as
`-optc-fPIC` (passes -fPIC to gcc via ghc).
iserv-proxy: add -debug to Android ghc-options (matches enableDebugRTS)
v1's android override included `enableDebugRTS = true` alongside
`-optl-static -optl-ldl` — the haskell.nix module translates
that to `--ghc-option=-debug`, picking the debug RTS variant.
Without it the statically-linked iserv-proxy-interpreter
segfaulted under qemu-aarch64.  Add `-debug` to the cabal.project
ghc-options block so v2 picks up the same combination.
iserv-proxy: key Android static-link guard off the INNER pkgs
The cabalProjectLocal closure is shared between two
`cabalProject'` instantiations of the iserv-proxy project — one
under `pkgsBuildBuild` (build host iserv-proxy) and one under
`final` (cross-target iserv-proxy-interpreter).  Guarding on
`final.stdenv.hostPlatform.isAndroid` matched both because
`final` is the OUTER cross context (android).  The build-host
iserv-proxy then got `-optl-static` and the link of its shared
library failed with
`requires dynamic R_X86_64_32 reloc against '__TMC_END__' which
may overflow at runtime`.

Use the INNER `pkgs.stdenv.hostPlatform.isAndroid` (the lambda's
own `pkgs` arg) so each instantiation sees its own platform.

The Windows case is fine as-is because it has a cabal-level
`if os(mingw32)` guard inside the project file.
iserv-proxy: drop `if os(android)` guard (isAndroid Nix check is enough)
The `final.lib.optionalString final.stdenv.hostPlatform.isAndroid`
already keeps the flags out of the build-platform iserv-proxy
build (different cabalProject' instantiation under pkgsBuildBuild).
Drop the redundant `if os(android)` so the block parses cleanly
— cabal's project-file conditional may not have matched the
android os name we use here, leaving the ghc-options block
inert.
iserv-proxy: link statically on Android in cabal.project for v2
v1's iserv-proxy-interpreter.override added `--ghc-option=-optl-static
--ghc-option=-optl-ldl` (and `-optl-no-pie` on aarch32) via
`setupBuildFlags` so the cross-compiled binary is self-contained
— necessary because qemu-user-mode on the build host can't
satisfy Android's dynamic loader (`/system/bin/linker64` /
`/system/bin/linker`).  v2 ignores `setupBuildFlags`, so the
slice's binary came out dynamically linked and qemu refused to
launch it with
`qemu-aarch64: Could not open '/system/bin/linker64'`, breaking
cross-TH for any package that touched iserv on android.

Express the same flags in the iserv-proxy project's
cabalProjectLocal under `if os(android)`.  plan-nix now records
them in the slice's UnitId-relevant configure-args, so the
slice's cabal v2-build picks them up — same outcome as v1.
linux-cross: provide `wrapGhc` for v2 cross-TH
v2 builder's slice cabal v2-build doesn't consume v1's
`setupBuildFlags`, so the cross-TH iserv ghc-options
(`-fexternal-interpreter -pgmi <qemu-wrapper>` etc.) never reached
the cross GHC and slices for packages with TH (e.g. th-orphans,
th-lift) failed with "Couldn't find a target code interpreter.
Try with -fexternal-interpreter" on aarch64-android-prebuilt,
aarch64-multiplatform, and other linux-cross targets.

Mirror `overlays/mingw_w64.nix`'s `wrapGhc`: a derivation that
`--add-flags`-wraps every ghc / ghci binary with the linux-cross
`ghcOptions` (already used by v1 via `setupBuildFlags`).  Cabal
still sees the unwrapped `ghc --info` output (compiler-id, target
platform, capabilities) so UnitId hash inputs are unchanged and
plan-nix's recorded UnitIds keep matching the slice's
computation.  Other GHC binaries (`ghc-pkg`, `hsc2hs`, `runghc`,
`haddock`, …) are symlinked through unchanged.

The v2 comp-v2-builder already consumes
`templateHaskell.wrapGhc` when set (see `sliceGhc`), so wiring
this through is purely additive — windows already had it.
docs+v2: route DWARF via compilerSelection and debug-info, document the migration
v2 splits v1's monolithic `.dwarf` overlay into two pieces:
  * `debug-info:` in cabal.project for DWARF in the user's source
    (plan-nix records it → matching UnitIds).
  * `compilerSelection` swap to `c.dwarf` for DWARF in the RTS /
    `ghc-internal` (every plan/slice/dummy-ghc derives from the
    same GHC, so UnitIds stay consistent).

  * `docs/dev/debug-info.md` — parallel to docs/dev/profiling.md.
  * `builder/comp-v2-builder.nix` — `.dwarf` now throws with both
    knobs in the migration hint and a pointer to the new doc.
  * `test/cabal-simple-debug/default.nix` — uses both knobs and
    queries `.exePath` instead of `.dwarf.exePath`.
v2 builder: error on \`.dwarf\` with migration hint; route DWARF via cabal.project
Mirrors the existing \`.profiled\` treatment.  v1's \`.dwarf\` was an
overlay rebuild with \`enableDWARF = true\`, which would diverge
from plan-nix's UnitId in v2 (the overlay toggles aren't in
plan-nix's hash).  Express DWARF in cabal.project's \`debug-info:\`
instead — the slice itself carries DWARF when that's set, and
\`<slice>.exePath\` gives the debug-info exe directly.

Updated test/cabal-simple-debug to add \`debug-info: 2\` to
cabalProjectLocal and use \`.exePath\` instead of \`.dwarf.exePath\`.
v2 builder: accept backpack instantiations in the captured-unit set
The exe slice that consumes an indefinite backpack library ends up
materialising the instantiated form (a `+`-suffixed unit-id, e.g.
`bckpck-0.1.0.0-55b00c8f+Dwq5ijz...`) alongside the exe itself,
because the indefinite consumer's own slice produced no compiled
artifacts to compose in (cabal v2-build for an indefinite library
is "Up to date" and writes no `.conf`).  v1 handles this with a
per-instantiation derivation (`lib/default.nix:369`'s
`components.library.override { inherit instantiations; }`); v2
doesn't yet have a slice-per-instantiation, so the instantiated
unit rides along inside the consuming slice's $out.

Three captured-unit changes:
  * Read pkg-name from cabal's `dist-newstyle/cache/plan.json`
    for bin-only units (no `.conf`).  Falling back to uid parsing
    misclassified OS-prefix-shortened names like `bckpck-...` as
    a package called `bckpck` instead of `backpack`.
  * Allow 0 target-package units (indefinite libraries register
    nothing).
  * Skip `+`-suffixed unit-ids when counting target-package units
    — they're cabal-emitted instantiation byproducts, not the
    slice's own target.  The "no foreign package" invariant
    (everything outside `expectedPackage` ∪ `allowedBuildToolPackages`
    is rejected) still applies and is what catches an accidentally
    rebuilt lib dep.
v2 builder: handle sublib pkg-name + drop backpack nix-tools override
Three related cleanups:

* `build-cabal-slice.nix`: prefer `package-name:` over `name:` in
  the captured-unit `.conf` parser.  For sublibs cabal records
  `name: z-<pkg>-z-<sublib>` (z-encoded) but also
  `package-name: <pkg>` — the latter is what the slice's expected
  package check should match against.

* `test/backpack/default.nix`: drop
  `inherit (evalPackages.haskell-nix) nix-tools;`.  The line was
  needed when backpack PR (#2467, 2026-03-17) added a new
  `make-install-plan` flag that wasn't yet in the prebuilt
  `nix-tools-static` tarball.  The tarball has been updated since
  (febad32e4 on 2026-03-24, and subsequently), so the default
  `nix-tools-unchecked` picks up backpack support; the override
  forces an unnecessary from-source build that on aarch64-darwin
  cascades into a v2 slice mismatch on `cabal-install`'s
  `pkg-src.repo.type` (`secure-repo` vs `remote-repo`).

* `test/cabal.project.local`: bump the pinned
  `head.hackage.ghc.haskell.org` `--sha256` to match the current
  index tarball — the previous hash was stale and would fail
  the fixed-output hash check during plan-to-nix.
v2 builder: use .conf 'name:' field over uid prefix for pkg matching
cabal's OS-prefix patch shortens unit-id prefixes (e.g.
`bytrdr-1.0.4-...` for the `byteorder` package), so parsing the
pkg-name from the uid alone misclassified lib units and failed the
post-install captured-unit check with
`slice captured 0 units for the target package 'byteorder'` even
when the captured uid WAS the right one.

Read the authoritative pkg-name from each unit's `.conf` (which
records the real package name) and fall back to uid parsing only
for bin-only units that don't have a .conf.
v2 builder: inject component.libs paths into post-register .conf
GHC's runtime linker (the one TH eval uses, including under
`-fexternal-interpreter` / `ghc-iserv`) dlopens each loaded
package's `extra-libraries:` entries by bare name (e.g.
HsOpenSSL's `extra-libraries: ssl crypto` → `libssl.so`).
Cabal records empty `library-dirs:` etc. in the per-unit
`.conf` because we deliberately don't push these paths into
the slice's configure args (that would land in
`pkgHashExtraLibDirs` and fork the slice's unit-id from
plan-nix).

v1 baked these into the .conf via `make-config-files.nix`'s
`extra-lib-dirs` flag fed to `Setup configure` (v1 used its
own unit-id scheme so the hash didn't matter).  In v2 we
post-process: after `cabal v2-build` registers the unit, append
the lib paths to the `library-dirs:` / `library-dirs-static:` /
`dynamic-library-dirs:` fields, then `ghc-pkg recache`.

Downstream consumers' unit-ids are unaffected — their config hash
records each dep's unit-id, not the dep's .conf content — so the
chain stays plan-nix-consistent end to end.