Home / Input Output / haskell.nix
Apr 30, 12-1 AM (1)
Apr 30, 1-2 AM (0)
Apr 30, 2-3 AM (0)
Apr 30, 3-4 AM (0)
Apr 30, 4-5 AM (0)
Apr 30, 5-6 AM (0)
Apr 30, 6-7 AM (0)
Apr 30, 7-8 AM (0)
Apr 30, 8-9 AM (0)
Apr 30, 9-10 AM (0)
Apr 30, 10-11 AM (0)
Apr 30, 11-12 PM (0)
Apr 30, 12-1 PM (0)
Apr 30, 1-2 PM (0)
Apr 30, 2-3 PM (0)
Apr 30, 3-4 PM (0)
Apr 30, 4-5 PM (0)
Apr 30, 5-6 PM (0)
Apr 30, 6-7 PM (0)
Apr 30, 7-8 PM (0)
Apr 30, 8-9 PM (0)
Apr 30, 9-10 PM (0)
Apr 30, 10-11 PM (0)
Apr 30, 11-12 AM (0)
May 01, 12-1 AM (1)
May 01, 1-2 AM (0)
May 01, 2-3 AM (0)
May 01, 3-4 AM (0)
May 01, 4-5 AM (0)
May 01, 5-6 AM (0)
May 01, 6-7 AM (0)
May 01, 7-8 AM (0)
May 01, 8-9 AM (0)
May 01, 9-10 AM (0)
May 01, 10-11 AM (0)
May 01, 11-12 PM (0)
May 01, 12-1 PM (0)
May 01, 1-2 PM (0)
May 01, 2-3 PM (0)
May 01, 3-4 PM (0)
May 01, 4-5 PM (0)
May 01, 5-6 PM (0)
May 01, 6-7 PM (0)
May 01, 7-8 PM (1)
May 01, 8-9 PM (0)
May 01, 9-10 PM (0)
May 01, 10-11 PM (0)
May 01, 11-12 AM (0)
May 02, 12-1 AM (1)
May 02, 1-2 AM (0)
May 02, 2-3 AM (0)
May 02, 3-4 AM (0)
May 02, 4-5 AM (0)
May 02, 5-6 AM (0)
May 02, 6-7 AM (0)
May 02, 7-8 AM (0)
May 02, 8-9 AM (0)
May 02, 9-10 AM (0)
May 02, 10-11 AM (0)
May 02, 11-12 PM (0)
May 02, 12-1 PM (0)
May 02, 1-2 PM (0)
May 02, 2-3 PM (0)
May 02, 3-4 PM (0)
May 02, 4-5 PM (0)
May 02, 5-6 PM (0)
May 02, 6-7 PM (0)
May 02, 7-8 PM (0)
May 02, 8-9 PM (0)
May 02, 9-10 PM (0)
May 02, 10-11 PM (0)
May 02, 11-12 AM (0)
May 03, 12-1 AM (1)
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 (0)
26 commits this week Apr 30, 2026 - May 07, 2026
v2 builder: cabal v2-build slicing, dev shell, plan-nix matching
Adds the v2 component builder alongside v1 and a project-level
`builderVersion` switch that picks between them.  Where v1 runs
`Setup.hs configure / build` per component, v2 runs `cabal
v2-build` against a per-slice slicing repo and pre-composed
starting store, registers each new unit under
`$out/store/ghc-<ver>[-inplace]/...`, and emits a plan-entry
diff against plan-nix when something diverges.

Highlights:

* `builder/comp-v2-builder.nix` — per-component slice builder.
  Drives a per-slice cabal.project (constraints pinned from
  plan-nix's `depends`, `extra-packages:` listing every
  transitive dep, per-pkg `flags:` / `ghc-options:` /
  `configure-options:` blocks) and a per-slice slicing repo
  of source tarballs and a `00-index.tar.gz` so cabal's solver
  hits a closed package set.  Hands the cabal.project plus
  the slice's direct dep slices off to `build-cabal-slice.nix`
  to actually run cabal.
* `builder/build-cabal-slice.nix` — generic runner the per-
  component builder calls into.  Composes the starting store
  via `lndir` from every dep slice (walks `pkgsHostTarget`
  for direct deps and follows each entry's
  `$out/nix-support/transitive-deps` for the rest of the
  closure, so transitive dep slices stay out of nix-side
  `buildInputs`), runs `cabal v2-build`, captures every newly
  installed unit back into `$out/store`, writes the slice's
  own `nix-support/transitive-deps` for downstream consumers,
  and on `dryRunOnly` emits a per-entry diff between cabal's
  planned `plan.json` and plan-nix's expectations.
* `builder/compose-store.nix` — `lndir`-based store
  composition with the same direct + transitive-deps walk.
  Used by `comp-v2-builder` for the `passthru.store` exposed
  on each slice and by `shell-for-v2.nix` for the dev shell's
  composed dep store.
* `builder/cabal-install-patches/{prune-unreachable-sublibs*,
  skip-installed-revdeps-in-completed}.patch` — three
  cabal-install patches the v2 slice solver needs to keep its
  plan in sync with plan-nix.
* `builder/shell-for-v2.nix` — v2 dev shell.  Resolves each
  selected component's direct external `component.depends` to
  its library (via `haskellLib.dependToLib`, so sublib
  references resolve to their sublib slices), composes those
  into a starting cabal store via `composeStore` (the
  transitive closure is materialised at build time from each
  slice's `nix-support/transitive-deps`), exposes the result
  via `~/.cabal/store/<ghc>-inplace/` (the default
  `exposePackagesVia = "cabal-store"`) or via a
  `GHC_ENVIRONMENT`-wrapped ghc (`exposePackagesVia =
  "ghc-pkg"`), and merges `passthru.depSlices` from
  `inputsFrom` so cross + native shells both see each other's
  slices.
* `builder/{default,hspkg-builder}.nix` — dispatch on
  `builderVersion`: the per-component derivation at
  `hsPkgs.<pkg>.components.<kind>.<n>` is whichever builder
  the project selects (no per-component opt-in).
  `hspkg-builder.nix` also resolves `homeDependIds` and the
  `packageRefersOwnExe` flag from `config.plan-json-by-id`
  (the project-level index) so these per-component lookups
  are O(log N) attrset hits instead of linear scans over the
  install-plan.
* `modules/{component-driver,project-common,shell}.nix` —
  `builderVersion`, `crossTemplateHaskellSupport`,
  `exposePackagesVia` options.
* `modules/install-plan/configure-args.nix` — filter
  `--ghc-option=-hide-all-packages` out of `ghcOptions` for
  the v2 builder (cabal injects it on every Setup configure
  call, and round-tripping it through cabal.project would
  duplicate it in `pkgHashProgramArgs`); extract
  `--configure-option=...` entries into a new dedicated
  `package.configureOptions` option (declared in
  `modules/package-options.nix`) so the v2 builder can emit
  them back into its own cabal.project.
* `modules/install-plan/non-reinstallable.nix` — gate the
  ghcjs/wasm `ghci`-related `non-reinstallable` set on
  `builderVersion != 2`; under v2 the slice solver follows
  cabal's install plan literally.
* `lib/check.nix` — `passthru.isSlice` branch that runs the
  slice-built exe directly instead of going through v1's
  `setup test` pipeline.
* `lib/default.nix` — `uniqueWithNameKey` prefers
  `identifier.unit-id` when present so two `foo-1.2.3` slices
  with different inputs partition into different buckets.
* `lib/call-cabal-project-to-nix.nix` — set
  `CABAL_INSTALLED_PACKAGE_ID_OS = pkgs.stdenv.buildPlatform.parsed.kernel.name;`
  on the plan-to-nix derivation so the patched cabal-install
  (#2501) actually fires; without the env var the patch is a
  no-op.
* `compiler/ghc/default.nix`, `overlays/{bootstrap,tools}.nix`
  — pin GHC, `hscolour`, and the boot tools to
  `builderVersion = 1` so v2 churn doesn't trigger GHC
  rebuilds while v2 is still settling.
* `overlays/{windows,mingw_w64}.nix` — v2-builder-aware
  Windows-cross TH wrapper: `mingw_w64.nix` exposes
  `wrapGhc :: ghc -> ghc` and `iservRuntimeLibs`;
  `overlays/windows.nix` plumbs the build-platform
  `runCommand` / `makeWrapper` / `libffi` through to that
  module.
* `overlays/haskell.nix` — project-level shell dispatch
  (`shellForV1` / `shellForV2`), `crossTemplateHaskellSupport`
  plumbing, and `cabalProjectLocal` refactored so the
  Windows-host iserv-proxy linker flags can be additive.
* `test/cabal-sublib-shell/` — new test exercising a sublib
  consumer in the v2 dev shell.  Other test wiring tweaks
  (`test/{th-dlls,th-dlls-minimal,cabal-sublib}/default.nix`,
  `test/cabal.project.local`, `test/{setup-deps,
  shell-for-setup-deps}/pkg/pkg.cabal`, `test/default.nix`)
  to keep things passing under v2.
* `ci.nix` — pin `evalSystem` default to `x86_64-linux` so
  cross derivations evaluate consistently.
* `docs/dev/builder-v{1,2}.md` (+ `docs/SUMMARY.md` entry) —
  design notes for the two builders.

Performance notes:
* `transitiveTarballs` is exposed as the deduped values of
  `depTarballsDeduped`.  The un-deduped concat (which sticks
  every direct dep's full transitive list onto each consumer
  unfiltered) grows exponentially with depth in a typical
  Haskell graph (every package shares `base` / `bytestring` /
  `text` / ...) and materialises multi-GiB blocks in the
  evaluator on cardano-wallet-sized projects.
* `homeDepSliceOf` / `depTransitiveTarballsOf` fall through
  to any sublib's slice when a dep package defines only
  `library <name>` stanzas (no main library — e.g.
  `cardano-wallet-ui:{common,shelley}`).
* `propagated` keeps `libs` Windows-only (matching v1):
  cross-target slices land in the v2 dev shell's
  `inputsFrom`, and unconditionally propagating `libs` would
  drag e.g. `pkgs.liburing` from a Linux-target slice into a
  Darwin consumer's `pkgsHostTarget`.  Each slice gathers its
  own transitive sysLibs (deduped) into `extraBuildInputs`
  via `transitiveDepLibs` instead.
v2 builder: cabal v2-build slicing, dev shell, plan-nix matching
Adds the v2 component builder alongside v1 and a project-level
`builderVersion` switch that picks between them.  Where v1 runs
`Setup.hs configure / build` per component, v2 runs `cabal
v2-build` against a per-slice slicing repo and pre-composed
starting store, registers each new unit under
`$out/store/ghc-<ver>[-inplace]/...`, and emits a plan-entry
diff against plan-nix when something diverges.

Highlights:

* `builder/comp-v2-builder.nix` — per-component slice builder.
  Drives a per-slice cabal.project (constraints pinned from
  plan-nix's `depends`, `extra-packages:` listing every
  transitive dep, per-pkg `flags:` / `ghc-options:` /
  `configure-options:` blocks) and a per-slice slicing repo
  of source tarballs and a `00-index.tar.gz` so cabal's solver
  hits a closed package set.  Hands the cabal.project plus
  the slice's direct dep slices off to `build-cabal-slice.nix`
  to actually run cabal.
* `builder/build-cabal-slice.nix` — generic runner the per-
  component builder calls into.  Composes the starting store
  via `lndir` from every dep slice (walks `pkgsHostTarget`
  for direct deps and follows each entry's
  `$out/nix-support/transitive-deps` for the rest of the
  closure, so transitive dep slices stay out of nix-side
  `buildInputs`), runs `cabal v2-build`, captures every newly
  installed unit back into `$out/store`, writes the slice's
  own `nix-support/transitive-deps` for downstream consumers,
  and on `dryRunOnly` emits a per-entry diff between cabal's
  planned `plan.json` and plan-nix's expectations.
* `builder/compose-store.nix` — `lndir`-based store
  composition with the same direct + transitive-deps walk.
  Used by `comp-v2-builder` for the `passthru.store` exposed
  on each slice and by `shell-for-v2.nix` for the dev shell's
  composed dep store.
* `builder/cabal-install-patches/{prune-unreachable-sublibs*,
  skip-installed-revdeps-in-completed}.patch` — three
  cabal-install patches the v2 slice solver needs to keep its
  plan in sync with plan-nix.
* `builder/shell-for-v2.nix` — v2 dev shell.  Resolves each
  selected component's direct external `component.depends` to
  its library (via `haskellLib.dependToLib`, so sublib
  references resolve to their sublib slices), composes those
  into a starting cabal store via `composeStore` (the
  transitive closure is materialised at build time from each
  slice's `nix-support/transitive-deps`), exposes the result
  via `~/.cabal/store/<ghc>-inplace/` (the default
  `exposePackagesVia = "cabal-store"`) or via a
  `GHC_ENVIRONMENT`-wrapped ghc (`exposePackagesVia =
  "ghc-pkg"`), and merges `passthru.depSlices` from
  `inputsFrom` so cross + native shells both see each other's
  slices.
* `builder/{default,hspkg-builder}.nix` — dispatch on
  `builderVersion`: the per-component derivation at
  `hsPkgs.<pkg>.components.<kind>.<n>` is whichever builder
  the project selects (no per-component opt-in).
  `hspkg-builder.nix` also resolves `homeDependIds` and the
  `packageRefersOwnExe` flag from `config.plan-json-by-id`
  (the project-level index) so these per-component lookups
  are O(log N) attrset hits instead of linear scans over the
  install-plan.
* `modules/{component-driver,project-common,shell}.nix` —
  `builderVersion`, `crossTemplateHaskellSupport`,
  `exposePackagesVia` options.
* `modules/install-plan/configure-args.nix` — filter
  `--ghc-option=-hide-all-packages` out of `ghcOptions` for
  the v2 builder (cabal injects it on every Setup configure
  call, and round-tripping it through cabal.project would
  duplicate it in `pkgHashProgramArgs`); extract
  `--configure-option=...` entries into a new dedicated
  `package.configureOptions` option (declared in
  `modules/package-options.nix`) so the v2 builder can emit
  them back into its own cabal.project.
* `modules/install-plan/non-reinstallable.nix` — gate the
  ghcjs/wasm `ghci`-related `non-reinstallable` set on
  `builderVersion != 2`; under v2 the slice solver follows
  cabal's install plan literally.
* `lib/check.nix` — `passthru.isSlice` branch that runs the
  slice-built exe directly instead of going through v1's
  `setup test` pipeline.
* `lib/default.nix` — `uniqueWithNameKey` prefers
  `identifier.unit-id` when present so two `foo-1.2.3` slices
  with different inputs partition into different buckets.
* `lib/call-cabal-project-to-nix.nix` — set
  `CABAL_INSTALLED_PACKAGE_ID_OS = pkgs.stdenv.buildPlatform.parsed.kernel.name;`
  on the plan-to-nix derivation so the patched cabal-install
  (#2501) actually fires; without the env var the patch is a
  no-op.
* `compiler/ghc/default.nix`, `overlays/{bootstrap,tools}.nix`
  — pin GHC, `hscolour`, and the boot tools to
  `builderVersion = 1` so v2 churn doesn't trigger GHC
  rebuilds while v2 is still settling.
* `overlays/{windows,mingw_w64}.nix` — v2-builder-aware
  Windows-cross TH wrapper: `mingw_w64.nix` exposes
  `wrapGhc :: ghc -> ghc` and `iservRuntimeLibs`;
  `overlays/windows.nix` plumbs the build-platform
  `runCommand` / `makeWrapper` / `libffi` through to that
  module.
* `overlays/haskell.nix` — project-level shell dispatch
  (`shellForV1` / `shellForV2`), `crossTemplateHaskellSupport`
  plumbing, and `cabalProjectLocal` refactored so the
  Windows-host iserv-proxy linker flags can be additive.
* `test/cabal-sublib-shell/` — new test exercising a sublib
  consumer in the v2 dev shell.  Other test wiring tweaks
  (`test/{th-dlls,th-dlls-minimal,cabal-sublib}/default.nix`,
  `test/cabal.project.local`, `test/{setup-deps,
  shell-for-setup-deps}/pkg/pkg.cabal`, `test/default.nix`)
  to keep things passing under v2.
* `ci.nix` — pin `evalSystem` default to `x86_64-linux` so
  cross derivations evaluate consistently.
* `docs/dev/builder-v{1,2}.md` (+ `docs/SUMMARY.md` entry) —
  design notes for the two builders.

Performance notes:
* `transitiveTarballs` is exposed as the deduped values of
  `depTarballsDeduped`.  The un-deduped concat (which sticks
  every direct dep's full transitive list onto each consumer
  unfiltered) grows exponentially with depth in a typical
  Haskell graph (every package shares `base` / `bytestring` /
  `text` / ...) and materialises multi-GiB blocks in the
  evaluator on cardano-wallet-sized projects.
* `homeDepSliceOf` / `depTransitiveTarballsOf` fall through
  to any sublib's slice when a dep package defines only
  `library <name>` stanzas (no main library — e.g.
  `cardano-wallet-ui:{common,shelley}`).
* `propagated` keeps `libs` Windows-only (matching v1):
  cross-target slices land in the v2 dev shell's
  `inputsFrom`, and unconditionally propagating `libs` would
  drag e.g. `pkgs.liburing` from a Linux-target slice into a
  Darwin consumer's `pkgsHostTarget`.  Each slice gathers its
  own transitive sysLibs (deduped) into `extraBuildInputs`
  via `transitiveDepLibs` instead.
plan modules: expose `identifier.unit-id` and `package-ids-by-name` (#2503)
* plan modules: expose `identifier.unit-id` and `package-ids-by-name`

Pure additions for consumers that need to look up packages by
plan-id rather than canonical name.

* `package.identifier.unit-id` (modules/package.nix): a new
  optional field carrying plan.json's per-entry `id` (defaults to
  null when not derived from a plan entry).
* `modules/install-plan/planned.nix`: when constructing the
  per-plan-id package entries, populate `package.identifier`
  with `name` / `version` / `unit-id`.  Without this,
  reading `package.identifier.{name,version}` on a plan-id-keyed
  entry threw — the canonical-name-keyed entry already gets a
  real identifier via `load-cabal-plan.nix`, but the per-plan-id
  ones (used when one canonical name has multiple plan instances)
  did not.
* `package-ids-by-name` (modules/plan.nix +
  install-plan/override-package-by-name.nix): a name → [id]
  index over `plan-json.install-plan`.  A canonical pkg-name can
  appear under several plan ids (per-instance UnitIDs that differ
  only in their dep UnitIDs, or a multi-component package split
  across per-component entries); consumers can now iterate the
  ids unambiguously instead of name-matching against
  `config.packages`.

* plan modules: expose `plan-json-by-id`

Index `config.plan-json.install-plan` by its per-entry `id` so
consumers can look up a single plan entry with one attrset
lookup instead of a linear scan over the install-plan list.
The keys are haskell.nix's per-instance UnitIDs; the values are
the corresponding plan-json entries.

Computed once in
`modules/install-plan/override-package-by-name.nix` (alongside
`package-ids-by-name`) and exposed via the new
`config.plan-json-by-id` option on `modules/plan.nix`.
plan modules: expose `plan-json-by-id`
Index `config.plan-json.install-plan` by its per-entry `id` so
consumers can look up a single plan entry with one attrset
lookup instead of a linear scan over the install-plan list.
The keys are haskell.nix's per-instance UnitIDs; the values are
the corresponding plan-json entries.

Computed once in
`modules/install-plan/override-package-by-name.nix` (alongside
`package-ids-by-name`) and exposed via the new
`config.plan-json-by-id` option on `modules/plan.nix`.
plan modules: expose `identifier.unit-id` and `package-ids-by-name`
Pure additions for consumers that need to look up packages by
plan-id rather than canonical name.

* `package.identifier.unit-id` (modules/package.nix): a new
  optional field carrying plan.json's per-entry `id` (defaults to
  null when not derived from a plan entry).
* `modules/install-plan/planned.nix`: when constructing the
  per-plan-id package entries, populate `package.identifier`
  with `name` / `version` / `unit-id`.  Without this,
  reading `package.identifier.{name,version}` on a plan-id-keyed
  entry threw — the canonical-name-keyed entry already gets a
  real identifier via `load-cabal-plan.nix`, but the per-plan-id
  ones (used when one canonical name has multiple plan instances)
  did not.
* `package-ids-by-name` (modules/plan.nix +
  install-plan/override-package-by-name.nix): a name → [id]
  index over `plan-json.install-plan`.  A canonical pkg-name can
  appear under several plan ids (per-instance UnitIDs that differ
  only in their dep UnitIDs, or a multi-component package split
  across per-component entries); consumers can now iterate the
  ids unambiguously instead of name-matching against
  `config.packages`.
dummy GHC: mirror real GHC's `--info` capabilities and ghc-pkg `id:` field (#2502)
* dummy GHC: mirror real GHC's `--info` capabilities and ghc-pkg `id:` field

Bring plan-to-nix's dummy GHC closer to the real cross GHC it
stands in for, so cabal-install computes the same UnitId hashes
either way.

`--info` capability fields cabal-install reads to decide
configure-args (`--enable-shared` / `--disable-shared`,
`--enable-library-for-ghci` / `--disable-library-for-ghci`,
etc.) — those land in `pkgHashConfigInputs` and feed the UnitId
hash.  The current dummy stops at platform fields, so cabal
silently picks the conservative `--disable-*` defaults.  Mirror
the real cross GHC's reported capabilities, conditioned on
target platform:

  * x86_64-w64-mingw32 (Windows mingw): no `Support shared
    libraries` field, `Support dynamic-too: NO`, `GHC Dynamic:
    NO`, RTS ways without any `_dyn` entries, Stage 1.
  * ghcjs / wasm: stage-1 with only `v debug` RTS ways and no
    dynamic linking.
  * native Linux / Darwin: dynamic everything, Stage 2.

ghc-pkg dump entries:

  * Add the missing `id:` line.  Real `ghc-pkg dump` always
    emits one, and consumers (including cabal-install's solver)
    rely on it.
  * Mirror the `-inplace` convention real GHC uses for in-tree
    libraries (base, ghc-prim, text, ...) by appending `-inplace`
    to both the package's own id and to dep ids referenced from
    other packages.  Skip the suffix for `rts` and
    `system-cxx-std-lib`, which are registered with bare
    `<name>-<version>` ids in the real package conf and would
    otherwise fork dep-id strings from what cabal computes.

* nix-tools: move cabal-install patch back inside nix-tools/ tree

#2501 placed the shared patch at `builder/cabal-install-patches/`
and referenced it from `nix-tools/cabal-install-patches.nix` as
`../builder/cabal-install-patches/...`.  That breaks the static
nix-tools build (e.g. job
`aarch64-darwin.nix-tools.static.zipped.nix-tools-static-no-ifd`,
build 1046198 on ci.zw3rk.com) with

  error: access to absolute path '/nix/store/builder' is forbidden
         in pure evaluation mode (use '--impure' to override)

`cabalProject'` copies `src = ./.` (the nix-tools dir) to the
nix store and re-evaluates module paths from there.  A path that
escapes the source tree resolves to `/nix/store/<...>/../builder`
→ `/nix/store/builder`, which doesn't exist and is rejected
under pure-eval.

Move the patch into `nix-tools/cabal-install-patches/` so the
relative path (`./cabal-install-patches/...`) stays inside the
copied source tree.  Both the regular and static nix-tools
builds still import the same shared module, so they continue to
patch cabal-install identically.

* dummy GHC: convert in-script bash comments to nix comments

The previous commit added two large explanatory `#` comment
blocks *inside* the dummy GHC's shell script (one before the
`${...}` capability-fields interpolation, one before the
`suffix()` bash function in the ghc-pkg-dump script).  Bash
comments inside the `''...''` strings are part of the resulting
script — every word change re-hashes the derivation and busts
caches downstream.

Move both blocks into nix-level `${ # ... "" }` interpolations
that evaluate to the empty string.  The comments stay adjacent
to the code they describe but no longer land in the script,
so re-wording them only churns the .nix file.
dummy GHC: convert in-script bash comments to nix comments
The previous commit added two large explanatory `#` comment
blocks *inside* the dummy GHC's shell script (one before the
`${...}` capability-fields interpolation, one before the
`suffix()` bash function in the ghc-pkg-dump script).  Bash
comments inside the `''...''` strings are part of the resulting
script — every word change re-hashes the derivation and busts
caches downstream.

Move both blocks into nix-level `${ # ... "" }` interpolations
that evaluate to the empty string.  The comments stay adjacent
to the code they describe but no longer land in the script,
so re-wording them only churns the .nix file.
nix-tools: move cabal-install patch back inside nix-tools/ tree
#2501 placed the shared patch at `builder/cabal-install-patches/`
and referenced it from `nix-tools/cabal-install-patches.nix` as
`../builder/cabal-install-patches/...`.  That breaks the static
nix-tools build (e.g. job
`aarch64-darwin.nix-tools.static.zipped.nix-tools-static-no-ifd`,
build 1046198 on ci.zw3rk.com) with

  error: access to absolute path '/nix/store/builder' is forbidden
         in pure evaluation mode (use '--impure' to override)

`cabalProject'` copies `src = ./.` (the nix-tools dir) to the
nix store and re-evaluates module paths from there.  A path that
escapes the source tree resolves to `/nix/store/<...>/../builder`
→ `/nix/store/builder`, which doesn't exist and is rejected
under pure-eval.

Move the patch into `nix-tools/cabal-install-patches/` so the
relative path (`./cabal-install-patches/...`) stays inside the
copied source tree.  Both the regular and static nix-tools
builds still import the same shared module, so they continue to
patch cabal-install identically.
dummy GHC: mirror real GHC's `--info` capabilities and ghc-pkg `id:` field
Bring plan-to-nix's dummy GHC closer to the real cross GHC it
stands in for, so cabal-install computes the same UnitId hashes
either way.

`--info` capability fields cabal-install reads to decide
configure-args (`--enable-shared` / `--disable-shared`,
`--enable-library-for-ghci` / `--disable-library-for-ghci`,
etc.) — those land in `pkgHashConfigInputs` and feed the UnitId
hash.  The current dummy stops at platform fields, so cabal
silently picks the conservative `--disable-*` defaults.  Mirror
the real cross GHC's reported capabilities, conditioned on
target platform:

  * x86_64-w64-mingw32 (Windows mingw): no `Support shared
    libraries` field, `Support dynamic-too: NO`, `GHC Dynamic:
    NO`, RTS ways without any `_dyn` entries, Stage 1.
  * ghcjs / wasm: stage-1 with only `v debug` RTS ways and no
    dynamic linking.
  * native Linux / Darwin: dynamic everything, Stage 2.

ghc-pkg dump entries:

  * Add the missing `id:` line.  Real `ghc-pkg dump` always
    emits one, and consumers (including cabal-install's solver)
    rely on it.
  * Mirror the `-inplace` convention real GHC uses for in-tree
    libraries (base, ghc-prim, text, ...) by appending `-inplace`
    to both the package's own id and to dep ids referenced from
    other packages.  Skip the suffix for `rts` and
    `system-cxx-std-lib`, which are registered with bare
    `<name>-<version>` ids in the real package conf and would
    otherwise fork dep-id strings from what cabal computes.
nix-tools: apply cabal-install unit-id-OS-override patch to regular build too (#2501)
#2499 added the `installed-package-id-os-override.patch` only to
the static nix-tools build (`nix-tools/static/project.nix`).  But
the regular nix-tools build is the one that actually runs
`make-install-plan` for IFD-based plan-nix evaluation, and
without the patch its `cabal-install` reads `buildOS` directly
— so plan-nix unit-ids fork from slice-build unit-ids whenever
the eval system differs from the build system (e.g. evaluating
a cross derivation on Darwin while building it on x86_64-linux).

Extract the patch module to `nix-tools/cabal-install-patches.nix`
and import it from both `nix-tools/overlay.nix` (regular build)
and `nix-tools/static/project.nix` (static build) so they stay
in sync.  Move the patch file from `nix-tools/static/cabal-install-patches/`
to `builder/cabal-install-patches/` alongside the other
cabal-install patches.
nix-tools: apply cabal-install unit-id-OS-override patch to regular build too
#2499 added the `installed-package-id-os-override.patch` only to
the static nix-tools build (`nix-tools/static/project.nix`).  But
the regular nix-tools build is the one that actually runs
`make-install-plan` for IFD-based plan-nix evaluation, and
without the patch its `cabal-install` reads `buildOS` directly
— so plan-nix unit-ids fork from slice-build unit-ids whenever
the eval system differs from the build system (e.g. evaluating
a cross derivation on Darwin while building it on x86_64-linux).

Extract the patch module to `nix-tools/cabal-install-patches.nix`
and import it from both `nix-tools/overlay.nix` (regular build)
and `nix-tools/static/project.nix` (static build) so they stay
in sync.  Move the patch file from `nix-tools/static/cabal-install-patches/`
to `builder/cabal-install-patches/` alongside the other
cabal-install patches.
ghc-pre-existing: add `template-haskell-lift` / `template-haskell-quasiquoter` for GHC 9.14+
GHC 9.14 split `template-haskell` into the bundled wrapper plus
two new pre-existing libraries — `template-haskell-lift` and
`template-haskell-quasiquoter` — that ship in the compiler's
package.conf.d.  Without them in haskell.nix's `ghc-pre-existing`
list, plan-to-nix treats them as reinstallable, the solver tries
to reach Hackage for them, and projects depending (transitively)
on `template-haskell` against GHC 9.14+ fail to plan.

Add both alongside the existing `rts-headers` / `rts-fs` entries
in the same `>= 9.14` block.
foldComponents: skip null `comps.library` (#2500)
The pre-existing comment promised "ensure that comps.library exists
and is not null" but the check only looked at presence
(`comps ? library`).  A null library — which `lookupDependency`
emits for plan entries that have no library component (exe-only
packages, build-tool-only deps) — passed through and was handed to
the fold's accumulator, crashing downstream consumers that read
`.buildable` / `.depends` (e.g. `allComponent`'s `c.buildable`
filter, `flatLibDepends` walking `c.depends`).

Tighten the guard so the fold actually skips null libraries.
foldComponents: skip null `comps.library`
The pre-existing comment promised "ensure that comps.library exists
and is not null" but the check only looked at presence
(`comps ? library`).  A null library — which `lookupDependency`
emits for plan entries that have no library component (exe-only
packages, build-tool-only deps) — passed through and was handed to
the fold's accumulator, crashing downstream consumers that read
`.buildable` / `.depends` (e.g. `allComponent`'s `c.buildable`
filter, `flatLibDepends` walking `c.depends`).

Tighten the guard so the fold actually skips null libraries.
patch cabal-install to pin unit-id OS via env var (#2499)
* static-nix-tools: patch cabal-install to pin unit-id OS via env var

`hashedInstalledPackageId` selects between Long / Short / VeryShort
unit-id formats based on `buildOS` — the OS where cabal-install is
currently executing.  For haskell.nix that's the *eval* platform of
the plan-nix derivation, not the *build* platform where cabal will
later actually do the compile.  When the two differ (e.g. evaluating
on Darwin while building x86_64-linux derivations), plan-nix unit-ids
diverge from the unit-ids slice cabal v2-build computes — every slice
then tries to rebuild every dep from source.

Add a `CABAL_INSTALLED_PACKAGE_ID_OS` env var that overrides
`buildOS` for unit-id format selection.  haskell.nix sets it to the
build platform's OS when invoking `make-install-plan`.

* update nix-tools-static.nix

---------

Co-authored-by: Auto Update Bot <[email protected]>
Add WASM tutorial to docs (#2497)
Documents how to build WebAssembly packages with haskell.nix using a
Nix flake, covering the flake setup, build commands, test execution,
dev shell usage, the overlay stack, and known limitations.

docs/wasm: macOS (aarch64-darwin) is also a supported build host

Fixes incorrect claim that WASM cross-compilation only works on Linux.
aarch64-darwin is known to work; x86_64-darwin is untested.

(cherry picked from commit c314d1654fffd7a83d379366f9685a43afa2c447)
static-nix-tools: patch cabal-install to pin unit-id OS via env var
`hashedInstalledPackageId` selects between Long / Short / VeryShort
unit-id formats based on `buildOS` — the OS where cabal-install is
currently executing.  For haskell.nix that's the *eval* platform of
the plan-nix derivation, not the *build* platform where cabal will
later actually do the compile.  When the two differ (e.g. evaluating
on Darwin while building x86_64-linux derivations), plan-nix unit-ids
diverge from the unit-ids slice cabal v2-build computes — every slice
then tries to rebuild every dep from source.

Add a `CABAL_INSTALLED_PACKAGE_ID_OS` env var that overrides
`buildOS` for unit-id format selection.  haskell.nix sets it to the
build platform's OS when invoking `make-install-plan`.