Home /
Input Output /
haskell.nix
May 27, 5-6 AM (0)
May 27, 6-7 AM (0)
May 27, 7-8 AM (0)
May 27, 8-9 AM (0)
May 27, 9-10 AM (0)
May 27, 10-11 AM (0)
May 27, 11-12 PM (0)
May 27, 12-1 PM (0)
May 27, 1-2 PM (0)
May 27, 2-3 PM (0)
May 27, 3-4 PM (0)
May 27, 4-5 PM (0)
May 27, 5-6 PM (0)
May 27, 6-7 PM (0)
May 27, 7-8 PM (0)
May 27, 8-9 PM (0)
May 27, 9-10 PM (0)
May 27, 10-11 PM (0)
May 27, 11-12 AM (0)
May 28, 12-1 AM (1)
May 28, 1-2 AM (1)
May 28, 2-3 AM (0)
May 28, 3-4 AM (0)
May 28, 4-5 AM (0)
May 28, 5-6 AM (0)
May 28, 6-7 AM (0)
May 28, 7-8 AM (0)
May 28, 8-9 AM (0)
May 28, 9-10 AM (0)
May 28, 10-11 AM (2)
May 28, 11-12 PM (1)
May 28, 12-1 PM (0)
May 28, 1-2 PM (0)
May 28, 2-3 PM (0)
May 28, 3-4 PM (0)
May 28, 4-5 PM (0)
May 28, 5-6 PM (0)
May 28, 6-7 PM (0)
May 28, 7-8 PM (0)
May 28, 8-9 PM (0)
May 28, 9-10 PM (0)
May 28, 10-11 PM (0)
May 28, 11-12 AM (0)
May 29, 12-1 AM (2)
May 29, 1-2 AM (0)
May 29, 2-3 AM (0)
May 29, 3-4 AM (0)
May 29, 4-5 AM (0)
May 29, 5-6 AM (0)
May 29, 6-7 AM (0)
May 29, 7-8 AM (0)
May 29, 8-9 AM (0)
May 29, 9-10 AM (0)
May 29, 10-11 AM (0)
May 29, 11-12 PM (0)
May 29, 12-1 PM (0)
May 29, 1-2 PM (0)
May 29, 2-3 PM (0)
May 29, 3-4 PM (0)
May 29, 4-5 PM (0)
May 29, 5-6 PM (0)
May 29, 6-7 PM (0)
May 29, 7-8 PM (0)
May 29, 8-9 PM (0)
May 29, 9-10 PM (0)
May 29, 10-11 PM (0)
May 29, 11-12 AM (0)
May 30, 12-1 AM (1)
May 30, 1-2 AM (0)
May 30, 2-3 AM (0)
May 30, 3-4 AM (0)
May 30, 4-5 AM (0)
May 30, 5-6 AM (0)
May 30, 6-7 AM (0)
May 30, 7-8 AM (0)
May 30, 8-9 AM (0)
May 30, 9-10 AM (0)
May 30, 10-11 AM (0)
May 30, 11-12 PM (0)
May 30, 12-1 PM (0)
May 30, 1-2 PM (0)
May 30, 2-3 PM (0)
May 30, 3-4 PM (0)
May 30, 4-5 PM (0)
May 30, 5-6 PM (0)
May 30, 6-7 PM (0)
May 30, 7-8 PM (0)
May 30, 8-9 PM (0)
May 30, 9-10 PM (0)
May 30, 10-11 PM (0)
May 30, 11-12 AM (0)
May 31, 12-1 AM (1)
May 31, 1-2 AM (0)
May 31, 2-3 AM (0)
May 31, 3-4 AM (0)
May 31, 4-5 AM (0)
May 31, 5-6 AM (0)
May 31, 6-7 AM (0)
May 31, 7-8 AM (0)
May 31, 8-9 AM (0)
May 31, 9-10 AM (0)
May 31, 10-11 AM (0)
May 31, 11-12 PM (0)
May 31, 12-1 PM (0)
May 31, 1-2 PM (0)
May 31, 2-3 PM (0)
May 31, 3-4 PM (0)
May 31, 4-5 PM (0)
May 31, 5-6 PM (0)
May 31, 6-7 PM (0)
May 31, 7-8 PM (0)
May 31, 8-9 PM (0)
May 31, 9-10 PM (0)
May 31, 10-11 PM (0)
May 31, 11-12 AM (0)
Jun 01, 12-1 AM (1)
Jun 01, 1-2 AM (0)
Jun 01, 2-3 AM (0)
Jun 01, 3-4 AM (0)
Jun 01, 4-5 AM (0)
Jun 01, 5-6 AM (0)
Jun 01, 6-7 AM (0)
Jun 01, 7-8 AM (0)
Jun 01, 8-9 AM (0)
Jun 01, 9-10 AM (0)
Jun 01, 10-11 AM (0)
Jun 01, 11-12 PM (0)
Jun 01, 12-1 PM (0)
Jun 01, 1-2 PM (0)
Jun 01, 2-3 PM (0)
Jun 01, 3-4 PM (0)
Jun 01, 4-5 PM (0)
Jun 01, 5-6 PM (0)
Jun 01, 6-7 PM (0)
Jun 01, 7-8 PM (0)
Jun 01, 8-9 PM (0)
Jun 01, 9-10 PM (0)
Jun 01, 10-11 PM (0)
Jun 01, 11-12 AM (0)
Jun 02, 12-1 AM (2)
Jun 02, 1-2 AM (0)
Jun 02, 2-3 AM (0)
Jun 02, 3-4 AM (0)
Jun 02, 4-5 AM (2)
Jun 02, 5-6 AM (0)
Jun 02, 6-7 AM (0)
Jun 02, 7-8 AM (7)
Jun 02, 8-9 AM (1)
Jun 02, 9-10 AM (0)
Jun 02, 10-11 AM (0)
Jun 02, 11-12 PM (0)
Jun 02, 12-1 PM (2)
Jun 02, 1-2 PM (1)
Jun 02, 2-3 PM (0)
Jun 02, 3-4 PM (0)
Jun 02, 4-5 PM (0)
Jun 02, 5-6 PM (0)
Jun 02, 6-7 PM (0)
Jun 02, 7-8 PM (0)
Jun 02, 8-9 PM (0)
Jun 02, 9-10 PM (0)
Jun 02, 10-11 PM (2)
Jun 02, 11-12 AM (0)
Jun 03, 12-1 AM (1)
Jun 03, 1-2 AM (0)
Jun 03, 2-3 AM (0)
Jun 03, 3-4 AM (0)
Jun 03, 4-5 AM (0)
Jun 03, 5-6 AM (0)
28 commits this week
May 27, 2026
-
Jun 03, 2026
DEBUG: dump full Setup configure argv for drifting packages in checkAgainstPlan
DEBUG: trace v2 build-tool buildSlice resolution (lookupDepPkg vs bare attr)
v2: version-pin the full installed closure, not just the lib-dep closure
A test/bench slice (or any component that reaches a dep only through its own package's library or a sibling component, rather than through component.depends) seeds the blocks closure (blkFrags / transitive-deps) but not the lib-dep closure (libFrags / lib-dep-slices). The composed cabal.project only emitted version pins from libFrags, so those deps were installed-but-unpinned. With allow-older + allow-boot-library- installs the solver was then free to re-solve a reinstalled boot library (e.g. text 2.1.4 -> GHC-bundled text 2.1.3), drifting every downstream UnitId and failing checkAgainstPlan. Emit a bare '<pkg> ==<ver>' pin for every unit in the installed (blkFrags) closure; keep extra-packages and the 'source' qualifier scoped to libFrags so build-tool exes aren't forced to rebuild from source. Surfaced by ghcjs tests.js-template-haskell.check (reinstalls text).
comp-v2-builder: compose source-repository-package deps at build time; always allow boot-lib installs
Two fixes for source-repo / reinstallable-lib:ghc projects under v2, surfaced by the ghc-lib-reinstallable-cabal test: 1. Build-time source-repository-package composition. The cleanup commit dropped source-repo handling (cardano-node has none), which broke any project with source-repo deps — including lib:ghc via useLocalGhcLib. Each source-repo package now emits its own `source-repository-package` block (wrapping its source in a minimal git repo) into its fragment, and the build-time composer collects them across the closure and omits those packages from `extra-packages:`. The block references a derivation, so it's written with `writeText` (not `toFile`). 2. `allow-boot-library-installs: True` is now emitted unconditionally, combined with `allow-newer`/`allow-older` in the project skeleton (`solverRelaxations`). It only *permits* reinstalling boot libs; cabal can act on it solely when a boot lib's source is in this slice's repo (one plan-nix reinstalled), so it's a no-op otherwise. This drops the `bootLibPkgNames` gating machinery and lets cabal pick the source-repo `ghc` for useLocalGhcLib slices. Also added `allow-older: *:*` alongside `allow-newer: *:*` (harmless given the per-slice repo has one candidate version per package). Verified: tests.ghc9141.native.tests.ghc-lib-reinstallable-cabal builds (reinstalls lib:ghc end to end); Stream / happy / proto-lens-protobuf-types unaffected.
default builderVersion to 2 (cabal v2-build slicing builder)
Make the cabal v2-build slicing builder (comp-v2-builder) the default for all projects, so CI exercises it. v2 eval is now at parity with v1 (per-slice work composed at build time, Nix touches only direct deps), and the v2 builder handles the cases the test suite and cardano-node exercise (multi-version deps, multi-sublib packages, build-tool closures, Custom-build setup unit-ids). Projects can still opt back to the Setup.hs builder with `builderVersion = 1`.
v2 build-time composition: remove the now-dead Nix-side closure code
With the slicing repo and cabal.project both composed at build time,
migrate the two remaining Nix-cabal.project consumers and delete the
now-unused closure-assembly machinery (net ~-395 lines in
comp-v2-builder.nix):
* checkAgainstPlan and docSlice now use the build-time path too
(v2Fragment + slicePreBuildV2 + localRepo = null), so nothing
forces the Nix-assembled cabal.project / slicingRepo anymore;
* sublib reachability seeds (HASKELLNIX_EXTRA_SUBLIB_SEEDS) are
composed at build time from the per-package fragments' sublib-seeds
across the all-dep closure plus this slice's own target sublib —
replacing the last Nix-side allDepClosure walk (extraSublibSeeds);
* the 'is this a v2 slice' marker moves from passthru.transitiveTarballs
to the existing passthru.isSlice (comp-v2-builder + shell-for-v2);
* deleted: cabalProject, slicePreBuild, slicingRepo (+ repoCopyCmds /
indexEntryCmds / depTarballsDeduped), depTransitives /
depTransitiveTarballsOf / transitiveTarballs, sliceCanonicalNames +
the six all*Blocks, depConstraints / libConstraintPins /
libConstraintClosure / exeUnitsInAllDeps, extraPackages*,
sourceRepo* / minimalSourceRepo / wrapAsMinimalRepo, allowBootLibBlock,
and the pre-existing dead libDepClosure / libDepsOf / pkgPlanUnits /
pkgLibDepClosure.
Kept (still used by the per-package fragment / build-time path): the
per-package block functions (flagBlockFor, ...), bootLibPkgNames,
allDepClosure (build-tool store composition), localCabalProject.
Two build-time fixes for multi-sublib packages: the repo index copy is
no-clobber, and the selfFrag carries its own sublib-seeds (the seeds
loop is robust under set -e/pipefail).
Verified: eval clean, ~15.7s user (v1 parity); Stream / code-page /
network / happy (multi-sublib + build-tool) build with unchanged
unit-ids. (proto-lens-protobuf-types fails to build, but that's a
pre-existing v2 bug independent of this change — it fails identically
at a77f5524c, before any build-time-composition work.)
comp-v2-builder: pin Custom-build packages' setup-depends (notably Cabal)
A `build-type: Custom` package's unit-id hashes its custom-setup configuration, including which `Cabal` the setup is built against. The v2 slice's cabal.project pinned library dep versions but left the per-package custom-setup solve unconstrained, so when a slice's closure contained a reinstalled `Cabal` (e.g. pulled in by another package's `proto-lens-setup` custom-setup) cabal preferred it over the GHC-bundled `Cabal` plan-nix had used — forking the Custom-build unit-id. Concretely: `ghc-paths` (build-type: Custom, setup-depends Cabal) built standalone as plan-nix's `ghc-pths-0.1.0.12-142b137f` (setup against bundled Cabal-3.10.3.0), but inside `proto-lens-protobuf-types`' slice its setup resolved against the reinstalled Cabal-3.16.1.0 → `…-e732a98d`. The pre-built `proto-lens-protoc` exe (linked against 142b137f) was then rejected, cabal rebuilt it + ghc-paths, and the "exactly one expected unit" check failed. Fix: emit per-package `constraints: <pkg>:setup.<dep> ==<ver>` for each package's `components.setup.depends` (which plan-nix records), composed at build time across the all-dep closure alongside the library constraints. cabal accepts the per-package `pkg:setup.dep` qualifier (verified), so each Custom-build package's setup now resolves to the same deps plan-nix used, reproducing its unit-id. Verified: proto-lens-protobuf-types now builds (with the project's `build-tools = [protobuf]` providing `protoc` for codegen); Stream (Strm-0.4.7.2-a9e4dded) and happy (hppy-2.2-70e3af7a) unchanged.
v2 build-time composition (step 2a): assemble slicing repo at build time
When a slice carries a `v2Fragment`, assemble its hackage slicing repo in the build phase from the per-package `repo-frag/`s — this package's own source (passed directly, since its own fragment isn't built yet) plus every dep's fragment reached through the same `transitive-deps` closure walk used to compose the store. Replaces referencing the Nix-built `localRepo` for these slices. cabal hashes package source + .cabal descriptions (not the index bytes or the repo path), so the content-equivalent repo keeps unit-ids identical: the Stream leaf slice still builds as Strm-0.4.7.2-a9e4dded. (The Nix `slicingRepo` is still computed but unused for these slices; it's removed in step 3 along with the other Nix-side closure walks.)
v2 build-time composition (step 3): drop the Nix-side closure walks
The real slice now assembles its cabal.project + slicing repo entirely
at build time from per-package fragments, so the per-slice dependency-
closure walk no longer runs in the Nix evaluator:
* baseSlice's preBuild (slicePreBuildV2) only stages local test/bench
sources — it no longer interpolates the Nix-assembled `cabalProject`,
so the six `sliceCanonicalNames` block-assemblies, `depConstraints`
/`libConstraintPins`/`libConstraintClosure`, `extraPackages`,
`sourceRepoBlocks` and `allowBootLibBlock` are no longer forced;
* localRepo is null for these slices, so `slicingRepo` (and the
`transitiveTarballs` recursion behind it) is no longer forced;
* the build-time content-equivalence diff harness is removed (it was
only needed while developing the composer), and the local skeleton
is written via redirect rather than `cp` (which would otherwise
create cabal.project read-only and break the closure `>>` append).
`checkAgainstPlan` / `docSlice` keep the full Nix `cabalProject` path —
they don't carry a `v2Fragment` and are lazy (not forced on the main
slice / devShell path).
Effect on `nix path-info --derivation .#devShells…default`:
comp-v2-builder eval calls 13.2M -> 6.5M, total ~62M -> ~49M, CPU
~24s -> ~20s. The remaining cost is dominated by the module system
(~18M lib/modules.nix calls, unchanged) — the inherent evaluation of
`config.packages.<id>` when each slice references its dep slices — which
is structural and not addressable by build-time composition.
Unit-ids unchanged throughout (Stream Strm-0.4.7.2-a9e4dded,
happy hppy-2.2-70e3af7a).
v2 build-time composition (step 2b): assemble cabal.project closure sections at build time
Compose the dependency-closure-derived parts of each slice's
cabal.project in the build phase, from per-package fragments, instead of
in the Nix evaluator:
* extra-packages: self + lib-dep-closure pins
* the six per-package block groups (flags / ghc-options /
configure-options / program-options / documentation / extra-lib-dirs)
* allow-boot-library-installs (gated on closure boot-libs)
* constraints: self `any.<pkg> source` + lib-dep-closure pins
The Nix side now emits only the LOCAL/global skeleton
(`localCabalProject`) and each package's OWN fragment; the build script
appends the composed sections. cabal hashes content, not field order,
so we emit local-then-closure and verify content-equivalence with an
order-insensitive diff against the (still-computed) Nix cabal.project —
empty for every slice tested.
Direct-deps-only in Nix (per design): the lib-dep / constraint closure
is seeded from this component's DIRECT lib-deps and DIRECT build tools
(`buildToolSlices`, not the Nix-walked `transitiveBuildToolSlices`).
Transitivity is accumulated at build time — each slice's emitted
`lib-dep-slices` folds in its own direct build tools' lib closures, so
following a dep's pointer reaches every transitive build tool's lib
closure without a Nix-side allDepClosure walk.
Two multi-sublib fixes (e.g. happy-lib's grammar/frontend/tabular, all
sharing pkg-name happy-lib):
* repo index copy is no-clobber (`-n`) — duplicate identical .cabal
across sublib fragments no longer hits the read-only first copy;
* closure dedup is by pkg-name (`sort -k1,1 -u`), matching the Nix
side's `sliceCanonicalNames` / `libConstraintPins` dedup, so a
package's block / constraint / extra-packages entry appears once.
Verified content-identical (empty diff) + unchanged unit-ids on Stream
(leaf), code-page + network (hsc2hs), and happy (multi-sublib +
build-tool with reinstalled happy-lib). No source-repo packages exist
in this plan, so source-repository-package block composition is deferred.
v2 build-time composition (step 1): emit per-package fragment in $out
First step of moving the v2 slicing-repo / cabal.project assembly out of
the Nix evaluator and into each slice's build phase, so a consumer never
has to walk its transitive dependency graph in Nix.
Each slice now writes its OWN contribution into $out (additive; not yet
consumed):
* repo-frag/ — this package's source tarball + index .cabal
* nix-support/v2-frag — the six per-package cabal.project blocks
(flags / ghc-options / configure-options /
program-options / documentation / extra-lib-dirs),
its constraint pin line, pkg-name, and sublib seeds
* nix-support/lib-dep-slices — flattened lib-dep closure pointer
(built the same way as transitive-deps)
All values are computed once for THIS package (O(1) per slice), reusing
the existing per-package block functions. Verified additive: the Stream
leaf slice's unit-id is unchanged (Strm-0.4.7.2-a9e4dded).
Steps 2 (compose at build time) and 3 (drop the Nix-side closure walks)
follow.
comp-v2-builder: compute project-global cabal.project data once (#2516)
The v2 builder derived three values from the install plan while emitting
every slice's cabal.project:
* projectConfigPragmas — the `package *` / per-package pragma blocks
parsed from each configured unit's `configure-args` (3 regex
matches per arg, over every configured plan entry);
* pkgDocEnabled — `lib.any (... -haddock ...) planJson` per pkg;
* planFlagsFor — `lib.findFirst (...) planJson` per pkg.
All three depend only on the plan (+ ghc version and host platform),
never on the individual component — yet they were recomputed from
scratch for every one of the ~2000 component slices. On cardano-node
that was the dominant evaluation cost: ~290M of 535M function calls and
the bulk of 125M `builtins.match` / 94M `lib.elem` primop calls.
Hoist them into a pure helper (builder/v2-project-globals.nix) computed
once behind the project-level `config.plan-json-v2-globals` option
(mirroring the existing `plan-json-by-id` index), and reduce the
per-slice sites to O(1) lookups.
`nix path-info --derivation .#devShells.aarch64-darwin.default` on
cardano-node drops from ~4:00 to ~26s, and the resulting devShell
derivation is byte-identical to before — this is a pure memoisation
with no change to any emitted cabal.project or slice.
comp-v2-builder: compute project-global cabal.project data once
The v2 builder derived three values from the install plan while emitting
every slice's cabal.project:
* projectConfigPragmas — the `package *` / per-package pragma blocks
parsed from each configured unit's `configure-args` (3 regex
matches per arg, over every configured plan entry);
* pkgDocEnabled — `lib.any (... -haddock ...) planJson` per pkg;
* planFlagsFor — `lib.findFirst (...) planJson` per pkg.
All three depend only on the plan (+ ghc version and host platform),
never on the individual component — yet they were recomputed from
scratch for every one of the ~2000 component slices. On cardano-node
that was the dominant evaluation cost: ~290M of 535M function calls and
the bulk of 125M `builtins.match` / 94M `lib.elem` primop calls.
Hoist them into a pure helper (builder/v2-project-globals.nix) computed
once behind the project-level `config.plan-json-v2-globals` option
(mirroring the existing `plan-json-by-id` index), and reduce the
per-slice sites to O(1) lookups.
`nix path-info --derivation .#devShells.aarch64-darwin.default` on
cardano-node drops from ~4:00 to ~26s, and the resulting devShell
derivation is byte-identical to before — this is a pure memoisation
with no change to any emitted cabal.project or slice.
comp-v2-builder: disambiguate dep lookups by version (#2515)
When the cabal plan holds more than one version of a package -- e.g. a
GHC boot library like text-2.0.2 alongside a cabal-reinstalled
text-2.1.4 -- the bare hsPkgs.<name> redirect is ambiguous and throws
"Multiple versions for <name>".
comp-v2-builder looked dependency packages up by bare name in eight
places (externalDepIds collapses deps to { name; version; } pairs,
dropping the unit id, and the helpers then re-looked-up by name only),
so any v2 build whose dependency closure spanned such a package failed
to evaluate.
Add a lookupDepPkg helper that prefers the unique <name>-<version>
redirect when the version is known and falls back to the bare name
otherwise, and route all the bare-name lookups through it.
comp-v2-builder: disambiguate dep lookups by version
When the cabal plan holds more than one version of a package -- e.g. a
GHC boot library like text-2.0.2 alongside a cabal-reinstalled
text-2.1.4 -- the bare hsPkgs.<name> redirect is ambiguous and throws
"Multiple versions for <name>".
comp-v2-builder looked dependency packages up by bare name in eight
places (externalDepIds collapses deps to { name; version; } pairs,
dropping the unit id, and the helpers then re-looked-up by name only),
so any v2 build whose dependency closure spanned such a package failed
to evaluate.
Add a lookupDepPkg helper that prefers the unique <name>-<version>
redirect when the version is known and falls back to the bare name
otherwise, and route all the bare-name lookups through it.
v2 builder: pin build-tools' lib closures in slice constraints (#2514)
* v2 builder: fix checkAgainstPlan call args (pname/version)
`checkAgainstPlan` was calling `buildCabalStoreSlice` with `name =`
but the slice builder requires `pname` + `version` (matches the
neighbouring `baseSlice` / `docSlice` callsites). Evaluation
crashed with "function 'anonymous lambda' called without required
argument 'version'" the moment anything asked for the diagnostic
attribute.
* v2 builder: pin build-tools' lib closures in slice constraints
`libConstraintPins` previously walked only `libDepClosure` — the
slice's own lib closure — so reinstalled libs reached only through
`build-tool-depends:` never made it into the slice's
`extra-packages:` / `constraints:` blocks. cabal in the slice then
solved each transitive build-tool (alex / happy / hsc2hs / ...)
against GHC-bundled `-inplace` versions instead of the reinstalls
plan-nix recorded, the tool's computed UnitId diverged from the
pre-built slice composed into the starting store, and cabal
rebuilt the tool from source.
Concrete failure: zlib's lib closure is `{base, bytestring}` — no
directory/filepath/process — but `build-tool-depends: hsc2hs:hsc2hs`
brings hsc2hs in, and plan-nix had reinstalled directory-1.3.11.0 /
filepath-1.5.5.0 / process-1.6.29.0. hsc2hs in the slice resolved
against directory-1.3.10.0-inplace etc., computed
`hsc2hs-0.68.10-981b6fe4`, and refused the composed
`hsc2hs-0.68.10-bb34db40`.
Fix: extend the closure used by `libConstraintPins` to seed from
this slice's plan units AND every exe-component plan unit reached
via `allDepClosure`. Each exe seed's lib closure (via `libDepsOf`)
contributes its reinstalled deps to the pin set. Within a single
plan-nix every package has one version, so the first-wins
grouping is stable across the merged closure.
* v2 shell: add pkg-config and surface slices' C-side deps
cabal's solver inside the v2 shell wasn't seeing `pkg-config` or
the pkgconfig deps of any selected pkg / dep slice, so when a
package declared `pkgconfig-depends:` (e.g. `zlib` with the
default `+pkg-config` flag) cabal silently flipped the flag to
`-pkg-config` and computed a different unit-id than plan-nix had
recorded. The composed cabal store held the `+pkg-config`
unit-id, so cabal didn't recognise its prebuilt slice and planned
a from-source rebuild — cascading to every package downstream of
zlib (streaming-commons, warp, websockets, wai-websockets, ...).
Fix:
1. Add `pkgs.buildPackages.cabalPkgConfigWrapper` to the v2
shell's `nativeBuildInputs` unconditionally — same wrapper v1's
`comp-builder.nix:345` threads via `--with-pkg-config=`, real
pkg-config plus a `--libs --static` failure shim (#1642), NOT
the fake `allPkgConfigWrapper`. Always-on so the unit-id stays
stable if the user adds a pkgconfig dep later.
2. Fold every selected component's and dep slice's
`passthru.runtimeLibs` (= each slice's own `extraBuildInputs`,
covering `component.libs / pkgconfig / frameworks`) into the
shell's `buildInputs`. Without this, pkg-config alone finds
nothing — none of the project's `.pc` files would be on
`PKG_CONFIG_PATH`. Mirrors v1's `systemInputs` line in
`shell-for.nix:108`, which collects the same set via stdenv
propagation of `c.buildInputs ++ c.propagatedBuildInputs`; v2
slices don't ride that propagation chain (they flow through
the cabal store instead), so the inputs are surfaced
explicitly.
v2 shell: add pkg-config and surface slices' C-side deps
cabal's solver inside the v2 shell wasn't seeing `pkg-config` or the pkgconfig deps of any selected pkg / dep slice, so when a package declared `pkgconfig-depends:` (e.g. `zlib` with the default `+pkg-config` flag) cabal silently flipped the flag to `-pkg-config` and computed a different unit-id than plan-nix had recorded. The composed cabal store held the `+pkg-config` unit-id, so cabal didn't recognise its prebuilt slice and planned a from-source rebuild — cascading to every package downstream of zlib (streaming-commons, warp, websockets, wai-websockets, ...). Fix: 1. Add `pkgs.buildPackages.cabalPkgConfigWrapper` to the v2 shell's `nativeBuildInputs` unconditionally — same wrapper v1's `comp-builder.nix:345` threads via `--with-pkg-config=`, real pkg-config plus a `--libs --static` failure shim (#1642), NOT the fake `allPkgConfigWrapper`. Always-on so the unit-id stays stable if the user adds a pkgconfig dep later. 2. Fold every selected component's and dep slice's `passthru.runtimeLibs` (= each slice's own `extraBuildInputs`, covering `component.libs / pkgconfig / frameworks`) into the shell's `buildInputs`. Without this, pkg-config alone finds nothing — none of the project's `.pc` files would be on `PKG_CONFIG_PATH`. Mirrors v1's `systemInputs` line in `shell-for.nix:108`, which collects the same set via stdenv propagation of `c.buildInputs ++ c.propagatedBuildInputs`; v2 slices don't ride that propagation chain (they flow through the cabal store instead), so the inputs are surfaced explicitly.
v2 builder: pin build-tools' lib closures in slice constraints
`libConstraintPins` previously walked only `libDepClosure` — the
slice's own lib closure — so reinstalled libs reached only through
`build-tool-depends:` never made it into the slice's
`extra-packages:` / `constraints:` blocks. cabal in the slice then
solved each transitive build-tool (alex / happy / hsc2hs / ...)
against GHC-bundled `-inplace` versions instead of the reinstalls
plan-nix recorded, the tool's computed UnitId diverged from the
pre-built slice composed into the starting store, and cabal
rebuilt the tool from source.
Concrete failure: zlib's lib closure is `{base, bytestring}` — no
directory/filepath/process — but `build-tool-depends: hsc2hs:hsc2hs`
brings hsc2hs in, and plan-nix had reinstalled directory-1.3.11.0 /
filepath-1.5.5.0 / process-1.6.29.0. hsc2hs in the slice resolved
against directory-1.3.10.0-inplace etc., computed
`hsc2hs-0.68.10-981b6fe4`, and refused the composed
`hsc2hs-0.68.10-bb34db40`.
Fix: extend the closure used by `libConstraintPins` to seed from
this slice's plan units AND every exe-component plan unit reached
via `allDepClosure`. Each exe seed's lib closure (via `libDepsOf`)
contributes its reinstalled deps to the pin set. Within a single
plan-nix every package has one version, so the first-wins
grouping is stable across the merged closure.