Home / Input Output / haskell.nix
May 03, 1-2 AM (0)
May 03, 2-3 AM (0)
May 03, 3-4 AM (0)
May 03, 4-5 AM (0)
May 03, 5-6 AM (0)
May 03, 6-7 AM (0)
May 03, 7-8 AM (1)
May 03, 8-9 AM (0)
May 03, 9-10 AM (0)
May 03, 10-11 AM (0)
May 03, 11-12 PM (0)
May 03, 12-1 PM (0)
May 03, 1-2 PM (0)
May 03, 2-3 PM (0)
May 03, 3-4 PM (0)
May 03, 4-5 PM (0)
May 03, 5-6 PM (0)
May 03, 6-7 PM (0)
May 03, 7-8 PM (0)
May 03, 8-9 PM (0)
May 03, 9-10 PM (0)
May 03, 10-11 PM (1)
May 03, 11-12 AM (0)
May 04, 12-1 AM (1)
May 04, 1-2 AM (2)
May 04, 2-3 AM (0)
May 04, 3-4 AM (1)
May 04, 4-5 AM (0)
May 04, 5-6 AM (0)
May 04, 6-7 AM (0)
May 04, 7-8 AM (0)
May 04, 8-9 AM (0)
May 04, 9-10 AM (0)
May 04, 10-11 AM (0)
May 04, 11-12 PM (0)
May 04, 12-1 PM (0)
May 04, 1-2 PM (0)
May 04, 2-3 PM (0)
May 04, 3-4 PM (0)
May 04, 4-5 PM (0)
May 04, 5-6 PM (0)
May 04, 6-7 PM (0)
May 04, 7-8 PM (6)
May 04, 8-9 PM (0)
May 04, 9-10 PM (0)
May 04, 10-11 PM (0)
May 04, 11-12 AM (0)
May 05, 12-1 AM (2)
May 05, 1-2 AM (0)
May 05, 2-3 AM (0)
May 05, 3-4 AM (0)
May 05, 4-5 AM (1)
May 05, 5-6 AM (2)
May 05, 6-7 AM (0)
May 05, 7-8 AM (0)
May 05, 8-9 AM (0)
May 05, 9-10 AM (2)
May 05, 10-11 AM (1)
May 05, 11-12 PM (0)
May 05, 12-1 PM (0)
May 05, 1-2 PM (0)
May 05, 2-3 PM (1)
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 (0)
May 10, 1-2 AM (0)
51 commits this week May 03, 2026 - May 10, 2026
cabal-project: inject `executable-static: True` for musl hosts
Plan-to-nix's cabal-install records `--disable-executable-static`
in `configure-args` for every component when the project hasn't
explicitly enabled it.  v1's `builder/comp-builder.nix:384`
papered over this by injecting `--ghc-option=-optl=-static` (and
`-optl=-pthread`) at comp-builder time — outside plan.nix's view.
v1 doesn't enforce UnitId alignment with plan.json, so the
post-plan flag insertion is harmless there.

Under v2 the slice mirrors plan.json's `configure-args` exactly,
so without something at the project level the slice links
dynamically and the `tests.c-ffi.run` musl assertion (`grep "not
a"` on glibc-ldd output) fails.

Inject `executable-static: True` into `cabalProjectLocal` when
`stdenv.hostPlatform.isMusl`.  Plan-to-nix then records
`--enable-executable-static`, v2's
`comp-v2-builder.nix:projectConfigPragmas` round-trips it through,
and cabal links the executable statically.  v1 is unaffected — its
post-plan `-optl=-static` injection still fires, redundant but
harmless.

Verified by running `tests.c-ffi.run` on the
`x86_64-linux.unstable.ghc9141.musl64` jobset
(commit `d027fbe4e` + this patch in a fresh worktree).
Replace post-plan ghc.src override with project-level useLocalGhcLib
`modules/configuration-nix.nix` used to unconditionally `mkForce`
`packages.ghc.src` to a symlinkJoin of `(configured-src + generated)`
under `compiler/`, redirecting `lib:ghc`'s source to the local GHC
tree *after* plan-nix had been computed against hackage's
`ghc-X.Y.Z.tar.gz`.  v1 (Setup.hs) didn't care — it builds whatever
`src` it's given.  v2 forks the slice's UnitId from plan-nix's
recorded one (the slice rebuilds from a different source than the
planner saw) and the slice fails its UnitId check.

Replace with an opt-in project-level flag, `useLocalGhcLib`, that
exposes the GHC compiler tree to the planner up-front.  Mechanism
differs by project type:

  * Cabal projects (modules/cabal-project.nix) inject a
    `source-repository-package` block + `allow-boot-library-installs:
    True` into `cabalProjectLocal`, with an `inputMap` entry keyed
    by `<url>/<ref>` (string-context-stripped) so haskell.nix
    short-circuits the `builtins.fetchGit` path.  Plan-to-nix and
    the v2 slice both see the same wrapped repo, cabal hashes the
    same content into `pkg-src-sha256`, and UnitIds align.

  * Stack projects (modules/stack-project.nix) re-add the
    `packages.ghc.src` override as a contributed module, gated on
    `useLocalGhcLib`.  Stack-to-nix's input shape doesn't fit the
    cabal source-repo path; instead we lean on stack only supporting
    v1 for now (`builderVersion = mkForce 1` in stack-project.nix's
    config) — v1 doesn't enforce UnitId alignment, so the post-plan
    swap is fine.

The option itself lives in `modules/project-common.nix` so both
project types see it without duplication.

`builder/comp-v2-builder.nix` emits `allow-boot-library-installs:
True` in the slice's cabal.project whenever the target or any non-
pre-existing lib dep is on cabal-install's hard-coded
non-reinstallable list (`ghc`, `template-haskell`, `Cabal`,
`Cabal-syntax`, `ghc-prim`, `ghc-bignum`, `ghc-boot`,
`ghc-boot-th`, `ghc-heap`, `base`, `ghci`, `ghc-internal`, `rts`).
Without it the slice's solver rejects the source instance with
"constraint from non-reinstallable package requires installed
instance" once `ghc` is being source-built.

`test/ghc-lib-reinstallable/cabal.nix` and
`test/ghc-lib-reinstallable/stack.nix` opt in via
`useLocalGhcLib = true`.  The stack test's resolver is bumped to
`nightly-2026-05-03` (latest known to the pinned stackage.nix at
the time of this commit).

Verified end-to-end:
  * `tests.ghc-lib-reinstallable-cabal.run` (v2, ghc9141) — passes
  * `tests.ghc-lib-reinstallable-cabal.run` (v1, ghc9141) — passes
  * `tests.ghc-lib-reinstallable-stack.run` (v1, ghc9124) — passes
v2 builder: fix `ghcShim` collision when cross GHC ships unprefixed tools
GHC 9.14.1's `armv7a-android` cross GHC ships *both*
`<prefix>deriveConstants` and an unprefixed `deriveConstants` in
its `bin/` (the latter for build-host use).  `ghcShim`'s
single-pass loop iterated `${ghc}/bin/*` alphabetically, hit
`armv7a-...-deriveConstants` first, and the case branch
synthesised an unprefixed-fallback alias
`$out/bin/deriveConstants -> <prefix>deriveConstants` (the
`[ -e ]` guard was for that fallback, not the raw link).  When
the loop later reached the real `deriveConstants`, the raw
`ln -s "$f" "$out/bin/$base"` crashed with "File exists".

Earlier cross GHCs only shipped `<prefix>deriveConstants`, so
the fallback was the sole producer of `$out/bin/deriveConstants`
and never collided.  GHC 9.14.1 added the unprefixed sibling and
exposed the race.

Switch to a two-pass shape: pass 1 links every source bin/ entry
under its own name, pass 2 synthesises unprefixed aliases only
for prefixed names that have no real unprefixed sibling.  Real
files always win; aliases only fill genuine gaps.

Same fix in both `builder/build-cabal-slice.nix` and
`builder/shell-for-v2.nix` (which carries an identical shim for
the dev shell's cross-cabal wrapper).
v2 builder: expose `.doc` via sibling `cabal v2-haddock` slice
Under v1 every component's `.doc` was a sibling Setup haddock
derivation that shared the lib's UnitId.  Under cabal v2-build
this no longer holds: `cabal v2-haddock` flips
`elabBuildHaddocks` (and the haddock-html / haddock-hscolour /
... family) on every unit's `ElaboratedConfiguredPackage`, which
all land in `pkgHashConfigInputs`.  Calling `cabal v2-haddock`
against a plan that didn't already have `documentation: True`
forks every UnitId in the closure and triggers a from-source
rebuild.

Round-tripping `documentation: True` through plan.json's
`--ghc-option=-haddock` / `configure-args.nix` /
`ghc-options: -haddock` is NOT equivalent: the ghc-option keeps
haddock comments in `.hi` files but doesn't set the haddock-config
booleans, so the slice's `pkgHashConfigInputs` diverges from
plan-nix's and the dep closure's UnitIds fork (observed on
OneTuple in `tests.sublib-docs`: plan-nix `9a847723` vs slice
`f546bd36`).

Approach:

  * `comp-v2-builder.nix` detects per-package `--ghc-option=-haddock`
    in plan.json (the `documentation: True` signal cabal-install
    surfaces) and emits `package <pkg>\n  documentation: True\n`
    in the slice's cabal.project for every documented package.
    `-haddock` is filtered out of the per-pkg `ghc-options:` block
    so cabal doesn't see it twice in `pkgHashGhcOptions`.

  * Each library slice exposes `.doc`, a sibling derivation that
    runs `cabal v2-haddock` against the already-built unit (no
    closure rebuild).  `.doc` throws with a migration hint when
    documentation isn't in the project's plan-json — that's the
    only shape where the UnitIds align.

  * Doc slices propagate `(map d: d.doc)` for `docEnabled` deps
    only, so cross-package hyperlinks resolve.  Mixed projects
    (some packages docs, others not) keep working because non-doc
    deps come in as plain slices.

  * `build-cabal-slice.nix` keeps cabal's native unit-dir layout
    (`$out/store/<ghc>/<unit-id>/share/doc/html/`) so doc slices
    `lndir` into `~/.cabal/store/` as drop-in replacements, and
    cross-package hyperlinks (absolute
    `file:///nix/store/<doc-slice>/store/<ghc>/<dep-uid>/...`)
    resolve back into the slice's own tree.  Non-target unit
    haddocks are stripped from `$out` to keep each slice lean —
    deps' html lives in the deps' own `.doc` slices.

`docs/dev/haddock.md` documents the v2 semantics, the
`documentation: True` requirement, and why there's no
`slice.haddockDir` (local plan-nix UnitIds use `<pkg>-<ver>-inplace`
form while the slice's cabal-store uses cabal's mangled hash form,
so there's no eval-time-stable html path — callers `find` it
under the doc slice).

Verified `tests.sublib-docs.run` builds Lib.html and Slib.html
under `slice.doc`, and OneTuple's UnitId in plan.json matches
plan-nix's recorded id.
plan-nix: gate `-inplace` on GHC ≥ 9.8
GHC 9.8 added an `-inplace` suffix to its boot-package UnitIds
(`base-4.19.2.0-inplace` vs `base-4.18.3.0` on 9.6) and started
emitting a `Project Unit Id` field in `ghc --info`.  The dummy
ghc/ghc-pkg in `lib/call-cabal-project-to-nix.nix` was hardcoding
both, so cabal computed UnitIds against the dummy with
`-inplace` even on GHC 9.6.  When the slice's real ghc 9.6 then
returned ids without `-inplace`, the slice's UnitId for any
package depending on a boot package (`colour` ↔ `base`) diverged
from plan-nix and the slice failed its UnitId check.

Verified empirically that `-inplace` appears starting at GHC 9.8:
  ghc967  → id: base-4.18.3.0
  ghc984  → id: base-4.19.2.0-inplace
  ghc9103 → id: base-4.20.2.0-inplace
  ghc9124 → id: base-4.21.2.0-inplace
  ghc9141 → id: base-4.22.0.0-inplace

Make both the `Project Unit Id` field and the `-inplace` suffix
conditional on `versionAtLeast ghc.version "9.8"`.

Verified against `tests.shell-for.env` on ghc967 (which exercises
the colour slice that was failing in CI).
test: migrate `.profiled` callers to sibling cabal.project
v2 slices no longer ship `.profiled` as an overlay rebuild — see
`docs/dev/profiling.md`.  Migrate the remaining tests that
referenced `…components.<kind>.<name>.profiled`
(`js-template-haskell`, `th-dlls-minimal`, `gi-gtk`) to a sibling
project whose `cabalProjectLocal` enables profiling, matching the
shape already used by `exe-dlls` / `exe-lib-dlls` / `th-dlls`.

Expose each new project's `plan-nix` under a distinct
`ifdInputs` key so the materialised plan-nix expressions are
covered by CI:

  plan-nix             — non-profiled (default)
  plan-nix-ei          — externalInterpreter variant (th-dlls /
                          th-dlls-minimal)
  plan-nix-profiled    — profiled
  plan-nix-profiled-ei — profiled + externalInterpreter
test/with-packages: use \`project.shellFor\` instead of \`comp.shell\`/\`comp.env\`
v2 slices don't expose v1's per-component \`.shell\` / \`.env\`
attributes — \`shell\` is provided at the project level via
\`project.shellFor\`, and that mode covers what this test needs
(a wrapped \`ghc\` / \`runghc\` that can see the package's deps).
\`exposePackagesVia = "ghc-pkg"\` makes \`shell.ghc\` a wrapped
GHC stacking the composed package.db via \`GHC_ENVIRONMENT\`, so
\`runghc ./Point.hs\` and \`ghc Point.hs\` resolve the deps the
same way v1's \`library.env\` did.

Works under both v1 and v2 — no per-component shell required.
v2 builder: trim dist-newstyle/ from slice $out
The slice's `$out/dist-newstyle/` carried cabal's source tarballs
and the build tree — hundreds of MB to GB per slice for nontrivial
projects.  Nothing downstream of a successfully-built slice reads
it: subsequent slices only pull from `$out/store/`, and the
diagnostic `checkAgainstPlan` is a separate derivation with its
own dist-newstyle.

Trim it at the end of `installPhase`, after
`comp-v2-builder.nix`'s test/bench install step (which does need
to find the unpinged binary in `dist-newstyle/build/.../<exe>`).
Lift `cache/plan.json` to `$out/plan.json` so it stays available
for human debugging — the rest of `cache/` is internal.

Verified on the ghc967.ghcjs.hello slice: $out shrinks from
~7.9GB to ~1MB, with `bin/`, `store/`, `plan.json`, `unit-ids`,
and `nix-support/` retained.
v2 builder: drop \`.jsexe/all.js\` exeExt under cabal v2-build
v1 (Setup.hs install) for ghcjs preserves the
\`bin/<exe>.jsexe/all.js\` directory layout, which is why
\`comp-builder.nix:450\` appends \`.jsexe/all.js\` to the exe name
for \`isGhcjs && ghc < 9.8\`.  v2 builds via \`cabal v2-build\`,
whose install step bundles the \`.jsexe/\` contents into a single
self-contained \`#!/usr/bin/env node\` script at \`bin/<cname>\` —
there is no \`.jsexe/\` directory in the slice's output.  Use
the bundled-file path so v2's \`exePath\` / \`find\` lookup land
on the file cabal actually produced.

Verified against \`aarch64-darwin.unstable.ghc967.ghcjs.hello\`
(slice surfaced \`bin/hello\` cleanly, no placeholder fallback).
v2 builder: align slice/check derivation names with v1
Drops the `cabal-slice-` prefix and reorders the slice's `name`
attribute from `<pkg>-<ver>-<ctype>-<cname>` to v1's shape
`<pkg>-<ctype>-<cname>-<ver>` (mirrors `comp-builder.nix:268`).
Same for the auxiliary `check-*` and `store-*` derivations.

Callers that hardcoded v1 derivation names — most prominently
`tests.coverage.run`, which checks for
`tix/pkgb-test-tests-0.1.0.0-check/tests.tix` — keep working
without per-test edits when projects flip
`builderVersion = 1 → 2`.

Also surface the lib slice's `.mix` files under
`<pkg>-<ver>-inplace/<Module>.mix` so they line up with the
inplace UnitId that test-slice-built `tests.tix` files reference
(`Tix [ TixModule "<pkg>-<ver>-inplace/<Module>" … ]`); the .mix
content itself is byte-identical between the lib slice's
hash-named UnitId build and the test slice's inplace rebuild.

Move the coverage-test's `doCoverage` modules into
`cabalProjectLocal` (`package <pkg>\n  coverage: True`) so v2's
plan-nix records `--enable-coverage` and the slice actually
produces .mix/.tix output.  The module-level overrides are kept
for the v1 builder.

Verified against `tests.coverage.run`.
configure-args: pick up profiling/coverage from plan.json
Plan-to-nix's `modules/install-plan/configure-args.nix` already
translates `--ghc-option=` and `--configure-option=` entries from
plan.json's per-pkg `configure-args` into haskell.nix module
options.  Extend it to also pick up the `--enable-{profiling,
library-profiling, coverage}` toggles so v1's `comp-builder`
(which reads `enableProfiling`, `enableLibraryProfiling`,
`doCoverage` straight off the component) honours
`package <pkg>\n  profiling: True` set in cabal.project — without
needing module-level overrides.

v2 already reads these from plan.json directly via its own
`projectConfigPragmas`; the picked-up values are merely
consistent there.

Migrate the `tests.{exe,exe-lib,th}-dlls.{check-,build-}profiled`
variants to a sibling project whose `cabalProjectLocal` enables
profiling — the v2 builder no longer ships `.profiled` as an
overlay rebuild (see `docs/dev/profiling.md`).
v2 builder: error on `.profiled` with migration hint
v1 slices provided `.profiled` as an overlay rebuild with
`enableLibraryProfiling = true`.  v2 reads configure-args from
plan.json, so an overlay would emit `--enable-profiling` toggles
that plan-nix doesn't know about and the slice's UnitId would
diverge.

Replace the (missing) attribute with a `throw` that points users
at `cabal.project` / `cabalProjectLocal`:

    package <pkgname>
      profiling: True
      library-profiling: True

so plan-nix records the toggles and the slice's UnitId stays
aligned.  Set the `throw` at both the top level and inside
`passthru` because mkDerivation only lifts passthru → top level at
derivation creation time; post-hoc `// { passthru = ... }` doesn't
re-lift.

Adds `docs/dev/profiling.md` with the migration recipe.
v2 builder: surface HPC artefacts and src dir for cover.nix
Three incremental improvements toward making `lib/cover.nix` work
against v2 lib/test slices.  The end-to-end coverage test still
fails because v2's lib slices use a hashed UnitId
(`pkgb-0.1.0.0-<hash>`) while a sibling test slice rebuilds the
lib inplace and emits `.tix` referencing `pkgb-0.1.0.0-inplace`,
so HPC can't match them up.  That alignment is a deeper change;
land the supporting plumbing first:

  * Add `srcSubDir` / `srcSubDirPath` to v2 slice passthru so
    `lib/cover.nix:19`'s `map (l: l.srcSubDirPath) mixLibraries`
    no longer hits "attribute missing".

  * Library slices now copy each `extra-compilation-artifacts/hpc/<way>/{mix,tix}`
    subdir up to `$out/share/hpc/<way>/{mix,tix}/<pkg>-<ver>/`
    (the predictable name `lib/cover.nix` and the cover-report
    tests expect — v1 does this too in `comp-builder.nix:831`).

  * v2's `lib/check.nix` branch copies `<exeName>.tix` from the
    test run to `$out/share/hpc/vanilla/tix/<exeName>/`, mirroring
    v1's `lib/check.nix:119`.
v2 dev shell: speed up haskell-nix-cabal-store-sync
The script ran a per-conf `diff -q` and a per-unit `diff -qr` on
every shell entry — for cardano-wallet's ~240 slices that meant
forking hundreds of `diff` invocations and walking each unit's
file tree.  Two short-circuits:

  * Per-composedStore marker file at
    `~/.cabal/store/.haskell-nix-shell-markers/<basename-of-src>`,
    written after a successful run.  On re-entry the marker is
    present and the script exits before the scan.  Different
    shells have distinct basenames, so alternating shells don't
    invalidate each other's markers.

  * Readlink fast-path in the scan: confs and lib files are
    installed as symlinks to `$src/...`, and unit dirs are lndir
    trees of symlinks under `$src/$ghcName/$unitId/`.  When the
    target is already in the right shape, skip the per-file `diff`
    fork.  Only when the cheap check fails does the script fall
    back to `diff -q` / `diff -qr` to detect genuine conflicts.

Measured on cardano-wallet's shell (~240 slices, fresh CABAL_DIR):
first run 24.5s, second run 0.012s.
test/shell-for: use `ghc-pkg` exposure for runghc
The test runs `runghc conduit-test.hs` against `env.ghc` /
`envDefault.ghc`.  With v2's default
`exposePackagesVia = "cabal-store"` only `~/.cabal/store` is
seeded; plain `runghc` doesn't read the cabal store, so it failed
with "Could not find module 'Conduit'".  Switch the three shells
to `"ghc-pkg"` so `env.ghc` is wrapped to stack the composed
package.db via `GHC_ENVIRONMENT`.  Mirrors the earlier fix in
`tests.shell-for-setup-deps`.
test: move HsOpenSSL pointer-types workaround to cabal.project.local
`packages.HsOpenSSL.ghcOptions` previously held a
`-optc=-Wno-incompatible-pointer-types` workaround.  v2's slices
read configure-args from plan.json; module-level `ghcOptions`
never reach plan-nix's plan.json, so under v2 the slice picked up
the flag (via comp-v2-builder's per-package ghcOptions block) but
plan-nix did not — and the resulting UnitIds diverged
(`HsOpnSSL-0.11.7.10-9ebdbccd` expected vs `-ecc34d19` produced).

Move the workaround into `test/cabal.project.local` under
`package HsOpenSSL ghc-options:` so plan-nix and the slice agree.
Drop the now-unneeded `HsOpenSSL` entry from `package-keys` in
`test/modules.nix`.

Verified against `tests.exe-dlls.build`.
test/cabal-sublib: restore ghcjs `broken` marker
Re-enables the `broken = isGhcjs && ghc >= 9.6.1` guard that
commit 19b9a5110 commented out while bringing up v2.  The
underlying failure is in ghcjs/emscripten wasm-ld ("section too
large" linking the `-O`-optimized C-backend objects), which
predates v2 and isn't a slicer issue.  Skip the test on ghcjs
again so CI doesn't fail on a pre-existing upstream limitation.
v2 builder: run hpack on local hpack-using packages
When a project has `supportHpack = true` and a local package
ships only `package.yaml` (no `.cabal`), v1 ran hpack at the
component-build phase to generate the `.cabal`.  v2 never ran
it, so the v2 source tarball had no `.cabal` and the
`v2-exe-repo` pre-build step failed with `tar:
<pkg>-<ver>/<pkg>.cabal: Not found in archive`.

Thread `cabal-generator` through `hspkg-builder.nix` to
`comp-v2-builder.nix` and run hpack inside the staged source
dir before tarballing, mirroring v1's
`comp-builder.nix:485` invocation.  Skipped when the package
isn't local (hackage tarballs already contain a generated
`.cabal`) or when `cabalFile` is set (e.g. an X-revision from
`package-description-override`, which provides its own `.cabal`).

Verified against `tests.cabal-hpack.run`.
v2 builder: support per-package configure-args
Previously `projectConfigPragmas` read configure-args off the
first configured plan entry and emitted them under a single
`package *` block, on the assumption that "the same flags appear
in every configured plan entry's configure-args".  That holds for
project-wide toggles (--enable-optimization, --disable-shared,
...) but breaks the moment a user sets something per-package —
e.g. `package cabal-simple\n  profiling: True`.  The setting only
landed on cabal-simple's plan entry; if the first configured
entry was a transitive dep, the slice's cabal.project never saw
`profiling: True` and the exe built without `-prof`.

Group plan entries by `pkg-name`, take the union of pragmas
across each pkg's units (cabal.project only supports per-package
granularity), then split into:

  * `package *` — pragmas common to *every* pkg-name.  The slice's
    `cabal v2-build` resolves transitive hackage deps fresh, so
    these have to land project-wide for the deps' UnitIds to line
    up with plan-nix.

  * `package <name>` — for each pkg-name with pragmas extending
    the baseline (e.g. `package cabal-simple` getting
    `profiling: True` from
    `packages.cabal-simple.enableProfiling = true` mirrored into
    cabal.project).

`tests.cabal-simple-prof.run` now uses the per-package shape that
mirrors haskell.nix's modules:

  package *
    library-profiling: True
  package cabal-simple
    profiling: True
test/cabal-simple-prof: enable profiling via cabalProjectLocal
The test sets `enableProfiling = true` / `enableLibraryProfiling
= true` via haskell.nix modules.  Under v1 those translate
directly to per-component `--enable-profiling` configure flags,
but under v2 (now the default) the slice reads its toggles from
plan.json's recorded `configure-args` — and plan.json was
generated from cabal.project alone, where neither flag is set.
The slice therefore built without `-prof`, and `+RTS -p` failed
with "the flag -p requires the program to be built with -prof".

Inject `profiling: True` / `library-profiling: True` at project
level via `cabalProjectLocal` so plan-nix records
`--enable-profiling` / `--enable-library-profiling`, matching the
slice's actual build and keeping the slice's UnitId
reproducible.  The module-level overrides are kept so v1 still
builds with profiling too.
test/shell-for-setup-deps: use `ghc-pkg` exposure mode
The test runs `runghc conduit-test.hs` inside the v2 shell; that
needs `Conduit` visible to plain `ghc`/`runghc`.  Default
`exposePackagesVia = "cabal-store"` only seeds `~/.cabal/store`
(which `cabal v2-build` reads), so plain ghc still misses the
setup-deps.  Switch to `"ghc-pkg"` so the wrapped ghc stacks the
composed package.db via `GHC_ENVIRONMENT`.
v2 dev shell: exclude only `selectedPackages`, not all project pkgs
The previous commit treated every project package as "iterated on
from the working tree" and excluded them all from the shell's
cabal store.  That broke `tests.cabal-sublib-shell.run`, which
intentionally selects just `consumer` and expects `provider` (a
sibling project package) to be pre-built — so an unpatched cabal
inside the shell can reuse provider's lib/sublib instead of
rebuilding from source.

Mirror v1's `removeSelectedInputs` semantics: only exclude packages
in `selectedPackages` itself.  With the default
`packages = selectProjectPackages` every project pkg is selected
(so the cardano-wallet UX is unchanged); with an explicit
`packages = ps: [ ps.foo ]`, only `foo` is excluded and sibling
project pkgs land in the store.

Also drops the project-pkg closure walk — `selectedPackages`
already enumerates every unit-id fragment, so a straight
`concatMap getAllComponents` covers all components without the
walk.