Home / Input Output / haskell.nix
May 17, 8-9 PM (0)
May 17, 9-10 PM (0)
May 17, 10-11 PM (0)
May 17, 11-12 AM (1)
May 18, 12-1 AM (3)
May 18, 1-2 AM (0)
May 18, 2-3 AM (1)
May 18, 3-4 AM (2)
May 18, 4-5 AM (1)
May 18, 5-6 AM (0)
May 18, 6-7 AM (0)
May 18, 7-8 AM (0)
May 18, 8-9 AM (0)
May 18, 9-10 AM (0)
May 18, 10-11 AM (0)
May 18, 11-12 PM (0)
May 18, 12-1 PM (2)
May 18, 1-2 PM (1)
May 18, 2-3 PM (0)
May 18, 3-4 PM (0)
May 18, 4-5 PM (0)
May 18, 5-6 PM (0)
May 18, 6-7 PM (0)
May 18, 7-8 PM (0)
May 18, 8-9 PM (0)
May 18, 9-10 PM (0)
May 18, 10-11 PM (0)
May 18, 11-12 AM (1)
May 19, 12-1 AM (1)
May 19, 1-2 AM (0)
May 19, 2-3 AM (2)
May 19, 3-4 AM (1)
May 19, 4-5 AM (0)
May 19, 5-6 AM (0)
May 19, 6-7 AM (2)
May 19, 7-8 AM (0)
May 19, 8-9 AM (0)
May 19, 9-10 AM (0)
May 19, 10-11 AM (0)
May 19, 11-12 PM (1)
May 19, 12-1 PM (0)
May 19, 1-2 PM (0)
May 19, 2-3 PM (0)
May 19, 3-4 PM (0)
May 19, 4-5 PM (0)
May 19, 5-6 PM (0)
May 19, 6-7 PM (0)
May 19, 7-8 PM (0)
May 19, 8-9 PM (0)
May 19, 9-10 PM (0)
May 19, 10-11 PM (0)
May 19, 11-12 AM (1)
May 20, 12-1 AM (1)
May 20, 1-2 AM (0)
May 20, 2-3 AM (0)
May 20, 3-4 AM (0)
May 20, 4-5 AM (0)
May 20, 5-6 AM (0)
May 20, 6-7 AM (0)
May 20, 7-8 AM (0)
May 20, 8-9 AM (0)
May 20, 9-10 AM (1)
May 20, 10-11 AM (0)
May 20, 11-12 PM (0)
May 20, 12-1 PM (0)
May 20, 1-2 PM (0)
May 20, 2-3 PM (0)
May 20, 3-4 PM (0)
May 20, 4-5 PM (0)
May 20, 5-6 PM (0)
May 20, 6-7 PM (0)
May 20, 7-8 PM (0)
May 20, 8-9 PM (0)
May 20, 9-10 PM (0)
May 20, 10-11 PM (0)
May 20, 11-12 AM (1)
May 21, 12-1 AM (1)
May 21, 1-2 AM (0)
May 21, 2-3 AM (1)
May 21, 3-4 AM (2)
May 21, 4-5 AM (0)
May 21, 5-6 AM (2)
May 21, 6-7 AM (1)
May 21, 7-8 AM (2)
May 21, 8-9 AM (2)
May 21, 9-10 AM (0)
May 21, 10-11 AM (0)
May 21, 11-12 PM (0)
May 21, 12-1 PM (0)
May 21, 1-2 PM (0)
May 21, 2-3 PM (0)
May 21, 3-4 PM (0)
May 21, 4-5 PM (0)
May 21, 5-6 PM (0)
May 21, 6-7 PM (0)
May 21, 7-8 PM (0)
May 21, 8-9 PM (0)
May 21, 9-10 PM (0)
May 21, 10-11 PM (0)
May 21, 11-12 AM (0)
May 22, 12-1 AM (1)
May 22, 1-2 AM (0)
May 22, 2-3 AM (0)
May 22, 3-4 AM (0)
May 22, 4-5 AM (1)
May 22, 5-6 AM (5)
May 22, 6-7 AM (0)
May 22, 7-8 AM (0)
May 22, 8-9 AM (0)
May 22, 9-10 AM (0)
May 22, 10-11 AM (1)
May 22, 11-12 PM (1)
May 22, 12-1 PM (0)
May 22, 1-2 PM (0)
May 22, 2-3 PM (0)
May 22, 3-4 PM (0)
May 22, 4-5 PM (0)
May 22, 5-6 PM (0)
May 22, 6-7 PM (0)
May 22, 7-8 PM (0)
May 22, 8-9 PM (0)
May 22, 9-10 PM (2)
May 22, 10-11 PM (0)
May 22, 11-12 AM (1)
May 23, 12-1 AM (1)
May 23, 1-2 AM (0)
May 23, 2-3 AM (2)
May 23, 3-4 AM (0)
May 23, 4-5 AM (1)
May 23, 5-6 AM (0)
May 23, 6-7 AM (0)
May 23, 7-8 AM (0)
May 23, 8-9 AM (2)
May 23, 9-10 AM (0)
May 23, 10-11 AM (1)
May 23, 11-12 PM (0)
May 23, 12-1 PM (0)
May 23, 1-2 PM (0)
May 23, 2-3 PM (0)
May 23, 3-4 PM (0)
May 23, 4-5 PM (0)
May 23, 5-6 PM (0)
May 23, 6-7 PM (0)
May 23, 7-8 PM (0)
May 23, 8-9 PM (0)
May 23, 9-10 PM (0)
May 23, 10-11 PM (2)
May 23, 11-12 AM (0)
May 24, 12-1 AM (1)
May 24, 1-2 AM (0)
May 24, 2-3 AM (0)
May 24, 3-4 AM (0)
May 24, 4-5 AM (0)
May 24, 5-6 AM (2)
May 24, 6-7 AM (0)
May 24, 7-8 AM (0)
May 24, 8-9 AM (0)
May 24, 9-10 AM (1)
May 24, 10-11 AM (0)
May 24, 11-12 PM (0)
May 24, 12-1 PM (0)
May 24, 1-2 PM (0)
May 24, 2-3 PM (0)
May 24, 3-4 PM (0)
May 24, 4-5 PM (0)
May 24, 5-6 PM (0)
May 24, 6-7 PM (0)
May 24, 7-8 PM (0)
May 24, 8-9 PM (0)
59 commits this week May 17, 2026 - May 24, 2026
Add cabal v2-build-based component builder ("v2 builder")
Adds an alternative component builder that runs `cabal v2-build`
inside a slice derivation and emits a cabal-store layout rather
than going through Setup.hs, alongside the existing Setup-based
v1 builder.  Selected per-project via the new `builderVersion`
option (default `1` for now; flip to `2` to opt in on a
per-project basis).

What the v2 builder provides:

* Per-component cabal-store slices that compose into a single
  `~/.cabal/store/ghc-<version>/` layout via `composeStore`.  Each
  slice's UnitId matches what a downstream `cabal v2-build` would
  compute against the real compiler, so a v2 store can be reused
  by interactive cabal invocations without recomputation.
* Public sublibrary support (`library <n>` stanzas) end-to-end —
  not currently supported by v1's Setup-based path.
* A v2 dev shell (`shell-for-v2.nix`) wired through `project.shellFor`
  whenever `builderVersion = 2`.  Two exposure modes:
  `cabal-store` (default) pre-populates `~/.cabal/store` so plain
  `cabal v2-build` reuses the slice's units; `ghc-pkg` mode wraps
  `ghc` / `ghc-pkg` with `GHC_ENVIRONMENT` / `GHC_PACKAGE_PATH` so
  `ghc -e` and similar see the composed store directly.
* A shared ghc shim (`ghc-shim.nix`) used by both the slice's
  `cabal v2-build` and the v2 shell so the user-facing compiler
  matches the slice compiler.  Adds cabal near-compiler aliases,
  patches the ghcjs `ar command` setting (drops missing `.o`s for
  backpack-on-ghcjs), and adds native-musl `unlit` /
  `ghc-iserv[-dyn|-prof]` aliases.

Plumbing changes:

* `builder/default.nix` + `builder/hspkg-builder.nix` dispatch on
  `builderVersion`.  v2-specific helpers (`buildCabalStoreSlice`,
  `composeStore`, `makeGhcShim`, the patched `v2CabalInstall`)
  live in `builder/default.nix`.
* `modules/project-common.nix` adds `builderVersion`.
  `modules/component-driver.nix` propagates it (plus
  `crossTemplateHaskellSupport` and `cabalProjectLocal`) into the
  pkg-set config.  `modules/stack-project.nix` forces v1 for
  stack projects (stack-to-nix doesn't produce the plan-json
  shapes v2 consumes).
* `modules/package-options.nix` adds round-trip options
  (`configureOptions`, `extraLibDirs`, `extraIncludeDirs`,
  `programOptions`) that v2 emits back into each slice's
  cabal.project.
* `modules/install-plan/configure-args.nix` extracts those
  round-trip options from plan.json's configure-args, and
  filters `-hide-all-packages` for v2 (cabal injects it on every
  Setup configure call; round-tripping it would fork UnitId
  hashes between plan-nix and slice cabal).
* `modules/install-plan/non-reinstallable.nix` skips the
  ghcjs/wasm boot-package hiding on v2 (v2 follows cabal's plan
  literally rather than overlaying haskell.nix's exclusions).
* `modules/shell.nix` adds the `exposePackagesVia` option.
* `lib/check.nix` adds a v2-slice branch that runs the installed
  exe under the platform's testWrapper, mirroring the v1 check
  semantics for slice derivations.
* `overlays/haskell.nix` propagates `builderVersion` /
  `cabalProjectLocal` through `addProjectAndPackageAttrs`,
  dispatches `shellFor` on `builderVersion`, and rebuilds
  iserv-proxy as both a non-profiled and profiled variant — v2
  reads the prof variant from `package iserv-proxy profiling:
  True` in cabal.project rather than `enableProfiling = true` on
  the .override (which v2 doesn't honour).
* `overlays/linux-cross.nix` + `overlays/mingw_w64.nix` add a
  `wrapGhc` derivation that bakes `-fexternal-interpreter -pgmi
  <iserv-wrapper>` into ghc itself — v2 can't put those into
  per-package `ghc-options:` (UnitId divergence), so it wraps the
  compiler instead.  The `compilerSelection` chain picks the
  wrapped ghc on cross targets.

Internal projects pin v1:

* `compiler/ghc/default.nix` (hadrian) and
  `overlays/bootstrap.nix` (hscolour) set `builderVersion = 1`
  explicitly so v2 evolution doesn't churn GHC builds while v2 is
  still settling.  `overlays/tools.nix` defaults
  `haskell-nix.tool` invocations to v1 for the same reason.

Tests are NOT migrated in this commit — the existing tests run
under v1 (the default) and exercise master's behaviour as before.
A follow-up will migrate or duplicate them under v2 once we know
which expectations need adjusting.
Add cabal v2-build-based component builder ("v2 builder")
Adds an alternative component builder that runs `cabal v2-build`
inside a slice derivation and emits a cabal-store layout rather
than going through Setup.hs, alongside the existing Setup-based
v1 builder.  Selected per-project via the new `builderVersion`
option (default `1` for now; flip to `2` to opt in on a
per-project basis).

What the v2 builder provides:

* Per-component cabal-store slices that compose into a single
  `~/.cabal/store/ghc-<version>/` layout via `composeStore`.  Each
  slice's UnitId matches what a downstream `cabal v2-build` would
  compute against the real compiler, so a v2 store can be reused
  by interactive cabal invocations without recomputation.
* Public sublibrary support (`library <n>` stanzas) end-to-end —
  not currently supported by v1's Setup-based path.
* A v2 dev shell (`shell-for-v2.nix`) wired through `project.shellFor`
  whenever `builderVersion = 2`.  Two exposure modes:
  `cabal-store` (default) pre-populates `~/.cabal/store` so plain
  `cabal v2-build` reuses the slice's units; `ghc-pkg` mode wraps
  `ghc` / `ghc-pkg` with `GHC_ENVIRONMENT` / `GHC_PACKAGE_PATH` so
  `ghc -e` and similar see the composed store directly.
* A shared ghc shim (`ghc-shim.nix`) used by both the slice's
  `cabal v2-build` and the v2 shell so the user-facing compiler
  matches the slice compiler.  Adds cabal near-compiler aliases,
  patches the ghcjs `ar command` setting (drops missing `.o`s for
  backpack-on-ghcjs), and adds native-musl `unlit` /
  `ghc-iserv[-dyn|-prof]` aliases.

Plumbing changes:

* `builder/default.nix` + `builder/hspkg-builder.nix` dispatch on
  `builderVersion`.  v2-specific helpers (`buildCabalStoreSlice`,
  `composeStore`, `makeGhcShim`, the patched `v2CabalInstall`)
  live in `builder/default.nix`.
* `modules/project-common.nix` adds `builderVersion`.
  `modules/component-driver.nix` propagates it (plus
  `crossTemplateHaskellSupport` and `cabalProjectLocal`) into the
  pkg-set config.  `modules/stack-project.nix` forces v1 for
  stack projects (stack-to-nix doesn't produce the plan-json
  shapes v2 consumes).
* `modules/package-options.nix` adds round-trip options
  (`configureOptions`, `extraLibDirs`, `extraIncludeDirs`,
  `programOptions`) that v2 emits back into each slice's
  cabal.project.
* `modules/install-plan/configure-args.nix` extracts those
  round-trip options from plan.json's configure-args, and
  filters `-hide-all-packages` for v2 (cabal injects it on every
  Setup configure call; round-tripping it would fork UnitId
  hashes between plan-nix and slice cabal).
* `modules/install-plan/non-reinstallable.nix` skips the
  ghcjs/wasm boot-package hiding on v2 (v2 follows cabal's plan
  literally rather than overlaying haskell.nix's exclusions).
* `modules/shell.nix` adds the `exposePackagesVia` option.
* `lib/check.nix` adds a v2-slice branch that runs the installed
  exe under the platform's testWrapper, mirroring the v1 check
  semantics for slice derivations.
* `overlays/haskell.nix` propagates `builderVersion` /
  `cabalProjectLocal` through `addProjectAndPackageAttrs`,
  dispatches `shellFor` on `builderVersion`, and rebuilds
  iserv-proxy as both a non-profiled and profiled variant — v2
  reads the prof variant from `package iserv-proxy profiling:
  True` in cabal.project rather than `enableProfiling = true` on
  the .override (which v2 doesn't honour).
* `overlays/linux-cross.nix` + `overlays/mingw_w64.nix` add a
  `wrapGhc` derivation that bakes `-fexternal-interpreter -pgmi
  <iserv-wrapper>` into ghc itself — v2 can't put those into
  per-package `ghc-options:` (UnitId divergence), so it wraps the
  compiler instead.  The `compilerSelection` chain picks the
  wrapped ghc on cross targets.

Internal projects pin v1:

* `compiler/ghc/default.nix` (hadrian) and
  `overlays/bootstrap.nix` (hscolour) set `builderVersion = 1`
  explicitly so v2 evolution doesn't churn GHC builds while v2 is
  still settling.  `overlays/tools.nix` defaults
  `haskell-nix.tool` invocations to v1 for the same reason.

Tests are NOT migrated in this commit — the existing tests run
under v1 (the default) and exercise master's behaviour as before.
A follow-up will migrate or duplicate them under v2 once we know
which expectations need adjusting.
iserv-proxy: enable --optimistic-linking on GHC ≥ 9.14 (#2512)
Bake `--optimistic-linking` into iserv-proxy / iserv-proxy-interpreter
at link time via `-with-rtsopts`.  `GHC/Linker/Executable.hs` emits
this into the generated `main.c` as `__conf.rts_opts`, which
`setupRtsFlags` processes with `RtsOptsAll` — bypassing the
`OPTION_UNSAFE` gate that `+RTS --optimistic-linking -RTS` on the
command line is subject to.  Makes the runtime linker tolerate
undefined symbols when loading object files at TH-eval time;
splices that don't actually reference the missing symbol then
resolve fine instead of aborting the load.  `-rtsopts=all` is kept
so wrapper scripts / GHCRTS can still override at invocation.
`--optimistic-linking` is only available in GHC's RTS from 9.14
onwards; gated on the Nix side since cabal.project doesn't allow
`if` inside a `package` stanza.

Refactor `cabalProjectLocal` for the iserv-proxy project from
`//`-chained `optionalAttrs` blocks to a single string built via
`optionalString` concatenation so the new directive can compose
with the existing aarch64+<9.8 threaded gate without the last
`//` clobbering it.

Also drop the previous `allow-newer: *:base, *:bytestring` block
on GHC > 9.10.  The bounds in iserv-proxy.cabal (`base < 5`,
`bytestring < 0.13`, etc.) already accommodate current GHCs; if a
transitive package needs relaxation we'll re-add a more targeted
allow-newer once we know which one.

Pulled out of #2504 (`hkm/builder-v2`).
iserv-proxy: enable --optimistic-linking on GHC ≥ 9.14
Bake `--optimistic-linking` into iserv-proxy / iserv-proxy-interpreter
at link time via `-with-rtsopts`.  `GHC/Linker/Executable.hs` emits
this into the generated `main.c` as `__conf.rts_opts`, which
`setupRtsFlags` processes with `RtsOptsAll` — bypassing the
`OPTION_UNSAFE` gate that `+RTS --optimistic-linking -RTS` on the
command line is subject to.  Makes the runtime linker tolerate
undefined symbols when loading object files at TH-eval time;
splices that don't actually reference the missing symbol then
resolve fine instead of aborting the load.  `-rtsopts=all` is kept
so wrapper scripts / GHCRTS can still override at invocation.
`--optimistic-linking` is only available in GHC's RTS from 9.14
onwards; gated on the Nix side since cabal.project doesn't allow
`if` inside a `package` stanza.

Refactor `cabalProjectLocal` for the iserv-proxy project from
`//`-chained `optionalAttrs` blocks to a single string built via
`optionalString` concatenation so the new directive can compose
with the existing aarch64+<9.8 threaded gate without the last
`//` clobbering it.

Also drop the previous `allow-newer: *:base, *:bytestring` block
on GHC > 9.10.  The bounds in iserv-proxy.cabal (`base < 5`,
`bytestring < 0.13`, etc.) already accommodate current GHCs; if a
transitive package needs relaxation we'll re-add a more targeted
allow-newer once we know which one.

Pulled out of #2504 (`hkm/builder-v2`).
cabal-project: drop readFile defaults; inject platform cabalProjectLocal defaults (#2511)
Two related changes to `modules/cabal-project.nix`:

**1. `cabalProjectLocal` / `cabalProjectFreeze` no longer auto-load
from disk.**  The options were typed `nullOr lines` with
`readIfExists`-based defaults that did IFD lookups for
`cabal.project.local` / `cabal.project.freeze` in the project
source.  Internal projects (hadrian, ghc-extra-projects) explicitly
set them to `null` just to suppress those reads, and the nullability
also prevented `mkBefore` directives from merging cleanly.

Switch the types to plain `lines` (default `""`) — projects that
relied on the implicit `readFile` behaviour now do it explicitly:

    cabalProjectLocal = builtins.readFile ./cabal.project.local;

Internal callers that set `null` for IFD-avoidance lose the
explicit assignments — the new default is already IFD-free.

**2. Platform-conditional defaults are now injected into every
cabal project's `cabalProjectLocal`.**  Four `mkIf` blocks added:

  * **musl host** — `package * \n executable-static: True`.
    comp-builder adds `--ghc-option=-optl=-static` at build time;
    surfacing the toggle here makes plan-to-nix record
    `--enable-executable-static`.  Build artefacts are unchanged.
  * **x86_64-darwin host** — `package * \n library-for-ghci: True`.
    Mirrors what comp-builder passes for `!ghcjs && !wasm && !android`
    (always true on darwin).
  * **android host** —
    `package * \n ghc-options: -optl-static -optl-ldl` (plus
    `-optl-no-pie` on aarch32).  Mirrors `lib/check.nix`'s
    test-exe `setupBuildFlags` re-wrap.
  * **wasm GHC ≥ 9.12** — `package * \n shared: True`.  Wasm's RTS
    linker only loads `.so` files; `--disable-shared` (cabal's
    default given the real compiler's reported capabilities) would
    force a `.a`-only install that TH-eval can't load.

All four sit at `mkBefore` priority so a project's own
`cabalProjectLocal` overrides them.

Cache impact: plan-nix hashes change for affected platforms.
Internal GHC builds verified byte-identical (drvPath
`na9chax1gj2n6jx3d6v86v6fdah9r9fd-ghc-9.14.1.drv` matches master).

Changelog entry added describing both changes.

Pulled out of #2504 (`hkm/builder-v2`).
cabal-project: drop readFile defaults; inject platform cabalProjectLocal defaults
Two related changes to `modules/cabal-project.nix`:

**1. `cabalProjectLocal` / `cabalProjectFreeze` no longer auto-load
from disk.**  The options were typed `nullOr lines` with
`readIfExists`-based defaults that did IFD lookups for
`cabal.project.local` / `cabal.project.freeze` in the project
source.  Internal projects (hadrian, ghc-extra-projects) explicitly
set them to `null` just to suppress those reads, and the nullability
also prevented `mkBefore` directives from merging cleanly.

Switch the types to plain `lines` (default `""`) — projects that
relied on the implicit `readFile` behaviour now do it explicitly:

    cabalProjectLocal = builtins.readFile ./cabal.project.local;

Internal callers that set `null` for IFD-avoidance lose the
explicit assignments — the new default is already IFD-free.

**2. Platform-conditional defaults are now injected into every
cabal project's `cabalProjectLocal`.**  Four `mkIf` blocks added:

  * **musl host** — `package * \n executable-static: True`.
    comp-builder adds `--ghc-option=-optl=-static` at build time;
    surfacing the toggle here makes plan-to-nix record
    `--enable-executable-static`.  Build artefacts are unchanged.
  * **x86_64-darwin host** — `package * \n library-for-ghci: True`.
    Mirrors what comp-builder passes for `!ghcjs && !wasm && !android`
    (always true on darwin).
  * **android host** —
    `package * \n ghc-options: -optl-static -optl-ldl` (plus
    `-optl-no-pie` on aarch32).  Mirrors `lib/check.nix`'s
    test-exe `setupBuildFlags` re-wrap.
  * **wasm GHC ≥ 9.12** — `package * \n shared: True`.  Wasm's RTS
    linker only loads `.so` files; `--disable-shared` (cabal's
    default given the real compiler's reported capabilities) would
    force a `.a`-only install that TH-eval can't load.

All four sit at `mkBefore` priority so a project's own
`cabalProjectLocal` overrides them.

Cache impact: plan-nix hashes change for affected platforms.
Internal GHC builds verified byte-identical (drvPath
`na9chax1gj2n6jx3d6v86v6fdah9r9fd-ghc-9.14.1.drv` matches master).

Changelog entry added describing both changes.

Pulled out of #2504 (`hkm/builder-v2`).
Inject platform-conditional cabalProjectLocal defaults
Surface the static-linking / library-for-ghci / shared-libs flags
that comp-builder and `lib/check.nix` already apply at the
artefact level into the plan-to-nix step, so plan-nix's recorded
configure-args and UnitIds match what cabal v2-build would compute
against the real compiler.

Four mkIfs added to `modules/cabal-project.nix`:

* **musl host** — `package * \n executable-static: True`.
  comp-builder adds `--ghc-option=-optl=-static` at build time;
  surfacing the toggle here makes plan-to-nix record
  `--enable-executable-static`.  Build artefacts are unchanged.
* **x86_64-darwin host** — `package * \n library-for-ghci: True`.
  Mirrors what comp-builder passes for `!ghcjs && !wasm && !android`
  (always true on darwin).
* **android host** —
  `package * \n ghc-options: -optl-static -optl-ldl` (plus
  `-optl-no-pie` on aarch32).  Mirrors `lib/check.nix`'s test-exe
  setupBuildFlags re-wrap.
* **wasm GHC ≥ 9.12** — `package * \n shared: True`.  Wasm's RTS
  linker only loads `.so` files; `--disable-shared` (cabal's
  default given the real compiler's reported capabilities) would
  force a `.a`-only install that TH-eval can't load.

All four directives sit at `mkBefore` priority so a project's own
`cabalProjectLocal` overrides them.

Cache impact: plan-nix hashes change for affected platforms.

Changelog entry added in `changelog.md` describing the cache-bust
and the opt-out path.

Pulled out of #2504 (`hkm/builder-v2`).
Add `useLocalGhcLib` project option (#2510)
* Add `useLocalGhcLib` project option

Surface what `modules/configuration-nix.nix` used to do
unconditionally as an opt-in `useLocalGhcLib` flag, so the
`packages.ghc.src` override only fires when a project actually
constrains the `ghc` package (e.g. `ghc-lib-reinstallable`).

Four pieces:

* `modules/project-common.nix`: add the `useLocalGhcLib` option
  (default `false`).
* `modules/configuration-nix.nix`: drop the unconditional
  `packages.ghc.src` / `packages.ghc.package-description-override`
  overrides — they're moved into the per-project wiring below.
* `modules/stack-project.nix`: under `useLocalGhcLib`, re-apply
  the `packages.ghc.src` post-plan override.  Stack-to-nix can't
  use the cabal-project route, so this keeps the existing
  behaviour for stack users who flip the flag.
* `modules/cabal-project.nix`: under `useLocalGhcLib`, inject a
  `source-repository-package` block into `cabalProjectLocal`
  pointing at the configured-src + generated GHC tree, and add an
  `inputMap` entry so haskell.nix doesn't try to fetch the URL.
  Cabal then hashes the wrapped repo's content into
  `pkg-src-sha256` and installs `lib:ghc` like any other
  reinstallable dep.

Projects that need the previous always-on behaviour now set
`useLocalGhcLib = true` on the project module; everyone else gets
a smaller plan-nix and avoids the unconditional `configured-src`
materialisation.

Pulled out of #2504 (`hkm/builder-v2`).

* ghc-lib-reinstallable test + changelog: set useLocalGhcLib = true

Flips the new opt-in flag in both test variants and adds a
changelog entry telling users to do the same when they constrain
`lib:ghc`.
Add `useLocalGhcLib` project option
Surface what `modules/configuration-nix.nix` used to do
unconditionally as an opt-in `useLocalGhcLib` flag, so the
`packages.ghc.src` override only fires when a project actually
constrains the `ghc` package (e.g. `ghc-lib-reinstallable`).

Four pieces:

* `modules/project-common.nix`: add the `useLocalGhcLib` option
  (default `false`).
* `modules/configuration-nix.nix`: drop the unconditional
  `packages.ghc.src` / `packages.ghc.package-description-override`
  overrides — they're moved into the per-project wiring below.
* `modules/stack-project.nix`: under `useLocalGhcLib`, re-apply
  the `packages.ghc.src` post-plan override.  Stack-to-nix can't
  use the cabal-project route, so this keeps the existing
  behaviour for stack users who flip the flag.
* `modules/cabal-project.nix`: under `useLocalGhcLib`, inject a
  `source-repository-package` block into `cabalProjectLocal`
  pointing at the configured-src + generated GHC tree, and add an
  `inputMap` entry so haskell.nix doesn't try to fetch the URL.
  Cabal then hashes the wrapped repo's content into
  `pkg-src-sha256` and installs `lib:ghc` like any other
  reinstallable dep.

Projects that need the previous always-on behaviour now set
`useLocalGhcLib = true` on the project module; everyone else gets
a smaller plan-nix and avoids the unconditional `configured-src`
materialisation.

Pulled out of #2504 (`hkm/builder-v2`).
Extract dummy-ghc, make it cross-aware, and fix plan-nix UnitId stability (#2509)
Cabal-install reads `ghc --info` during plan elaboration to decide
per-unit settings (`--enable-shared` vs `--disable-shared`, RTS-way
inclusion, etc.) that feed into the recorded UnitId hash.  The
inline `dummy-ghc` script in `lib/call-cabal-project-to-nix.nix`
was a stripped-down stub whose `--info` output didn't match the
real cross GHCs', so plan-nix recorded UnitIds that diverged from
what a downstream cabal v2-build (or any consumer running cabal
against the real compiler) would compute.

Three changes:

1. **`lib/dummy-ghc.nix`** (new): extract the dummy-ghc script and
   emit cross-aware `--info` capabilities matching real cross GHCs
   for windows, ghcjs, wasm, android, static, native-musl, and
   native — `Support dynamic-too`, `Support shared libraries`,
   `RTS ways`, `Stage`, `GHC Dynamic`, the iserv-related fields,
   etc.

2. **`lib/call-cabal-project-to-nix.nix`**:
   * gate the dummy-ghc-pkg `-inplace` suffix on GHC ≥ 9.8 — older
     GHCs register pre-existing packages without it, so the dummy
     was synthesising the wrong package ids for plan-to-nix
     against ghc 8.10 / 9.0 / 9.2 / 9.4 / 9.6.
   * pin `CABAL_INSTALLED_PACKAGE_ID_OS` to the build platform's
     OS when running plan-to-nix.  Without this the patched
     cabal-install's installed-package-id format tracks the eval
     system; a darwin host evaluating a linux derivation gets the
     `VeryShort` form and forks UnitIds from what cabal v2-build
     on a linux builder would compute.

3. **`lib/default.nix`**: tighten `uniqueWithNameKey` so
   derivations (`.name = "<pkg>-<ver>"`) and module values
   (`.identifier.{name,version}`) can't collide on shared name
   fragments — partition into `unit-id:` / `id:` / `name:` buckets.
   Correctness is unchanged when buckets are correct; this just
   avoids the slow `lib.unique` fallback in more cases.

Pulled out of #2504 (`hkm/builder-v2`).
Extract dummy-ghc, make it cross-aware, and fix plan-nix UnitId stability
Cabal-install reads `ghc --info` during plan elaboration to decide
per-unit settings (`--enable-shared` vs `--disable-shared`, RTS-way
inclusion, etc.) that feed into the recorded UnitId hash.  The
inline `dummy-ghc` script in `lib/call-cabal-project-to-nix.nix`
was a stripped-down stub whose `--info` output didn't match the
real cross GHCs', so plan-nix recorded UnitIds that diverged from
what a downstream cabal v2-build (or any consumer running cabal
against the real compiler) would compute.

Three changes:

1. **`lib/dummy-ghc.nix`** (new): extract the dummy-ghc script and
   emit cross-aware `--info` capabilities matching real cross GHCs
   for windows, ghcjs, wasm, android, static, native-musl, and
   native — `Support dynamic-too`, `Support shared libraries`,
   `RTS ways`, `Stage`, `GHC Dynamic`, the iserv-related fields,
   etc.

2. **`lib/call-cabal-project-to-nix.nix`**:
   * gate the dummy-ghc-pkg `-inplace` suffix on GHC ≥ 9.8 — older
     GHCs register pre-existing packages without it, so the dummy
     was synthesising the wrong package ids for plan-to-nix
     against ghc 8.10 / 9.0 / 9.2 / 9.4 / 9.6.
   * pin `CABAL_INSTALLED_PACKAGE_ID_OS` to the build platform's
     OS when running plan-to-nix.  Without this the patched
     cabal-install's installed-package-id format tracks the eval
     system; a darwin host evaluating a linux derivation gets the
     `VeryShort` form and forks UnitIds from what cabal v2-build
     on a linux builder would compute.

3. **`lib/default.nix`**: tighten `uniqueWithNameKey` so
   derivations (`.name = "<pkg>-<ver>"`) and module values
   (`.identifier.{name,version}`) can't collide on shared name
   fragments — partition into `unit-id:` / `id:` / `name:` buckets.
   Correctness is unchanged when buckets are correct; this just
   avoids the slow `lib.unique` fallback in more cases.

Pulled out of #2504 (`hkm/builder-v2`).
Extract dummy-ghc, make it cross-aware, and fix plan-nix UnitId stability
Cabal-install reads `ghc --info` during plan elaboration to decide
per-unit settings (`--enable-shared` vs `--disable-shared`, RTS-way
inclusion, etc.) that feed into the recorded UnitId hash.  The
inline `dummy-ghc` script in `lib/call-cabal-project-to-nix.nix`
was a stripped-down stub whose `--info` output didn't match the
real cross GHCs', so plan-nix recorded UnitIds that diverged from
what a downstream cabal v2-build (or any consumer running cabal
against the real compiler) would compute.

Three changes:

1. **`lib/dummy-ghc.nix`** (new): extract the dummy-ghc script and
   emit cross-aware `--info` capabilities matching real cross GHCs
   for windows, ghcjs, wasm, android, static, native-musl, and
   native — `Support dynamic-too`, `Support shared libraries`,
   `RTS ways`, `Stage`, `GHC Dynamic`, the iserv-related fields,
   etc.

2. **`lib/call-cabal-project-to-nix.nix`**:
   * gate the dummy-ghc-pkg `-inplace` suffix on GHC ≥ 9.8 — older
     GHCs register pre-existing packages without it, so the dummy
     was synthesising the wrong package ids for plan-to-nix
     against ghc 8.10 / 9.0 / 9.2 / 9.4 / 9.6.
   * pin `CABAL_INSTALLED_PACKAGE_ID_OS` to the build platform's
     OS when running plan-to-nix.  Without this the patched
     cabal-install's installed-package-id format tracks the eval
     system; a darwin host evaluating a linux derivation gets the
     `VeryShort` form and forks UnitIds from what cabal v2-build
     on a linux builder would compute.

3. **`lib/default.nix`**: tighten `uniqueWithNameKey` so
   derivations (`.name = "<pkg>-<ver>"`) and module values
   (`.identifier.{name,version}`) can't collide on shared name
   fragments — partition into `unit-id:` / `id:` / `name:` buckets.
   Correctness is unchanged when buckets are correct; this just
   avoids the slow `lib.unique` fallback in more cases.

Pulled out of #2504 (`hkm/builder-v2`).
Pick up profiling / coverage / debug-info from plan.json's configure-args (#2508)
`cabal.project` `profiling:` / `library-profiling:` / `coverage:` /
`debug-info:` become `--enable-{profiling,library-profiling,coverage,debug-info}`
in plan.json's per-unit `configure-args` (cabal-install records the
boolean form regardless of level).  Translate those entries back
into haskell.nix module options so a project's cabal.project
stanza flows through to the builder without per-component module
overrides.

Three pieces:

1. `modules/component-options.nix`: surface `enableDWARF` as a
   per-component option (matches `enableProfiling`).
2. `builder/comp-builder.nix`: read the default from
   `component.enableDWARF` instead of hard-coding `false`.  The
   existing branches that swap in the `.dwarf` GHC variant and
   pass `-g3` to ghc then trigger automatically.
3. `modules/install-plan/configure-args.nix`: map
   `--enable-{profiling,library-profiling,coverage,debug-info}` from
   plan.json into `enableProfiling` / `enableLibraryProfiling` /
   `doCoverage` / `enableDWARF` respectively.

Confirmed `tests.cabal-simple-debug.run` on native ghc9141 builds
with `cabal.project` `debug-info: 2` and no module-level toggle.
Pick up profiling / coverage / debug-info from plan.json's configure-args
`cabal.project` `profiling:` / `library-profiling:` / `coverage:` /
`debug-info:` become `--enable-{profiling,library-profiling,coverage,debug-info}`
in plan.json's per-unit `configure-args` (cabal-install records the
boolean form regardless of level).  Translate those entries back
into haskell.nix module options so a project's cabal.project
stanza flows through to the builder without per-component module
overrides.

Three pieces:

1. `modules/component-options.nix`: surface `enableDWARF` as a
   per-component option (matches `enableProfiling`).
2. `builder/comp-builder.nix`: read the default from
   `component.enableDWARF` instead of hard-coding `false`.  The
   existing branches that swap in the `.dwarf` GHC variant and
   pass `-g3` to ghc then trigger automatically.
3. `modules/install-plan/configure-args.nix`: map
   `--enable-{profiling,library-profiling,coverage,debug-info}` from
   plan.json into `enableProfiling` / `enableLibraryProfiling` /
   `doCoverage` / `enableDWARF` respectively.

Confirmed `tests.cabal-simple-debug.run` on native ghc9141 builds
with `cabal.project` `debug-info: 2` and no module-level toggle.
Bug fixes (Windows coverage, native-musl iserv-dyn, aarch64 fPIC, darwin c++abi gate) (#2507)
* comp-builder: fix coverage installPhase hpc-dir lookup on Windows

The doCoverage installPhase tried
`${testExecutable}-tmp/extra-compilation-artifacts` which expands
to `dist/build/<cname>/<cname><exeExt>-tmp/...`.  On Windows hosts
`exeExt = ".exe"`, so the path becomes
`dist/build/<cname>/<cname>.exe-tmp/...` — a path cabal never
creates.  The fallback `cp -r dist/hpc $out/share` then errors with
"No such file or directory" and the slice fails.

cabal's per-component tmp dir is `dist/build/<cname>/<cname>-tmp`
regardless of platform, so use that path directly (and drop the
broken `${testExecutable}-tmp` case — it was only ever right when
`exeExt == ""`, which the new path also covers).

Confirmed `tests.coverage.run` on ucrt64 builds; native still passes.

* ghc-for-component-wrapper: add ghc-iserv-dyn alias on native-musl ≥ 9.10

Native-musl GHC ≥ 9.10 ships `<targetPrefix>ghc-iserv-dyn` and the
wrapper already creates unprefixed aliases for `ghc-iserv` and
`ghc-iserv-prof`, but it was missing `ghc-iserv-dyn`.  Without the
alias, projects compiling profiled + dyn-linked components on
native-musl fail at TH-eval time when ghc looks for
`<topdir>/bin/ghc-iserv-dyn`.

Add the symmetric `ghc-iserv-dyn` alias alongside the existing
ones.

* linux-cross: add -optc-fPIC alongside -fPIC on aarch64

aarch64 cross builds pass `-fPIC` to ghc but the same flag isn't
forwarded to the C compiler.  Some packages compile C sources that
end up needing position-independent code (e.g. when later linked
into shared libraries / TH plugins), and the missing `-fPIC` on
those triggers `relocation R_AARCH64_ADR_PREL_PG_HI21 ... can not
be used when making a shared object` link failures.

Pass `-optc-fPIC` alongside `-fPIC` so ghc forwards it to the C
compiler.

* darwin: gate double-conversion c++abi patch to GHC < 9.4

The `extra-libraries: c++ c++abi` substitution in
`double-conversion.cabal` exists to fix linkage on darwin where
the cabal file's `c++` entry alone is insufficient.  From GHC 9.4
onwards the `system-cxx-std-lib` pseudo-package handles c++abi
linkage automatically, so the patch becomes a no-op there at best
and double-application at worst.

Gate the override on `compiler.version < 9.4` so newer GHCs are
left alone.
darwin: gate double-conversion c++abi patch to GHC < 9.4
The `extra-libraries: c++ c++abi` substitution in
`double-conversion.cabal` exists to fix linkage on darwin where
the cabal file's `c++` entry alone is insufficient.  From GHC 9.4
onwards the `system-cxx-std-lib` pseudo-package handles c++abi
linkage automatically, so the patch becomes a no-op there at best
and double-application at worst.

Gate the override on `compiler.version < 9.4` so newer GHCs are
left alone.
linux-cross: add -optc-fPIC alongside -fPIC on aarch64
aarch64 cross builds pass `-fPIC` to ghc but the same flag isn't
forwarded to the C compiler.  Some packages compile C sources that
end up needing position-independent code (e.g. when later linked
into shared libraries / TH plugins), and the missing `-fPIC` on
those triggers `relocation R_AARCH64_ADR_PREL_PG_HI21 ... can not
be used when making a shared object` link failures.

Pass `-optc-fPIC` alongside `-fPIC` so ghc forwards it to the C
compiler.
ghc-for-component-wrapper: add ghc-iserv-dyn alias on native-musl ≥ 9.10
Native-musl GHC ≥ 9.10 ships `<targetPrefix>ghc-iserv-dyn` and the
wrapper already creates unprefixed aliases for `ghc-iserv` and
`ghc-iserv-prof`, but it was missing `ghc-iserv-dyn`.  Without the
alias, projects compiling profiled + dyn-linked components on
native-musl fail at TH-eval time when ghc looks for
`<topdir>/bin/ghc-iserv-dyn`.

Add the symmetric `ghc-iserv-dyn` alias alongside the existing
ones.
comp-builder: fix coverage installPhase hpc-dir lookup on Windows
The doCoverage installPhase tried
`${testExecutable}-tmp/extra-compilation-artifacts` which expands
to `dist/build/<cname>/<cname><exeExt>-tmp/...`.  On Windows hosts
`exeExt = ".exe"`, so the path becomes
`dist/build/<cname>/<cname>.exe-tmp/...` — a path cabal never
creates.  The fallback `cp -r dist/hpc $out/share` then errors with
"No such file or directory" and the slice fails.

cabal's per-component tmp dir is `dist/build/<cname>/<cname>-tmp`
regardless of platform, so use that path directly (and drop the
broken `${testExecutable}-tmp` case — it was only ever right when
`exeExt == ""`, which the new path also covers).

Confirmed `tests.coverage.run` on ucrt64 builds; native still passes.
Backport v1-affecting changes from hkm/builder-v2
Subset of the v2-builder branch that touches planning code and v1
behaviour, with the v2 builder itself, v2 dev shell, the new tests,
and all related plumbing left out.  Surfaces the v1-visible delta so
we can flag anything users may need to know about before bringing the
full v2 branch in.

Planning-code refactors (shared by v1 and v2):

* lib/dummy-ghc.nix (new): the cabal-install-input dummy `ghc`
  script extracted from lib/call-cabal-project-to-nix.nix.  Now
  emits cross-aware `ghc --info` capabilities (`Support
  dynamic-too`, `RTS ways`, `Support shared libraries`, etc.)
  matching real cross GHCs for windows, ghcjs, wasm, android,
  static, native musl, native.  Cabal feeds these into per-unit
  configuration that ends up in UnitId hashes, so misalignment vs
  the real compiler manifests as plan-to-nix UnitIds differing
  from cabal v2-build's local computation.
* lib/call-cabal-project-to-nix.nix: ports the dummy-ghc-pkg
  `-inplace` suffix gating to GHC ≥ 9.8 (older GHCs register
  pre-existing pkgs without the suffix).  Pins
  `CABAL_INSTALLED_PACKAGE_ID_OS` to the build platform's OS so
  unit-id format stays stable across host OSes.  Accepts an empty
  `cabalProjectLocal` as equivalent to null so platform-specific
  mkIfs in modules/cabal-project.nix can merge without bumping the
  recorded hash.
* modules/cabal-project.nix: adds platform-conditional
  `cabalProjectLocal` defaults — musl `executable-static: True`,
  x86_64-darwin `library-for-ghci: True`, android
  `ghc-options: -optl-static -optl-ldl …`, wasm ≥ 9.12
  `shared: True`.  These flow through plan-to-nix so the recorded
  install plan matches what a downstream cabal v2-build would
  compute; v1 reads what it always read (`-optl-static` already
  comes from comp-builder's per-component configureFlags, etc.) so
  observable behaviour is unchanged outside of UnitId-driven cache
  busts.
* modules/cabal-project.nix: adds the
  `source-repository-package` wiring for the new project-level
  `useLocalGhcLib` option (see project-common changes).
* modules/install-plan/configure-args.nix: translates
  `--enable-profiling` / `--enable-library-profiling` /
  `--enable-coverage` / `--enable-debug-info` from plan.json
  configure-args into per-component option overrides
  (`enableProfiling`, `enableLibraryProfiling`, `doCoverage`,
  `enableDWARF`).  Lets a project set e.g.
  `package <pkg>\n  profiling: True` /
  `debug-info: 2` in cabal.project and have v1 honour it without
  also needing module-level toggles.
* modules/project-common.nix: adds the `useLocalGhcLib` option.
* modules/stack-project.nix: under `useLocalGhcLib`, re-applies the
  `packages.ghc.src` post-plan override that
  modules/configuration-nix.nix used to do unconditionally.
* modules/configuration-nix.nix: drops the unconditional
  `packages.ghc.src` override (moved into the new
  `useLocalGhcLib` path).
* lib/default.nix: corrects the `foldComponents` `libComp` comment
  and tightens `uniqueWithNameKey` so derivations and module
  values can't collide on shared name fragments.

v1 builder fixes:

* modules/component-options.nix + builder/comp-builder.nix:
  surfaces `enableDWARF` as a component option and defaults the
  v1 comp-builder's parameter to `component.enableDWARF`.  Combined
  with the configure-args extraction above, this lets
  `cabal.project` `debug-info:` reach v1's `-g3` /
  `.dwarf` GHC swap.
* builder/comp-builder.nix: fixes the doCoverage installPhase hpc
  dir lookup on Windows hosts.  Previously
  `${testExecutable}-tmp` expanded to `<cname>.exe-tmp` (a path
  cabal never produces); switch to `<cname>/<cname>-tmp`.
* builder/ghc-for-component-wrapper.nix: native-musl wrapper now
  adds the missing `ghc-iserv-dyn` alias alongside `ghc-iserv`,
  `ghc-iserv-prof`, and `unlit`.

Overlay tweaks:

* overlays/linux-cross.nix: aarch64 cross builds add `-optc-fPIC`
  alongside the existing `-fPIC`.
* overlays/darwin.nix: gates the double-conversion `c++abi` patch
  to GHC < 9.4 — newer GHCs handle this via the
  `system-cxx-std-lib` pseudo-package.
* overlays/haskell.nix: routes iserv-proxy build flags through
  the project's `cabalProjectLocal` instead of post-plan
  `.override { setupBuildFlags = ... }`.  Adds
  `-with-rtsopts=--optimistic-linking` for GHC ≥ 9.14 so iserv's
  RTS linker tolerates undefined symbols at TH-eval load time.
  Routes android (`-optl-static -optl-ldl`, `-optl-no-pie` on
  aarch32), windows (`-debug` + image-base flags), and
  aarch64-pre-9.8 (`flags: -threaded`) overrides via cabal.project
  too, so plan-to-nix captures them in configure-args.

Tests are left untouched against master so any v1-visible
behaviour drift surfaces in CI rather than being silently masked
by the v2-branch's adjusted test expectations.