Build from ekapkgs instead of nixpkgs, keeping GHC 9.10.3 - #152
Conversation
aad8451 to
687875a
Compare
687875a to
b56f520
Compare
The Nix build moves to the ekala package ecosystem: corepkgs for the base system and the compiler, haskell-pkgs for the Hackage snapshot (Stackage LTS 24). flake-parts, haskell-flake, flake-compat and the nixpkgs input go; the flake has two inputs and uses `corepkgs.lib.mkFlake`. Everything, the tools included, is built with one compiler, GHC 9.10.3. GHC stays at 9.10.3: corepkgs carried only 9.8.4 and 9.0.2 bindists, so the 9.10.3 bindist and a `haskell.packages.ghc9103Binary` set were added upstream (ekala-project/corepkgs#178, corrected in #180), with the bindists' terminfo made to link (without that, nothing using haskeline links). The corepkgs input is `github:ekala-project/corepkgs`. `cabal-version: 3.12` and the sources are unchanged. haskell-language-server needs its whole closure built in one Cabal-syntax 3.14 scope (ormolu, fourmolu, cabal-add and the server want 3.14 where GHC 9.10 bundles 3.12, and `extensions` is pinned to 3.12; Cabal refuses the mix). nix/haskell.nix carries that override, the recipe of nixpkgs' configuration-common.nix and of ekala-project/haskell-pkgs#4; it goes when the fix lands upstream, in haskell-pkgs or corepkgs. Both inputs point at ekala-project. corepkgs' treefmt-nix input drags nixpkgs into the lock for a formatter this flake never evaluates; it follows nix-lib instead, so the lock and the cache push carry no nixpkgs checkout. Every entry point is preserved: `nix build`, `nix run .`/`.#repl`/`.#lsp`/ `.#format`/`.#format-lint`/`.#push-cachix`, the per-executable apps CI and the Emacs mode use, `nix develop`, `nix flake check`, `nix-build` and `nix-shell`. The default dev shell is the minimum that builds the project (GHC and cabal-install); `nix develop .#full` adds haskell-language-server, hlint, stylish-haskell and ghcid; hoogle is no longer built for either. The Nix code lives in nix/: haskell.nix names the compiler and the tools, telomare.nix is the package policy (built from `cabal sdist`, tests on, no haddock or profiling, as haskell-flake did), tools.nix the shell apps, devshell.nix, and pins.nix/pkgs.nix/legacy.nix for default.nix and shell.nix, which read the pins from flake.lock. The package expression is a checked-in cabal2nix file: ekapkgs does not define `cabal2nix-unwrapped`, so `callCabal2nix` cannot evaluate; a dependency missing from the file fails `Setup configure` loudly, and nothing is imported from a derivation any more. Shell apps carry an explicit shellcheck check phase, since `writeShellApplication`'s default refers to a `shellcheck-minimal` ekapkgs does not define; shellcheck and cachix come from the 9.10.3 set rather than haskell-pkgs' 9.8.4-built top level. The apps use `gitMinimal`: they only list and read tracked files, and the full git brings its manual into the closure. CI's install-nix-action and cachix-action move to v31 and v17. `cabal-install` is marked unbroken (the snapshot flags it with no broken dependency) and built against the snapshot's Cabal 3.16, which cabal-install 3.16 requires and GHC 9.10's bundled 3.12 is not. `apps.telomare-lsp` carries the version stamp like `apps.lsp`; `systems` is x86_64-linux until corepkgs marks aarch64-linux supported. CI pulls from `ekala-corepkgs` too and accepts the flake's nixConfig. Verified on x86_64-linux: the package builds from the sdist tarball with its five suites passing; `nix run .#telomare-repl -- --expr 'succ 7'` gives 8; `nix-build` yields the flake's store path; `nix develop .#full -c haskell-language-server --version` reports 2.13.0.0 on GHC 9.10.3 and the server typechecks the 26 modules under src/; against the nixpkgs build of master, `tictactoe.telc`, the simpleplus run transcript, its certificate and its meter are byte-identical.
b56f520 to
53d51b9
Compare
|
For this project, nixpkgs offers better-integrated Haskell tooling and broader platform support, at the cost of a large monolithic package set. Ekapkgs lets us consume smaller, independently maintained core and Haskell repositories; this PR also makes build policy explicit and removes evaluation-time Cabal-to-Nix generation. The tradeoff is more local maintenance: HLS/Cabal overrides, a checked-in dependency expression, an externally installed Cachix, and currently x86_64-linux-only outputs. The result is easier to inspect, but not more succinct overall: the migration adds Nix code even though the flake entry point is smaller. |
|
The previous comment was written by astra. One of the biggest upsides is that the ekapkgs implementation doesn't use IFD. It is a bit clearer, but we lose a bit of succinctness (although gain clarity?). I was also a bit partial to flake-parts' (haskell-flake) module use (it lets you <> nix configurations) |
Build from ekapkgs instead of nixpkgs
The Nix build moves to the ekala package ecosystem:
corepkgsfor the base system and the compiler,haskell-pkgsfor the Hackage snapshot (Stackage LTS 24). flake-parts, haskell-flake, flake-compat and the nixpkgs input go; the flake has two inputs and usescorepkgs.lib.mkFlake.GHC stays at 9.10.3. corepkgs carried only the 9.8.4 and 9.0.2 bindists; the 9.10.3 bindist and its
haskell.packages.ghc9103Binaryset are now upstream (ekala-project/corepkgs#178, corrected in #180), socorepkgspoints atgithub:ekala-project/corepkgs.cabal-version: 3.12and the sources are unchanged.haskell-language-server builds on this set once its whole closure is put in one Cabal-syntax 3.14 scope (ormolu, fourmolu, cabal-add and the server want 3.14 where GHC 9.10 bundles 3.12).
nix/haskell.nixcarries that override (nixpkgs'configuration-common.nixrecipe, the same as ekala-project/haskell-pkgs#4) so both inputs can point atekala-projecttoday; it goes when the fix lands upstream, in haskell-pkgs or corepkgs.nix develop .#fullhas HLS 2.13.0.0.nixpkgs-free. No input, no import-from-derivation, no nixpkgs path in the closure. corepkgs' treefmt-nix input would pull a nixpkgs checkout into the lock for a formatter this flake never evaluates; it follows
nix-libinstead. (Two contacts remain that are Nix's, not the flake's: fixed-output source tarballs may be substituted from cache.nixos.org, andnix developtakes its interactive bash from the user'snixpkgsregistry entry.)What is preserved — every entry point:
nix build,nix run ./.#repl/.#lsp/.#format/.#format-lint/.#push-cachix, the per-executable apps CI and the Emacs mode use (.#telomare-repl,.#telomare-lsp),nix develop,nix flake check(the package with its five suites, plus format-lint),nix-buildandnix-shell. The project and packaged Haskell tools use GHC 9.10.3; the cache-publishing helper uses the caller's installed Cachix.Why
mkFlakeand not flake-parts + haskell-flake over corepkgs. Both were built and compared against the same pins: the package derivations are identical apart from the sdist source derivation and thetelomarebinaries are byte-identical. The hybrid is 51 lines shorter but needs four more locked inputs (flake-parts, nixpkgs-lib, haskell-flake, flake-compat), a checked-in cabal2nix file anyway (corepkgs defines nocabal2nix-unwrapped, so haskell-flake cannot run cabal2nix at evaluation time),settings.telomare.check = true(corepkgs' builder skips tests by default), and nulling three of haskell-flake's default tools that are broken or absent on this snapshot. Plain Nix overmkFlakeis what corepkgs offers its consumers and needs none of that.What changes
nix/:haskell.nixnames the compiler and the tools,telomare.nixis the package policy (built fromcabal sdist, tests on, no haddock or profiling — what haskell-flake did),tools.nixthe shell apps,devshell.nix, andpins.nix/pkgs.nix/legacy.nixfordefault.nixandshell.nix, which read the pins fromflake.lockinstead of going through flake-compat.nix/telomare-cabal2nix.nix, header has the regeneration command). A dependency missing from the file failsSetup configure, which is the drift check. No import-from-derivation anywhere (master ran cabal2nix in a derivation at every evaluation).checkPhase:writeShellApplication's default refers to ashellcheck-minimalcorepkgs does not define.cabal-installismarkUnbroken(the snapshot flags it with no broken dependency) and gets the snapshot's Cabal 3.16 in its scope: cabal-install 3.16 requires it, and GHC 9.10 bundles 3.12.shellcheck, which validates the shell apps, comes from the same 9.10.3 set rather than haskell-pkgs' top level, avoiding a second GHC closure. The pinned Cachix dependency cannot configureamazonka-core-2.0against GHC 9.10.3, sopush-cachixuses the caller's installed Cachix and fails immediately with installation guidance if it is missing.gitMinimal: they only list and read tracked files, and the full git brings its manual into the closure. CI'sinstall-nix-actionandcachix-actionmove to v31 and v17 (the old ones warn about Node 20).apps.telomare-lspcarries the version stamp likeapps.lsp. The default dev shell is the minimum that builds the project (GHC and cabal-install);nix develop .#fulladds haskell-language-server, hlint, stylish-haskell and ghcid. Hoogle is no longer built for either (one line to re-enable).systemsisx86_64-linuxuntil corepkgs marks aarch64-linux supported.ekala-corepkgsas well and accepts the flake'snixConfig; the flake names both caches.push-cachixenumerates the declared development shells and publishes and verifies both their outputs and theirnix print-dev-envclosures, includingfullwith HLS and the editor tools.checks.push-cachixbuilds and shellchecks the helper in CI without uploading anything.Verified (x86_64-linux, corepkgs
4ef4208d, haskell-pkgs427f504)nix flake check:checks.telomare/checks.default(the library, three executables and five test suites, built from the sdist tarball, all suites passing inside the derivation),checks.format-lint, and the newchecks.push-cachix.nix run .#telomare-repl -- --expr 'succ 7'→8;nix run . -- --certificate simpleplus.telrenders the report;nix run .#lspstarts the server (exits on EOF).nix-buildyields the same store path as the flake's package;nix-shell --run 'cabal --version'→ 3.16.1.0.nix develop .#full -c haskell-language-server --version→2.13.0.0 (GHC: 9.10.3);haskell-language-server srcin that shell →Completed (26 files worked, 0 files failed).nix run .#format-lint: no hints under hlint 3.10 and stylish-haskell 0.15.1.0; the pre-commit hook ran for every commit.Against the nixpkgs build of master (
2md7b96x…):tictactoe.telc(57,651 bytes), the simpleplus run transcript, its certificate and its--meteroutput are byte-identical to this branch's.Cache-helper regression checks using mocked commands: both shell outputs and environment closures are submitted and verified; a failed full environment prevents publishing; missing Cachix produces the expected error. The real full environment was materialized and its closure checked for HLS, ghcid, hlint, and stylish-haskell. No live upload was performed during these checks.
Cost — nothing Haskell is cached upstream;
nix run .#push-cachixfrom a green build fills thetelomarecache with the compiler, the package, the shells and the tools, and CI pulls rather than builds afterwards.