Skip to content

feat: cabal2nix integration for IFD-less evalutation #382

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Dec 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,13 @@
};
};

test-cabal2nix = {
dir = "test/cabal2nix";
overrideInputs = {
inherit nixpkgs flake-parts haskell-flake;
};
};

test-with-subdir = {
dir = "test/with-subdir";
overrideInputs = {
Expand Down
46 changes: 35 additions & 11 deletions nix/build-haskell-package.nix
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
}:

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

name: root:
lib.pipe root
[
# Avoid rebuilding because of changes in parent directories
(mkNewStorePath "source-${name}")
(x: log.traceDebug "${name}.mkNewStorePath ${x.outPath}" x)
# Avoid rebuilding because of changes in parent directories
mkNewStorePath = name: src:
let newSrc = mkNewStorePath' name src;
in log.traceDebug "${name}.mkNewStorePath ${newSrc}" newSrc;

callCabal2nix = name: src:
let pkg = self.callCabal2nix name src { };
in log.traceDebug "${name}.callCabal2nix src=${src} deriver=${pkg.cabal2nixDeriver.outPath}" pkg;

# Use cached cabal2nix generated nix expression if present, otherwise use IFD (callCabal2nix)
callCabal2NixUnlessCached = name: src: cabal2nixFile:
let path = "${src}/${cabal2nixFile}";
in
if builtins.pathExists path
then
callPackage name path
else
callCabal2nix name src;

callPackage = name: nixFilePath:
let pkg = self.callPackage nixFilePath { };
in log.traceDebug "${name}.callPackage[cabal2nix] ${nixFilePath}" pkg;

callHackage = name: version:
let pkg = self.callHackage name version { };
in log.traceDebug "${name}.callHackage ver=${version}" pkg;
in

(root: self.callCabal2nix name root { })
(x: log.traceDebug "${name}.cabal2nixDeriver ${x.cabal2nixDeriver.outPath}" x)
]
name: cfg:
# If 'source' is a path, we treat it as such. Otherwise, we assume it's a version (from hackage).
if lib.types.path.check cfg.source
then
callCabal2NixUnlessCached name (mkNewStorePath name cfg.source) cfg.cabal2NixFile
else
callHackage name cfg.source
10 changes: 1 addition & 9 deletions nix/modules/project/packages/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -58,16 +58,8 @@ in
build-haskell-package = import ../../../build-haskell-package.nix {
inherit pkgs lib self log;
};
getOrMkPackage = name: cfg:
if lib.types.path.check cfg.source
then
log.traceDebug "${name}.callCabal2nix ${cfg.source}"
(build-haskell-package name cfg.source)
else
log.traceDebug "${name}.callHackage ${cfg.source}"
(self.callHackage name cfg.source { });
in
lib.mapAttrs getOrMkPackage project.config.packages;
lib.mapAttrs build-haskell-package project.config.packages;
};
};
}
10 changes: 10 additions & 0 deletions nix/modules/project/packages/package.nix
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,16 @@ in
'';
};

cabal2NixFile = lib.mkOption {
type = lib.types.str;
description = ''
Filename of the cabal2nix generated nix expression.

This gets used if it exists instead of using IFD (callCabal2nix).
'';
default = "cabal.nix";
};

cabal.executables = mkOption {
type = types.nullOr (types.listOf types.str);
description = ''
Expand Down
11 changes: 11 additions & 0 deletions test/cabal2nix/default.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{ mkDerivation, base, lib, random }:
mkDerivation {
pname = "haskell-flake-test";
version = "0.1.0.0";
src = ./.;
isLibrary = false;
isExecutable = true;
executableHaskellDepends = [ base random ];
license = "unknown";
mainProgram = "haskell-flake-test";
}
35 changes: 35 additions & 0 deletions test/cabal2nix/flake.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
# Disable IFD for this test.
nixConfig = {
allow-import-from-derivation = false;
};

# Since there is no flake.lock file (to avoid incongruent haskell-flake
# pinning), we must specify revisions for *all* inputs to ensure
# reproducibility.
inputs = {
nixpkgs = { };
flake-parts = { };
haskell-flake = { };
};

outputs = inputs@{ self, nixpkgs, flake-parts, ... }:
flake-parts.lib.mkFlake { inherit inputs; } {
systems = nixpkgs.lib.systems.flakeExposed;
imports = [
inputs.haskell-flake.flakeModule
];
debug = true;
perSystem = { config, self', pkgs, lib, ... }: {
haskellProjects.default = {
# If IFD is disabled,
# we need to specify the pre-generated `cabal2nix` expressions
# file to haskell-flake for the package,
# otherwise build would fail as it would use `callCabal2nix` function
# which uses IFD.
packages.haskell-flake-test.cabal2NixFile = "default.nix";
settings = { };
};
};
};
}
20 changes: 20 additions & 0 deletions test/cabal2nix/haskell-flake-test.cabal
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
cabal-version: 3.0
name: haskell-flake-test
version: 0.1.0.0
license: NONE
author: Joe
maintainer: [email protected]
build-type: Simple

common warnings
ghc-options: -Wall

executable haskell-flake-test
import: warnings
main-is: Main.hs
build-depends:
base,
random
hs-source-dirs: src
default-language: Haskell2010

6 changes: 6 additions & 0 deletions test/cabal2nix/src/Main.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module Main where

import System.Random

main :: IO ()
main = putStrLn "Hello, Haskell!"