Revamp flake.nix to use haskell.nix.

This allows the project to be built using `nix build` and run
using `nix run`.

The basis for this is @srid's work in #11707, with some
simplifications and improvements added on.

stack.yaml is used as a source of truth for the versions to use,
including for git repositories.

Closes #11707.
This commit is contained in:
John MacFarlane
2026-07-18 15:55:56 +02:00
parent d4e350c25a
commit eade305c71
2 changed files with 248 additions and 106 deletions
Generated
+83 -48
View File
@@ -1,5 +1,40 @@
{
"nodes": {
"all-cabal-hashes": {
"flake": false,
"locked": {
"lastModified": 1784356889,
"narHash": "sha256-B92Npl1FUEHj75+eqZ3uGj3S/qWps/fe+bQrjgbAAow=",
"owner": "commercialhaskell",
"repo": "all-cabal-hashes",
"rev": "cff3531b94de08924d254fc5e196e421ef6445c5",
"type": "github"
},
"original": {
"owner": "commercialhaskell",
"ref": "hackage",
"repo": "all-cabal-hashes",
"type": "github"
}
},
"flake-parts": {
"inputs": {
"nixpkgs-lib": "nixpkgs-lib"
},
"locked": {
"lastModified": 1782949081,
"narHash": "sha256-vp6Y/Grm98ESt6ceOkWiHWyZRDV3J1RID4w+6NWK9yA=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "17c9d6cdfc60c64f4ee8d306f9bc0b4ccb51481e",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "flake-parts",
"type": "github"
}
},
"flake-utils": {
"inputs": {
"systems": "systems"
@@ -18,36 +53,18 @@
"type": "github"
}
},
"flake-utils_2": {
"inputs": {
"systems": "systems_2"
},
"locked": {
"lastModified": 1731533236,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"ghc-wasm-meta": {
"inputs": {
"flake-utils": "flake-utils_2",
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs"
},
"locked": {
"host": "gitlab.haskell.org",
"lastModified": 1780507186,
"narHash": "sha256-qp/sLxsc3mHrXyVRTWn0zmqWMg+60G36zUGLeLhGIHQ=",
"lastModified": 1784272731,
"narHash": "sha256-VcoIdIlUXz7fxu7gXbwih9Wy0m+i2wq6yYsSiFMuDs0=",
"owner": "haskell-wasm",
"repo": "ghc-wasm-meta",
"rev": "ba0c8238314857ef1d5db1f20bb17b96178c9bd3",
"rev": "ec050b490a8499a1d1d1a3c093b8e760a20d4ebf",
"type": "gitlab"
},
"original": {
@@ -57,13 +74,28 @@
"type": "gitlab"
}
},
"haskell-flake": {
"locked": {
"lastModified": 1783975872,
"narHash": "sha256-jzVi9BpmBiixQU02srk2PTKqJVRgTZs25/eLxSkZeWI=",
"owner": "srid",
"repo": "haskell-flake",
"rev": "0cd8c0235e81a20aa8c21f798989601f6c8ae40f",
"type": "github"
},
"original": {
"owner": "srid",
"repo": "haskell-flake",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1780383649,
"narHash": "sha256-FS3od1iIyWYz68tqZGGc92Po7Ji+G2NEm6+a4s4RI0w=",
"lastModified": 1784182977,
"narHash": "sha256-Hy8zg2HEcsk4WqowQK3UKRXz1fdQM7Z2un3nSyLbJaQ=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "595ee2b1a31a50efcb8db3824999abb98ec1d0c9",
"rev": "29c22b7761070f37c4c2b0dd6a8777f0bd43e268",
"type": "github"
},
"original": {
@@ -73,25 +105,43 @@
"type": "github"
}
},
"nixpkgs_2": {
"nixpkgs-lib": {
"locked": {
"lastModified": 1780659424,
"narHash": "sha256-9XpsomaGHOBlDCkxXLgT1JTjRs+lYMSlPFOKGnp7tQc=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "5b05148328954d65995fcf531709c0f55689f514",
"lastModified": 1782614948,
"narHash": "sha256-ePjCwr1sNm9NYUqywL7QfK3JnlS015msC+eBu2zKlp8=",
"owner": "nix-community",
"repo": "nixpkgs.lib",
"rev": "db3f255737b94216eb71cce308e2912cf6bc2d7c",
"type": "github"
},
"original": {
"owner": "NixOS",
"owner": "nix-community",
"repo": "nixpkgs.lib",
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1784347607,
"narHash": "sha256-VI5cdo27nEZ3m1SlgB8RvBbrqFUO2/dUgrrLWe407oA=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "31cd72fdba8fa052e437ce7e6879c4fe62def10f",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"all-cabal-hashes": "all-cabal-hashes",
"flake-parts": "flake-parts",
"ghc-wasm-meta": "ghc-wasm-meta",
"haskell-flake": "haskell-flake",
"nixpkgs": "nixpkgs_2"
}
},
@@ -109,21 +159,6 @@
"repo": "default",
"type": "github"
}
},
"systems_2": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",
+165 -58
View File
@@ -1,75 +1,182 @@
{
description = "pandoc";
# Uses haskell-flake (https://haskell.nixos.asia) to build the local Haskell
# packages (pandoc, pandoc-cli, pandoc-lua-engine, pandoc-server) defined in
# cabal.project.
#
# nix build # builds the `pandoc` executable (from pandoc-cli)
# nix run # runs it
# nix develop # dev shell (cabal, HLS, the wasm toolchain, etc.)
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs";
flake-utils.url = "github:numtide/flake-utils";
nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
flake-parts.url = "github:hercules-ci/flake-parts";
haskell-flake.url = "github:srid/haskell-flake";
# 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
# a recent all-cabal-hashes that contains them.
all-cabal-hashes = {
url = "github:commercialhaskell/all-cabal-hashes/hackage";
flake = false;
};
# WASM toolchain, used (in the dev shell) to build pandoc for wasm32.
ghc-wasm-meta.url = "gitlab:haskell-wasm/ghc-wasm-meta?host=gitlab.haskell.org";
};
outputs = { self, nixpkgs, flake-utils, ghc-wasm-meta }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = nixpkgs.legacyPackages.${system};
outputs = inputs@{ flake-parts, ... }:
flake-parts.lib.mkFlake { inherit inputs; } {
systems = [ "x86_64-linux" "aarch64-linux" "aarch64-darwin" "x86_64-darwin" ];
imports = [ inputs.haskell-flake.flakeModule ];
haskellPackages = pkgs.haskellPackages;
# Or, override some dependencies as follows:
# haskellPackages = (pkgs.haskellPackages.override {
# all-cabal-hashes = pkgs.fetchurl {
# # The hash in the URL is just a git commit hash
# url = "https://github.com/commercialhaskell/all-cabal-hashes/archive/2e3f153549871ada6ecbf36719339a5da051bc76.tar.gz";
# sha256 = "ymHZb6wCZlrtKb+T+iOL17jyN8IzA7s52uiamUIkWNI=";
# };
# }).extend(self: super: {
# citeproc = pkgs.haskell.lib.dontCheck (self.callHackage "citeproc" "0.9.0.1" {});
# texmath = self.callHackage "texmath" "0.12.10.2" {};
# });
perSystem = { self', pkgs, lib, system, ... }:
let
# Single source of truth for dependency versions: pandoc's own
# `stack.yaml` `extra-deps`. haskell-flake's package set lags pandoc's
# `build-depends`, so we apply the very same overrides stack does.
# Parsed at eval time (IFD: yaml -> json).
stackExtraDeps = builtins.fromJSON (builtins.readFile
(pkgs.runCommand "stack-extra-deps.json" { } ''
${pkgs.yq-go}/bin/yq -o=json '.extra-deps' \
${inputs.self + "/stack.yaml"} > $out
''));
jailbreakUnbreak = pkg:
pkgs.haskell.lib.doJailbreak (pkg.overrideAttrs (_: { meta = { }; }));
# "hslua-module-doclayout-1.2.1.1" -> name + version
# (Hackage versions contain no '-', so version = last '-'-segment).
parseDep = s:
let parts = lib.splitString "-" s;
in lib.nameValuePair
(lib.concatStringsSep "-" (lib.init parts))
(lib.last parts);
# PUT YOUR PACKAGE NAME HERE:
packageName = "pandoc";
# name -> version, derived from stack.yaml's plain `pkg-version` entries.
bumpedDeps =
lib.listToAttrs
(map parseDep (builtins.filter builtins.isString stackExtraDeps))
// {
# Add things here that go beyond what's in stack.yaml, if needed:
# hslua-core = "2.3.2.1";
};
wasmToolchain = ghc-wasm-meta.packages.${system}.default;
# Alternatively, if you want a specific "bundle":
# wasmToolchain = ghc-wasm-meta.packages.${system}.all_9_14;
in {
packages.${packageName} =
haskellPackages.callCabal2nix packageName self rec {
# Dependency overrides go here
# git `source-repository-package`s from stack.yaml.
# The package name is read from the .cabal file at the source
# root (the repo basename can differ, e.g. typst-hs -> typst).
cabalPackageName = dir:
let
cabalFiles = lib.filterAttrs
(n: t: t == "regular" && lib.hasSuffix ".cabal" n)
(builtins.readDir dir);
in
lib.removeSuffix ".cabal" (lib.head (builtins.attrNames cabalFiles));
gitSources = lib.listToAttrs (lib.concatMap
(d:
let
repo = builtins.fetchGit {
url = d.git;
rev = d.commit;
allRefs = true;
};
srcDirs =
if d ? subdirs
then map (sub: repo + "/${sub}") d.subdirs
else [ repo ];
in
map
(dir: lib.nameValuePair
(cabalPackageName dir)
{ source = dir; })
srcDirs)
(builtins.filter builtins.isAttrs stackExtraDeps));
in
{
haskellProjects.default = {
basePackages = pkgs.haskellPackages.override {
all-cabal-hashes = inputs.all-cabal-hashes;
};
packages.default = self.packages.${system}.${packageName};
defaultPackage = self.packages.${system}.default;
# 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`).
defaults.packages = lib.mkForce { };
devShells.default = pkgs.mkShell {
buildInputs = with pkgs; [
wasmToolchain
haskellPackages.haskell-language-server
haskellPackages.hlint
haskellPackages.cabal-install
haskellPackages.cabal-plan
haskellPackages.hpc
haskellPackages.ghcid
haskellPackages.stylish-haskell
haskellPackages.eventlog2html
haskellPackages.profiterole
haskellPackages.profiteur
zlib.dev
git
gnumake
bashInteractive
ripgrep
unzip
jq
libxml2 # for xmllint
epubcheck # for validate-epub
nodejs # for validate-epub
];
inputsFrom = map (__getAttr "env") (__attrValues self.packages.${system});
packages = {
# Local packages (from cabal.project).
pandoc.source = inputs.self;
pandoc-cli.source = inputs.self + "/pandoc-cli";
pandoc-lua-engine.source = inputs.self + "/pandoc-lua-engine";
pandoc-server.source = inputs.self + "/pandoc-server";
}
// gitSources
// lib.mapAttrs (_: version: { source = version; }) bumpedDeps;
settings = {
pandoc = {
# Set flags (cabal.project is ignored):
cabalFlags.embed_data_files = true;
cabalFlags.http = true;
# Skip the (slow) test suite and haddock in `nix build`.
check = false;
haddock = false;
};
pandoc-cli = {
check = false;
haddock = false;
buildFromSdist = true;
};
pandoc-lua-engine = {
check = false;
haddock = false;
buildFromSdist = true;
};
pandoc-server = {
check = false;
haddock = false;
buildFromSdist = true;
};
# The git sources (texmath) and the bumped Hackage deps: skip
# tests/haddock and relax stale bounds against this GHC's boot libs.
} // lib.genAttrs
(builtins.attrNames bumpedDeps ++ builtins.attrNames gitSources)
(_: { check = false; haddock = false; jailbreak = true; });
# Dev shell (`nix develop`). haskell-flake already provides
# cabal-install, haskell-language-server, ghcid and hlint.
devShell = {
tools = hp: {
inherit (hp)
cabal-plan
hpc
stylish-haskell
eventlog2html
profiterole
profiteur;
};
mkShellArgs.nativeBuildInputs =
[ inputs.ghc-wasm-meta.packages.${system}.default ]
++ (with pkgs; [
zlib.dev
git
gnumake
bashInteractive
ripgrep
unzip
jq
libxml2 # for xmllint
epubcheck # for validate-epub
nodejs # for validate-epub
]);
};
autoWire = [ "packages" "apps" "devShells" ];
};
devShell = self.devShells.${system}.default;
});
# `nix build` / `nix run` -> the `pandoc` executable from pandoc-cli.
packages.default = self'.packages.pandoc-cli;
};
};
}