Home / Input Output / haskell.nix
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)
May 12, 4-5 PM (0)
May 12, 5-6 PM (0)
May 12, 6-7 PM (0)
May 12, 7-8 PM (0)
May 12, 8-9 PM (0)
May 12, 9-10 PM (0)
May 12, 10-11 PM (1)
May 12, 11-12 AM (0)
May 13, 12-1 AM (3)
May 13, 1-2 AM (0)
May 13, 2-3 AM (0)
May 13, 3-4 AM (0)
May 13, 4-5 AM (0)
May 13, 5-6 AM (2)
May 13, 6-7 AM (0)
May 13, 7-8 AM (0)
May 13, 8-9 AM (0)
May 13, 9-10 AM (0)
May 13, 10-11 AM (0)
May 13, 11-12 PM (0)
May 13, 12-1 PM (0)
May 13, 1-2 PM (0)
May 13, 2-3 PM (0)
May 13, 3-4 PM (0)
May 13, 4-5 PM (0)
May 13, 5-6 PM (0)
May 13, 6-7 PM (0)
May 13, 7-8 PM (0)
May 13, 8-9 PM (0)
May 13, 9-10 PM (0)
May 13, 10-11 PM (0)
May 13, 11-12 AM (0)
May 14, 12-1 AM (2)
May 14, 1-2 AM (0)
May 14, 2-3 AM (0)
May 14, 3-4 AM (1)
May 14, 4-5 AM (0)
May 14, 5-6 AM (0)
May 14, 6-7 AM (2)
May 14, 7-8 AM (0)
May 14, 8-9 AM (1)
May 14, 9-10 AM (0)
May 14, 10-11 AM (0)
May 14, 11-12 PM (0)
May 14, 12-1 PM (0)
May 14, 1-2 PM (0)
May 14, 2-3 PM (0)
May 14, 3-4 PM (0)
May 14, 4-5 PM (0)
May 14, 5-6 PM (0)
May 14, 6-7 PM (0)
May 14, 7-8 PM (0)
May 14, 8-9 PM (0)
May 14, 9-10 PM (0)
May 14, 10-11 PM (0)
May 14, 11-12 AM (0)
May 15, 12-1 AM (1)
May 15, 1-2 AM (0)
May 15, 2-3 AM (0)
May 15, 3-4 AM (0)
May 15, 4-5 AM (2)
May 15, 5-6 AM (0)
May 15, 6-7 AM (0)
May 15, 7-8 AM (0)
May 15, 8-9 AM (0)
May 15, 9-10 AM (0)
May 15, 10-11 AM (1)
May 15, 11-12 PM (1)
May 15, 12-1 PM (0)
May 15, 1-2 PM (0)
May 15, 2-3 PM (2)
May 15, 3-4 PM (0)
May 15, 4-5 PM (0)
May 15, 5-6 PM (0)
May 15, 6-7 PM (0)
May 15, 7-8 PM (0)
May 15, 8-9 PM (0)
May 15, 9-10 PM (0)
May 15, 10-11 PM (0)
May 15, 11-12 AM (0)
May 16, 12-1 AM (1)
May 16, 1-2 AM (0)
May 16, 2-3 AM (0)
May 16, 3-4 AM (0)
May 16, 4-5 AM (0)
May 16, 5-6 AM (0)
May 16, 6-7 AM (0)
May 16, 7-8 AM (0)
May 16, 8-9 AM (0)
May 16, 9-10 AM (0)
May 16, 10-11 AM (0)
May 16, 11-12 PM (0)
May 16, 12-1 PM (0)
May 16, 1-2 PM (0)
May 16, 2-3 PM (0)
May 16, 3-4 PM (0)
May 16, 4-5 PM (0)
May 16, 5-6 PM (0)
May 16, 6-7 PM (0)
May 16, 7-8 PM (0)
May 16, 8-9 PM (0)
May 16, 9-10 PM (0)
May 16, 10-11 PM (0)
May 16, 11-12 AM (0)
May 17, 12-1 AM (1)
May 17, 1-2 AM (0)
May 17, 2-3 AM (0)
May 17, 3-4 AM (0)
May 17, 4-5 AM (0)
May 17, 5-6 AM (0)
May 17, 6-7 AM (0)
May 17, 7-8 AM (0)
May 17, 8-9 AM (0)
May 17, 9-10 AM (1)
May 17, 10-11 AM (2)
May 17, 11-12 PM (0)
May 17, 12-1 PM (2)
May 17, 1-2 PM (0)
May 17, 2-3 PM (0)
May 17, 3-4 PM (0)
May 17, 4-5 PM (0)
May 17, 5-6 PM (0)
May 17, 6-7 PM (0)
May 17, 7-8 PM (0)
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)
58 commits this week May 11, 2026 - May 18, 2026
cabal-sublib-shell test: add musl `package * executable-static: True`, drop `with-compiler:`
Two changes:

* musl64 needs `executable-static: True` in `package *` to match
  the slice's prebuilt provider unit-ids.  The slice emits this
  inside `package *` (via `comp-v2-builder.nix`'s pragmasOf), which
  cabal hashes as `pkgHashFullyStaticExe` for every component —
  libraries included.  Without it the test's rebuilt provider lib's
  `cabal-hash.txt` is missing the `fully-static-exe: True` line and
  the unit-id forks from the slice.

* Drop the explicit `with-compiler: ghc-<version>` line.  The
  `${prefix}cabal` wrapper already passes `--with-compiler=` on
  the CLI, so the project-level entry is redundant — and on cross
  targets it was actively wrong (slice writes `<prefix>ghc`, test
  was writing `ghc-<version>`).  Empirically the line wasn't
  needed for unit-id alignment either; both wasi32 and musl64
  tests now reuse the shell's slices without it.

Verified locally on `x86_64-linux.unstable.ghc9141.musl64` and
`aarch64-darwin.unstable.ghc9141.wasi32`.
js-template-haskell, th-dlls: disable on android-aarch32 for ghc9124, ghc9141
iserv-proxy-interpreter segfaults under qemu-arm during TH eval on
android-aarch32 with these GHCs:

  qemu: uncaught target signal 11 (Segmentation fault) - core dumped
  iserv-proxy: hFlush: resource vanished (Broken pipe)
  External interpreter terminated (1)

Same failure mode the existing `ghc9102 / ghc9103` exclusion catches.
Extend the exclusion to ghc9124 and ghc9141 (and their llvm
variants) so these TH-using tests don't fail on this fragile combo
while the underlying qemu / iserv interaction is investigated.

(`th-dlls-minimal` is gated to Windows only and is unaffected.)
v2 builder: platform-specific cabal.project pragmas via rawCabalProject
Two platform-specific pragmas injected directly into the
`rawCabalProject` text in `lib/call-cabal-project-to-nix.nix` —
routed here rather than via the `cabalProjectLocal` module option
so we avoid a `nullOr lines` merge conflict against the internal
ghc-extra-projects / hadrian-plan setups that explicitly set
`cabalProjectLocal = null` to skip IFD reads.

  * x86_64-darwin → `library-for-ghci: True`

    v1 builds darwin with `--enable-library-for-ghci` (default for
    !ghcjs !wasm !android in `comp-builder.nix:383`), so cabal emits
    a merged `HS<unit>.o` per unit.  v2 mirrors plan.json's
    `--disable-library-for-ghci` default, so the merged `.o` is
    missing.  Under TH-eval / `-fexternal-interpreter` ghc-iserv-dyn
    then loads each dep's `.dylib` via dyld; on x86_64-darwin running
    under Rosetta (Hydra builds x86_64-darwin on aarch64-darwin
    hardware), dyld misbehaves on certain dylibs (libtext,
    libdouble-conversion, libHsOpenSSL) and iserv terminates with
    SIGBUS (`External interpreter terminated (-10)`).  Forcing
    `library-for-ghci: True` gives ghc-iserv the merged `.o` to load
    directly, bypassing dyld.  Scoped to x86_64 only — aarch64-darwin
    runs natively.

  * Android → `ghc-options: -optl-static -optl-ldl [-optl-no-pie]`

    v1's `lib/check.nix:68` re-overrides the test exe on Android
    with `setupBuildFlags = ["--ghc-option=-optl-static"]` so
    qemu-user can run it on the build host (a dynamic Android
    binary references `/system/bin/linker[64]` at runtime, which
    qemu-arm/aarch64 fails to open because the build host doesn't
    ship one).  v2 ignores `setupBuildFlags` (slices are immutable),
    so route the equivalent flags through cabal.project for every
    package, mirroring the iserv-proxy Android override in
    `overlays/haskell.nix:1175`.  `-optl-ldl` pulls in libdl that
    GHC's RTS still references even under a static link; aarch32
    additionally needs `-optl-no-pie` to disable PIE so the static
    link doesn't trip `dynamic STT_GNU_IFUNC` relocation errors.

`overlays/darwin.nix`: gate the `double-conversion preConfigure`
cabal-file patch to GHC < 9.4.  v2 doesn't honour `preConfigure`
anyway, and GHC >= 9.4 handles the c++/c++abi link via the
`system-cxx-std-lib` pseudo-package, so the patch is a no-op on
modern GHCs.

Platform detection uses `pkgs.stdenv.targetPlatform` because
`pkgs` here is `final.buildPackages.pkgs` (build-platform pkgs),
whose `hostPlatform` always reads as the build host.  The rest of
this file already follows the same `targetPlatform` convention for
ghcjs / windows / etc.
v2 builder: enable library-for-ghci on x86_64-darwin so iserv avoids dyld
v1 builds darwin with `--enable-library-for-ghci` (default for
!ghcjs !wasm !android in `comp-builder.nix:383`), which makes cabal
emit a merged `HS<unit>.o` per unit.  v2 mirrors plan.json's
configure-args, where cabal defaults to `--disable-library-for-ghci`,
so the merged `.o` is missing.  Under TH-eval `/-fexternal-interpreter`
ghc-iserv-dyn then loads each dep's `.dylib` via dyld; on x86_64-darwin
running under Rosetta (Hydra builds x86_64-darwin on aarch64-darwin
hardware), dyld misbehaves on certain dylibs (libtext,
libdouble-conversion, libHsOpenSSL) and iserv terminates with SIGBUS
(`External interpreter terminated (-10)`).

Inject `package *; library-for-ghci: True` via cabalProjectLocal for
x86_64-darwin hosts so plan-to-nix records `--enable-library-for-ghci`
for every package; the slice then produces the merged `HS<unit>.o`
that ghc-iserv loads directly, bypassing dyld.  Project-level
wildcard (`package *`) is needed because plain `library-for-ghci: True`
at the top of cabal.project only applies to local packages, not deps.

Scoped to x86_64-darwin only — aarch64-darwin runs natively and
already builds these tests cleanly without the merged `.o`.
Restricting scope also limits the cache invalidation to the
x86_64-darwin slice closure.

Drive-by fixes needed to make the mkIf merge through:

- `overlays/ghc-packages.nix` and `compiler/ghc/default.nix` set
  `cabalProjectLocal = null` to opt out of disk reads, but `nullOr lines`
  refuses to merge `null` with a string definition.  Switch both to
  `""` — same "no extra content" effect, but compatible with the
  cabalProjectLocal mkIfs (this one and any future platform-specific
  injections).

- `overlays/darwin.nix` gates the `double-conversion preConfigure`
  cabal-file patch to GHC < 9.4.  v2 doesn't honour `preConfigure`
  anyway, and GHC >= 9.4 handles the c++/c++abi link via the
  `system-cxx-std-lib` pseudo-package, so the patch is a no-op on
  modern GHCs.
cabal-sublib-shell test: emit `shared: True` on wasm 9.12+
For wasm 9.12+ the slice's `comp-v2-builder` rewrites cabal's
`--disable-shared` to `shared: True` in cabal.project (commit
34e20fcd7), so the slice's provider unit is hashed with
`shared-lib: True`.  The test's bare cabal.project omitted the
override, so unpatched cabal computed unit-ids with
`shared-lib: False` and rebuilt provider from source instead of
reusing the shell's prebuilt slice — failing the
"verifying solver reused provider" check.

Mirror the wasm shared override in the test's cabal.project so
cabal's unit-id matches the slice's, and the shell-installed
provider is picked up directly.
v2 doc slice: install sublib haddock html into unit-store layout
cabal `v2-haddock` installs the main library's html into the
unit-store `<unit>/share/doc/html/`, but for sublibraries it leaves
the html only under the per-package working dist dir at
`dist-newstyle/tmp/src-*/<pkg>-<ver>/dist/doc/html/<pkg>/<sublib>/`
and then wipes that tmp dir before the slice's `installPhase` runs.

Without this fix, a sublib's `.doc` slice ships an empty
`share/doc/html/`, and `find <doc-slice>/share/doc/html/*Slib.html`
returns nothing in the sublib-docs test.

Capture the html during buildPhase (right after `v2-haddock`), find
the sublib's just-registered unit by the cabal-mangled
`z-<pkg>-z-<sublib>` conf name, and copy `dist/doc/html/<pkg>/<sublib>/`
into the unit's `share/doc/html/` so the doc slice matches the
main-lib layout for downstream consumers.
v2 shell: propagate ghc's deps so wasm cc-wrapper finds libffi
The slice path puts the raw `ghc` in its `nativeBuildInputs`
alongside the `makeGhcShim` shim, so nixpkgs splicing pulls in
ghc's `depsTargetTargetPropagated` — notably `libffi-wasm.out` for
wasm32-wasi-ghc.  The wasm cc-wrapper then sees the libffi in its
target buildInput list and adds `-L<libffi-wasm>/lib` to
`NIX_LDFLAGS_FOR_TARGET`, so `wasm-ld -lffi` resolves at link time.

The v2 shell put only the shim in `nativeBuildInputs` — and the
shim is a `runCommand` drv with no propagation chain of its own,
so the libffi-wasm path never reached the wasm cc-wrapper.  A
plain `cabal v2-build` inside a wasi32 shell then failed with
`wasm-ld: error: unable to find library -lffi` (eval 1533's
`wasi32.tests.cabal-sublib-shell.run` regression).

Mirror the slice's pattern by also including the raw `ghc` in the
shell's `nativeBuildInputs`.  The shim is still primary for PATH
and `--with-compiler=` purposes; the raw ghc only contributes its
propagation chain.
v2 githash test: synthesise minimal package-root with .git for v2 slice
v1's comp-builder cp'd the entire projectRoot tree (with `.git` at the
top) into the build env and cd'd to `test/githash/`, so `tGitInfoCwd`
found the parent `.git` by walking up.  v2's slice tarballs only the
package subdir, so the parent `.git` is lost.

Add a `packageRoot` drv that flattens `test/githash/` to its own root
and copies just `HEAD` + `refs/` + `objects/` from `projectRoot/.git`
beside it — enough for the `rev-parse` / `log` / `rev-list` queries
githash issues.  Wrap `src` with `{ outPath; filterPath }` so
haskell.nix's default source cleaner doesn't strip `.git`.  Drop the
v1-only `packages.githash-test.src = mkForce { origSrc; origSubDir; }`
module override since both builders now see a normal package source.
iserv-proxy: build prof variant via cabal.project profiling toggle (v2)
v1's `iserv-proxy-interpreter.override { enableProfiling = true; }` works because v1's `comp-builder.nix:71,212,272` reads `enableProfiling` from component options and passes `--enable-profiling` to Setup configure. v2's `modules/install-plan/configure-args.nix:86-87` only reads profiling from plan-nix's recorded configure-args; it doesn't consult `component.enableProfiling`.

Net effect: in v2, `iserv-proxy-interpreter-prof` was built with the SAME RTS as `iserv-proxy-interpreter` (no profiling). When GHC selects the prof iserv at TH-eval time (by appending `-prof` to `-pgmi`), the wrapper spawns a binary that lacks profiling-RTS symbols like `registerCcList`, and loading `.p_o` modules into the interpreter fails with `Failed to lookup symbol: registerCcList`.

Fix: build the iserv-proxy project twice — once with vanilla cabal.project (for `iserv-proxy` and `iserv-proxy-interpreter`), once with `package iserv-proxy profiling: True` added to cabalProjectLocal so plan-nix records `--enable-profiling` and v2 honours it. Keep `enableProfiling = true` on the override too so v1 still works.

Verified on x86_64-linux.unstable.ghc9141.aarch64-multiplatform.tests.th-dlls.build-profiled.
v2 builder: extract makeGhcShim, share between slice and shell
Move the ghc-shim construction (cabal near-compiler aliases, ghcjs `ar command` settings patch, native-musl unlit/iserv aliases) out of `build-cabal-slice.nix` into `builder/ghc-shim.nix` so the v2 shell can use the same base.

`shell-for-v2.nix` previously did:

* cabal-store mode: `shellGhc = ghc` (raw) — no aliases, no `-B<libdir>` for musl, no ghcjs settings patch.  A `.lhs` compile or TH eval in this shell on musl ≥ 9.10 / ghcjs would hit exactly the same failures the slice did before the recent fixes.

* ghc-pkg mode: a `wrappedGhc` that took raw `ghc/bin/*` and wrapped just `ghc{,i,-version,i-version,-pkg}` / `runghc{,haskell}` / `haddock` with `GHC_ENVIRONMENT` / `GHC_PACKAGE_PATH` env vars.  Same alias gap.

Now both modes build on `baseShim = makeGhcShim { inherit ghc; }` (same derivation `build-cabal-slice` points `--with-compiler=` at):

* cabal-store mode: `shellGhc = baseShim` — byte-identical to the slice's ghc.

* ghc-pkg mode: `wrappedGhc` layers makeWrapper env-var wrappers on the shim's bins.  Wrapping a wrapper is fine — the outer wrapper exec's `baseShim/bin/...` which exec's real ghc with `-B<libdir>` (when applicable).  Symlinks every non-wrapped shim binary into `$out/bin/` so PATH still finds alex/happy/hsc2hs/unlit/etc.

No semantic change to the slice's behaviour; shell now inherits the alias fixes.

Verified on aarch64-darwin.unstable.ghc967.native.tests.shell-for-setup-deps.run.
ghc-shim: add unlit / ghc-iserv-{,dyn,prof} aliases for native-musl ≥ 9.10
Mirror v1's `ghc-for-component-wrapper.nix:136-139` workaround in v2's slice builder, and add `ghc-iserv-dyn` to v1.

Native-musl cross-ghc ≥ 9.10 ships only `<prefix>unlit` / `<prefix>ghc-iserv[-dyn|-prof]` in its `bin/` (no unprefixed alias).  GHC's literate / iserv lookup is relative to its own binary path (`<topdir>/bin/unlit`), so without an unprefixed alias under `<topdir>/bin/` it fails with `Literate pre-processor: could not execute …/bin/unlit`.

Fix in `build-cabal-slice.nix`: on native-musl ≥ 9.10, lndir real ghc's libdir into the shim, wrap the ghc binaries with `-B$out/<libRel>` (computed from `--print-libdir`), and add unprefixed aliases for `unlit` + `ghc-iserv*`.  The aliases are guarded on existence so a ghc lacking `ghc-iserv-dyn` (ghc967, ghc984) skips that one alias rather than dangling.

Fix in `ghc-for-component-wrapper.nix`: add `ghc-iserv-dyn` to the existing alias block.

Surveyed real ghc bin/ across ghc967, ghc984, ghc9103, ghc9124, ghc9141 musl64: 9.10+ have all four binaries; 9.6 / 9.8 have none (and their `>9.9` / `>=9.10` gate doesn't fire, so they don't try).

Verified passing on x86_64-linux.unstable.ghc9103.musl64.tests.literate-haskell.build.
shell-for-v2: omit -inplace suffix from ghcDirName for GHC < 9.8
cabal's per-compiler store dir is `ghc-<version>-<abi>`. GHC ≥ 9.8 reports `Project Unit Id: ghc-<version>-inplace` from which cabal infers `compiler-abi: inplace`; earlier GHCs omit the field and cabal records an empty abi (so the dir is just `ghc-<version>`).

shell-for-v2 hardcoded `ghc-<version>-inplace`, which mismatches the actual lndir-composed `ghc-<version>` dir for GHC < 9.8 — `tests.shell-for-setup-deps.run` on ghc967 failed with `ghc-pkg-9.6.7: /shell-store/ghc-9.6.7-inplace/package.db: getDirectoryContents:openDirStream: does not exist`.

Mirror dummy-ghc's own version gate (`pkgs.lib.versionAtLeast ghc.version "9.8"`) when picking the suffix. Verified on aarch64-darwin.unstable.ghc967.native.tests.shell-for-setup-deps.run.
dummy-ghc: version-gate `target RTS linker only supports shared libraries`
Real GHC < 9.12 omits this field entirely across every target surveyed (mingw, aarch64-multiplatform, musl64, native); GHC ≥ 9.12 emits "YES" on wasm and "NO" elsewhere. The dummy was unconditionally emitting "NO" on every non-wasm branch (and used a free-standing "YES"/"NO" toggle on the wasm/ghcjs branch), so the dummy-ghc-info diff test now trips for ghc984.ucrt64 etc. after the recent un-ignoring of this field.

Gate the emission on `builtins.compareVersions ghc.version "9.12" >= 0` per branch; keep the "YES vs NO" choice for the wasm branch.

Verified passing on: aarch64-darwin.unstable.ghc984.ucrt64, aarch64-darwin.unstable.ghc9124.wasi32, aarch64-darwin.unstable.ghc9124.native.
v2 builder + dummy-ghc: enable shared libs for wasm 9.12+ so TH works
Wasm 9.12+ GHC's RTS linker only supports shared libraries, and TH-eval's dyld needs a `.so` for every transitively reachable lib. Three connected fixes:

* `lib/dummy-ghc.nix`: real wasm 9.12+ GHC reports `target RTS linker only supports shared libraries: YES`; dummy was lying with `NO`. Per-version-gated so 9.6 / 9.8 / 9.10 wasm keep the legacy value.

* `test/dummy-ghc-info/default.nix`: un-ignore `target RTS linker only supports shared libraries` so the diff test catches this kind of divergence going forward. The prior comment claimed cabal didn't consult the field — that's true for the `pkgHashConfigInputs` hashing step, but the field still matters for shared-vs-static elaboration and is load-bearing for any other check that compares dummy vs real.

* `builder/comp-v2-builder.nix`: cabal's `--enable-shared` decision is driven by the `Support shared libraries` field (which real wasm GHC doesn't set), so plan-nix still records `--disable-shared` for wasm packages even with the dummy fix. v1 papered over this with the `enableShared || isWasm` clause in `comp-builder.nix:44`; v2 reads `configure-args` straight from plan-nix, so override the resulting pragma here to `shared: True` for wasm ≥ 9.12. Without this, TH-evaluating modules (e.g. `th-orphans`) fail at compile time with `dyld.findSystemLibrary(libHS…-…so): not found` plus `wasm-ld: error: unable to find library -lHS…-ghc<v>`.

* `builder/build-cabal-slice.nix`: add `gawk` to nativeBuildInputs. Darwin's stdenv `setup.sh` calls `awk` during fixup but darwin's default sandbox PATH doesn't carry it; build-cabal-slice's own diagnostics also use `awk`.

Verified on `aarch64-darwin.unstable.ghc9124.wasi32.tests.js-template-haskell.build` and `aarch64-darwin.unstable.ghc9124.{wasi32,native}.tests.dummy-ghc-info`.
v2 builder: handle Custom-Build / local packages where plan-nix's uid is a placeholder
Custom-Build packages collapse every component (lib / sublib / exe / setup) into a single plan-nix entry — plan-nix marks the shared entry by leaving `component-name` unset. cabal-install installs every component under one shared `<pkg>-<ver>-<hash>/` directory and writes a single `.conf`. With a per-component target (`:pkg:<pkg>:lib:<lib>`) the lib slice's `cabal v2-build` builds only the library yet still drops the package-wide `.conf` into `package.db`. A downstream exe slice that lndir-composes this then sees the `.conf`, concludes the unit is already installed, and reports "Up to date" without building the exe.

Compounding this, plan-nix records local packages (those listed in `packages:`) as `style: "local"` with the placeholder uid `<pkg>-<ver>-inplace`. The slice serves the same source as a tarball repo (`style: "global"` / `pkg-src: repo-tar`) and cabal computes a content-hashed real uid like `pkg-0.1.0.0-6b5595c2`. plan-nix's uid can never match cabal's — so the previous `expectedUnitId`-based cleanup, which kept only the plan-nix uid's dir, wiped out cabal's freshly installed real unit.

Two fixes:

* `comp-v2-builder.nix`: when plan-nix marks the package as Custom-Build (`component-name == null`), expand `targetSelector` to `:pkg:<pkg>` so every slice's `cabal v2-build` materialises the full shared-uid unit (lib + every exe). Downstream slices that lndir-compose the output then get the `bin/` they need.

* `build-cabal-slice.nix`: when `expectedUnitId` is null, fall back to the captured uids cabal actually installed in this slice (read from `$buildRoot/captured-unit-ids`) for the trim-keep set. If captured-uids is empty (cabal short-circuited on "Up to date" because a dep slice already supplied everything), skip the trim entirely so `kindSpecificInstallPhase` can still find `bin/<exeName>` in the lndir-composed dep-slice content.

Verified on `aarch64-darwin.unstable.ghc9124.native.tests.setup-deps.run` (with and without the library component) and `aarch64-darwin.unstable.ghc9141.native.tests.setup-deps.run`.
v2 builder: filter missing .o files for ghcjs backpack ar archives
When cabal v2-build instantiates a backpack signature on ghcjs, GHC's JS backend doesn't always emit a `.o` for the signature module — the signature is filled by an external implementation unit. Cabal then asks `emar` to archive both `Module.o` and `Consumer.o`, and `llvm-ar` aborts on the missing `Module.o`.

v1 had a wrapper in `comp-builder.nix` for this case but its gate (`instantiations != {}`) only fires on the indefinite library drv, never on the exe drv whose internal `cabal v2-build` rebuilds the sibling instantiated lib. v2 (cabal v2-build everywhere) never matches that gate at all.

Fix in `build-cabal-slice.nix`: on ghcjs, extend `ghcShim` with an lndir mirror of GHC's libdir, materialise a writable `settings` file, swap the `ar command` entry to a wrapper that drops missing `.o` entries (both inline argv and response-file lines), and wrap `ghc`/`ghc-<v>` with `makeWrapper --add-flags "-B<patched libdir>"` so they pick up the patched settings. `--with-ar=` would be the natural lever, but cabal v2-build's internal "Building library" reads `ar command` from GHC's settings instead.
iserv-proxy: bake --optimistic-linking via -with-rtsopts (GHC ≥9.14)
Add `-with-rtsopts=--optimistic-linking` to iserv-proxy's cabalProjectLocal so the RTS option is embedded in `main.c` at link time and processed with `RtsOptsAll` — bypassing the OPTION_UNSAFE gate that command-line `+RTS --optimistic-linking -RTS` is subject to. The wrapper scripts under `overlays/{linux-cross,mingw_w64}.nix` no longer need to pass it at invocation. Gated on `impl(ghc >=9.14)` since earlier RTS versions don't recognise the flag.