Skip to content

Commit d8a21e6

Browse files
authored
Support for cabal2nix for IFD-free eval (#382)
If a `cabal.nix` file (customizable) exists, haskell-flake will use `callPackage` on it. This file should ideally be auto-managed by https://github.com/cachix/git-hooks.nix Note that IFD will still be needed if you override package sources (to source paths or to Hackage versions).
1 parent f4e3cf1 commit d8a21e6

File tree

8 files changed

+125
-20
lines changed

8 files changed

+125
-20
lines changed

flake.nix

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,13 @@
6565
};
6666
};
6767

68+
test-cabal2nix = {
69+
dir = "test/cabal2nix";
70+
overrideInputs = {
71+
inherit nixpkgs flake-parts haskell-flake;
72+
};
73+
};
74+
6875
test-with-subdir = {
6976
dir = "test/with-subdir";
7077
overrideInputs = {

nix/build-haskell-package.nix

Lines changed: 35 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
}:
1313

1414
let
15-
mkNewStorePath = name: src:
15+
mkNewStorePath' = name: src:
1616
# Since 'src' may be a subdirectory of a store path
1717
# (in string form, which means that it isn't automatically
1818
# copied), the purpose of cleanSourceWith here is to create a
@@ -22,15 +22,39 @@ let
2222
name = "${name}";
2323
inherit src;
2424
};
25-
in
2625

27-
name: root:
28-
lib.pipe root
29-
[
30-
# Avoid rebuilding because of changes in parent directories
31-
(mkNewStorePath "source-${name}")
32-
(x: log.traceDebug "${name}.mkNewStorePath ${x.outPath}" x)
26+
# Avoid rebuilding because of changes in parent directories
27+
mkNewStorePath = name: src:
28+
let newSrc = mkNewStorePath' name src;
29+
in log.traceDebug "${name}.mkNewStorePath ${newSrc}" newSrc;
30+
31+
callCabal2nix = name: src:
32+
let pkg = self.callCabal2nix name src { };
33+
in log.traceDebug "${name}.callCabal2nix src=${src} deriver=${pkg.cabal2nixDeriver.outPath}" pkg;
34+
35+
# Use cached cabal2nix generated nix expression if present, otherwise use IFD (callCabal2nix)
36+
callCabal2NixUnlessCached = name: src: cabal2nixFile:
37+
let path = "${src}/${cabal2nixFile}";
38+
in
39+
if builtins.pathExists path
40+
then
41+
callPackage name path
42+
else
43+
callCabal2nix name src;
44+
45+
callPackage = name: nixFilePath:
46+
let pkg = self.callPackage nixFilePath { };
47+
in log.traceDebug "${name}.callPackage[cabal2nix] ${nixFilePath}" pkg;
48+
49+
callHackage = name: version:
50+
let pkg = self.callHackage name version { };
51+
in log.traceDebug "${name}.callHackage ver=${version}" pkg;
52+
in
3353

34-
(root: self.callCabal2nix name root { })
35-
(x: log.traceDebug "${name}.cabal2nixDeriver ${x.cabal2nixDeriver.outPath}" x)
36-
]
54+
name: cfg:
55+
# If 'source' is a path, we treat it as such. Otherwise, we assume it's a version (from hackage).
56+
if lib.types.path.check cfg.source
57+
then
58+
callCabal2NixUnlessCached name (mkNewStorePath name cfg.source) cfg.cabal2NixFile
59+
else
60+
callHackage name cfg.source

nix/modules/project/packages/default.nix

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -58,16 +58,8 @@ in
5858
build-haskell-package = import ../../../build-haskell-package.nix {
5959
inherit pkgs lib self log;
6060
};
61-
getOrMkPackage = name: cfg:
62-
if lib.types.path.check cfg.source
63-
then
64-
log.traceDebug "${name}.callCabal2nix ${cfg.source}"
65-
(build-haskell-package name cfg.source)
66-
else
67-
log.traceDebug "${name}.callHackage ${cfg.source}"
68-
(self.callHackage name cfg.source { });
6961
in
70-
lib.mapAttrs getOrMkPackage project.config.packages;
62+
lib.mapAttrs build-haskell-package project.config.packages;
7163
};
7264
};
7365
}

nix/modules/project/packages/package.nix

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,16 @@ in
2323
'';
2424
};
2525

26+
cabal2NixFile = lib.mkOption {
27+
type = lib.types.str;
28+
description = ''
29+
Filename of the cabal2nix generated nix expression.
30+
31+
This gets used if it exists instead of using IFD (callCabal2nix).
32+
'';
33+
default = "cabal.nix";
34+
};
35+
2636
cabal.executables = mkOption {
2737
type = types.nullOr (types.listOf types.str);
2838
description = ''

test/cabal2nix/default.nix

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{ mkDerivation, base, lib, random }:
2+
mkDerivation {
3+
pname = "haskell-flake-test";
4+
version = "0.1.0.0";
5+
src = ./.;
6+
isLibrary = false;
7+
isExecutable = true;
8+
executableHaskellDepends = [ base random ];
9+
license = "unknown";
10+
mainProgram = "haskell-flake-test";
11+
}

test/cabal2nix/flake.nix

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
{
2+
# Disable IFD for this test.
3+
nixConfig = {
4+
allow-import-from-derivation = false;
5+
};
6+
7+
# Since there is no flake.lock file (to avoid incongruent haskell-flake
8+
# pinning), we must specify revisions for *all* inputs to ensure
9+
# reproducibility.
10+
inputs = {
11+
nixpkgs = { };
12+
flake-parts = { };
13+
haskell-flake = { };
14+
};
15+
16+
outputs = inputs@{ self, nixpkgs, flake-parts, ... }:
17+
flake-parts.lib.mkFlake { inherit inputs; } {
18+
systems = nixpkgs.lib.systems.flakeExposed;
19+
imports = [
20+
inputs.haskell-flake.flakeModule
21+
];
22+
debug = true;
23+
perSystem = { config, self', pkgs, lib, ... }: {
24+
haskellProjects.default = {
25+
# If IFD is disabled,
26+
# we need to specify the pre-generated `cabal2nix` expressions
27+
# file to haskell-flake for the package,
28+
# otherwise build would fail as it would use `callCabal2nix` function
29+
# which uses IFD.
30+
packages.haskell-flake-test.cabal2NixFile = "default.nix";
31+
settings = { };
32+
};
33+
};
34+
};
35+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
cabal-version: 3.0
2+
name: haskell-flake-test
3+
version: 0.1.0.0
4+
license: NONE
5+
author: Joe
6+
maintainer: [email protected]
7+
build-type: Simple
8+
9+
common warnings
10+
ghc-options: -Wall
11+
12+
executable haskell-flake-test
13+
import: warnings
14+
main-is: Main.hs
15+
build-depends:
16+
base,
17+
random
18+
hs-source-dirs: src
19+
default-language: Haskell2010
20+

test/cabal2nix/src/Main.hs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
module Main where
2+
3+
import System.Random
4+
5+
main :: IO ()
6+
main = putStrLn "Hello, Haskell!"

0 commit comments

Comments
 (0)