Skip to content

Revamp flake.nix to use haskell-flake (nix build / nix run)#11707

Open
srid wants to merge 3 commits into
jgm:mainfrom
srid:haskell-flake-revamp
Open

Revamp flake.nix to use haskell-flake (nix build / nix run)#11707
srid wants to merge 3 commits into
jgm:mainfrom
srid:haskell-flake-revamp

Conversation

@srid

@srid srid commented Jun 15, 2026

Copy link
Copy Markdown

Summary

Revamps flake.nix to use haskell-flake so that:

  • nix build builds the pandoc executable (from pandoc-cli)
  • nix run runs it
  • nix develop gives a dev shell (cabal, HLS, the wasm toolchain, the various profiling/validation tools — unchanged from before)

This is a fresh take on the stale #9952.

Verified end-to-end from a clean checkout on both platforms (nix build + nix run -- --version + a markdown→HTML conversion):

Linux (x86_64) macOS (aarch64)
nix build
nix run -- --versionpandoc 3.10, +server +lua

A Nix GitHub Actions workflow builds the flake on ubuntu-latest and macos-latest.

Notable points

  • haskell-flake's cabal.project parser can't handle the if arch(wasm32) conditional blocks, so the packages are listed explicitly in the flake instead of auto-discovered.
  • nixpkgs's pinned all-cabal-hashes is too old for pandoc 3.10's dependency versions, so the flake points the Haskell package set at a recent snapshot.
  • The dependencies that nixpkgs ships too old (or, for asciidoc, not at all) for pandoc 3.10's build-depends are pinned to the highest in-range Hackage versions (collected in one bumpedDeps map): typst, citeproc, commonmark*, emojis, djot, pandoc-types, asciidoc, and the hslua 2.5 family. texmath uses the source-repository-package git pin via a flake input.
  • The sub-packages' COPYING.md is a ../COPYING.md symlink that dangles once a sub-directory is isolated as a source; the flake builds them from source (not sdist) and copies the real license file in.

Opening as a draft for discussion (e.g. whether to track the fast-moving pandoc-ecosystem deps as flake inputs instead of Hackage version pins).

🤖 Generated with Claude Code

srid added 2 commits June 15, 2026 12:17
Build the local pandoc packages (pandoc, pandoc-cli, pandoc-lua-engine,
pandoc-server) via haskell-flake (https://haskell.nixos.asia).

  nix build   # -> the pandoc executable (from pandoc-cli)
  nix run     # runs it

cabal.project's source-repository-package (texmath) and the dependency
versions that nixpkgs ships too old for pandoc 3.10 are overridden here.
Builds the flake (`nix build .#default`) on ubuntu-latest and
macos-latest, caching the Nix store between runs.
@srid srid mentioned this pull request Jun 15, 2026
6 tasks
@srid

srid commented Jun 15, 2026

Copy link
Copy Markdown
Author

@jgm I'll undraft this PR once it is ready for review. In the meanwhile, could you please approve the Github workflows?

EDIT: Not critical though, as I got Claude to do it on my fork:

image

@srid srid marked this pull request as ready for review June 15, 2026 18:27
@srid

srid commented Jun 15, 2026

Copy link
Copy Markdown
Author

Okay this is ready for a review!

image

@srid

srid commented Jun 15, 2026

Copy link
Copy Markdown
Author

One design question before taking this further: how would you like the flake to handle dependencies that nixpkgs ships too old (or not at all) for pandoc 3.10?

nixpkgs' Haskell set currently lags pandoc's build-depends, so the flake has to override ~25 packages — e.g. citeproc (0.9 → 0.13), typst (0.8 → 0.10), commonmark* (0.2 → 0.3), pandoc-types (1.23.1 → 1.23.1.2), emojis, djot, asciidoc (not in nixpkgs at all), and the whole hslua 2.5 family. I resolve these by (a) pointing the package set at a recent all-cabal-hashes snapshot and (b) pinning exact in-range Hackage versions (collected in one bumpedDeps map).

Three ways to maintain this going forward — which do you prefer?

  1. Exact Hackage version pins (what's here now) — precise and always matches your build-depends; downside is the all-cabal-hashes pin and the version map need bumping whenever you raise a dependency.
  2. Flake inputs (git) for the fast-moving deps you maintain (texmath, citeproc, typst, the hslua family, …) — nix flake update pulls them automatically; downside is a git ref can drift from the actual released version on Hackage.
  3. Rely on nixpkgs — drop most overrides and only support building pandoc once nixpkgs' Haskell set has caught up to its dependencies.

This mostly determines who carries the update burden and how closely the flake tracks your releases, so your preference would steer the rest of the PR.

@jgm

jgm commented Jun 15, 2026

Copy link
Copy Markdown
Owner

Thanks for doing this and for the detailed writeup!

The thing I don't like about this (not necessarily a deal-breaker) is the need to manually track versions. We already have to do something like this in stack.yaml, and it's a pain; I've considered removing stack.yaml because of it. I need to think about the costs and benefits.

I didn't understand what you said here:

haskell-flake's cabal.project parser can't handle the if arch(wasm32) conditional blocks, so the packages are listed explicitly in the flake instead of auto-discovered.

I didn't see the special versions needed for the wasm32 build listed in the flake?

@srid

srid commented Jun 15, 2026

Copy link
Copy Markdown
Author

I've considered removing stack.yaml because of it. I need to think about the costs and benefits.

What would you use for pandoc development without stack.yaml? Direct cabal, or would you switch to Nix primarily for development?

In any case, I'm happy to assist with the Nix related stuff.

I didn't see the special versions needed for the wasm32 build listed in the flake?

Do we need the flake to build the wasm version as well? I've explicitly ignored it for now.

…truth)

Rather than hand-maintaining a version map, parse stack.yaml's extra-deps at
eval time (IFD: yaml -> json) so the flake follows the same pins pandoc
already maintains there. On a release, bumping stack.yaml updates the flake
automatically.

Only a small nixpkgs-baseline gap (hslua-core/classes/aeson, which stack's
LTS resolver already ships new enough) remains pinned flake-side. The texmath
source-repository-package is also derived from stack.yaml's git entry,
replacing the dedicated texmath flake input.
@srid

srid commented Jun 15, 2026

Copy link
Copy Markdown
Author

In c9e7c50 the flake now derives its dependency overrides (and the texmath git pin) from stack.yaml's extra-deps, parsed at eval time — single source of truth, so bumping stack.yaml on a release updates the flake automatically. Only a 3-package gap (hslua-core/classes/aeson) stays pinned flake-side, since your LTS already ships those new enough but nixpkgs doesn't.

@jgm

jgm commented Jun 23, 2026

Copy link
Copy Markdown
Owner

What would you use for pandoc development without stack.yaml? Direct cabal, or would you switch to Nix primarily for development?

I'd just use cabal. It has worked well for a long time and I haven't used stack in ages.

I will check out your changes here. If it just requires manual intervention on stack.yaml, which I already do, then I think we might have a winner!

Comment thread flake.nix
Comment on lines +51 to +54
# are not pinned in stack.yaml but still need overriding here.
hslua-core = "2.3.2.1";
hslua-classes = "2.3.2";
hslua-aeson = "2.3.2";

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if it would be less fragile to just add these to stack.yaml with a comment to check the nixpkgs version (with a URL for where to look) before removing the lines?

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added these to stack.yaml so that this can be simplified.

Comment thread flake.nix
Comment on lines +58 to +63
# The package name is taken from the repo basename.
gitSources = lib.listToAttrs (map
(d: lib.nameValuePair
(lib.removeSuffix ".git" (baseNameOf d.git))
{ source = builtins.fetchGit { url = d.git; rev = d.commit; allRefs = true; }; })
(builtins.filter builtins.isAttrs stackExtraDeps));

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if the repo basename is different from package name? E.g. my typst-hs, where the package name is typst. This is not uncommon for the pandoc deps I pin to git commits. Another example is the skylighting dependencies. They all live in one repository, with several packages in different subdirectories. This latter problem could be avoided if the stack subdirs is parsed too.

Comment thread flake.nix
Comment on lines +65 to +68
# The sub-packages' `COPYING.md` is a `../COPYING.md` symlink that
# dangles once the sub-directory is isolated as a source, breaking
# the `license-file` copy during install. Replace it with the real
# file from the repo root.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm open to replacing these symlinks with regular files if they are troublesome. That would simplify this.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did this...no more symlinks.

Comment thread flake.nix
Comment on lines +78 to +80
# pandoc 3.10's dependencies (citeproc 0.13, typst 0.10, commonmark
# 0.3, ...) are newer than the all-cabal-hashes snapshot pinned in
# nixpkgs, so `callHackage` can't find them. Point the package set at

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably shouldn't hard-code versions in this comment, as it will get stale.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, but I guess this URL will need to be manually updated every time we rely on a new package from Hackage?

Comment thread flake.nix
Comment on lines +89 to +93
# haskell-flake's cabal.project parser can't handle pandoc's
# `if arch(wasm32)` conditional blocks, so disable auto-discovery
# and list the packages explicitly. Sub-package sources are derived
# from the flake root (`self`) so they are recognized as local
# (i.e. under `projectRoot`).

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would this be a lot simpler if we didn't use if arch()? We could maybe switch to using the --project-file cabal option with a separate wasm-specific cabal project file for the wasm build.

Comment thread flake.nix
Comment on lines +115 to +118
# The sub-package `.cabal` files reference `license-file: COPYING.md`
# via a `../COPYING.md` symlink that dangles once the sub-directory
# is isolated as a source; building from sdist would fail on it. The
# license file isn't needed to compile, so build from source directly.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's this comment and the above comment about COPYING.md. Is one of them obsolete? I was a bit confused about the workaround. Again, the easiest solution might be getting rid of the symlink in our sources.

Comment thread flake.nix
Comment on lines +123 to +124
# The git sources (texmath) and the bumped Hackage deps: skip
# tests/haddock and relax stale bounds against this GHC's boot libs.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's remove the reference to texmath, as that may change and we'll forget to change the comment.

Comment thread flake.nix
Comment on lines +110 to +111
cabalFlags.embed_data_files = true;
# Skip the (slow) test suite and haddock in `nix build`.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a need to set this, given that it is set to True by default in the cabal.project file ?

jgm added a commit that referenced this pull request Jul 12, 2026
...but will make the new nix flake easier. See #11707.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants