Skip to content

Commit 52280f2

Browse files
authored
[ifd] cabalProjectToNix (#122)
1 parent 66bf69e commit 52280f2

File tree

5 files changed

+131
-0
lines changed

5 files changed

+131
-0
lines changed

default.nix

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@ let
3838
# overridden with NIX_PATH.
3939
fetchExternal = import ./lib/fetch-external.nix;
4040

41+
mkHackageIndex = indexState: import ./lib/hackageIndex.nix {
42+
inherit (pkgs) runCommand cabal-install;
43+
inherit indexState;
44+
};
45+
4146
# All packages from Hackage as Nix expressions
4247
hackage = import (fetchExternal {
4348
name = "hackage-exprs-source";
@@ -123,6 +128,20 @@ let
123128
update-stackage = self.callPackage ./scripts/update-stackage.nix {};
124129
update-pins = self.callPackage ./scripts/update-pins.nix {};
125130
};
131+
132+
# Make this handy overridable fetch function available.
133+
inherit fetchExternal;
134+
135+
# Takes a haskell src directory runs cabal new-configure and plan-to-nix.
136+
# Resulting nix files are added to nix-plan subdirectory.
137+
callCabalProjectToNix = import ./lib/cabalProjectToNix.nix {
138+
inherit mkHackageIndex;
139+
inherit pkgs;
140+
inherit (pkgs) runCommand cabal-install ghc;
141+
inherit (pkgs.haskellPackages) hpack;
142+
inherit (self) nix-tools;
143+
inherit (pkgs) symlinkJoin;
144+
};
126145
});
127146

128147
in

lib/cabalProjectToNix.nix

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
{ mkHackageIndex, pkgs, runCommand, nix-tools, cabal-install, ghc, hpack, symlinkJoin }:
2+
let defaultGhc = ghc;
3+
defaultCabalInstall = cabal-install;
4+
in { hackageIndexState, src, ghc ? defaultGhc, cabal-install ? defaultCabalInstall }:
5+
let
6+
cabalFiles =
7+
pkgs.lib.cleanSourceWith {
8+
inherit src;
9+
filter = path: type:
10+
type == "directory" ||
11+
pkgs.lib.any (i: (pkgs.lib.hasSuffix i path)) [ ".project" ".cabal" "package.yaml" ];
12+
};
13+
plan = if (builtins.compareVersions cabal-install.version "2.4.0.0") < 0
14+
# cabal-install versions before 2.4 will generate insufficient plan information.
15+
then throw "cabal-install (current version: ${cabal-install.version}) needs to be at least 2.4 for plan-to-nix to work without cabal-to-nix"
16+
else runCommand "plan" {
17+
nativeBuildInputs = [ nix-tools ghc hpack cabal-install pkgs.rsync ];
18+
} ''
19+
tmp=$(mktemp -d)
20+
cd $tmp
21+
cp -r ${cabalFiles}/* .
22+
chmod +w -R .
23+
# warning: this may not generate the proper cabal file.
24+
# hpack allows globbing, and turns that into module lists
25+
# without the source available (we cleaneSourceWith'd it),
26+
# this may not produce the right result.
27+
find . -name package.yaml -exec hpack "{}" \;
28+
HOME=${mkHackageIndex hackageIndexState} cabal new-configure
29+
30+
export LANG=C.utf8 # Needed or stack-to-nix will die on unicode inputs
31+
mkdir -p $out
32+
33+
# ensure we have all our .cabal files (also those generated from package.yaml) files.
34+
# otherwise we'd need to be careful about putting the `cabal-generator = hpack` into
35+
# the nix expression. As we already called `hpack` on all `package.yaml` files we can
36+
# skip that step and just package the .cabal files up as well.
37+
#
38+
# This is also important as `plan-to-nix` will look for the .cabal files when generating
39+
# the relevant `pkgs.nix` file with the local .cabal expressions.
40+
rsync -a --prune-empty-dirs \
41+
--include '*/' --include '*.cabal' --include 'package.yaml' \
42+
--exclude '*' \
43+
$tmp/ $out/
44+
45+
# make sure the path's in the plan.json are relative to $out instead of $tmp
46+
# this is necessary so that plan-to-nix relative path logic can work.
47+
substituteInPlace $tmp/dist-newstyle/cache/plan.json --replace "$tmp" "$out"
48+
49+
# run `plan-to-nix` in $out. This should produce files right there with the
50+
# proper relative paths.
51+
(cd $out && plan-to-nix --plan-json $tmp/dist-newstyle/cache/plan.json -o .)
52+
53+
# move pkgs.nix to default.nix ensure we can just nix `import` the result.
54+
mv $out/pkgs.nix $out/default.nix
55+
'';
56+
in
57+
runCommand "plan-and-src" { nativeBuildInputs = [ pkgs.xorg.lndir pkgs.rsync ]; } ''
58+
mkdir $out
59+
# todo: should we clean `src` to drop any .git, .nix, ... other irelevant files?
60+
lndir -silent "${src}" "$out"
61+
rsync -a ${plan}/ $out/
62+
''

lib/hackageIndex.nix

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{ runCommand, cabal-install
2+
, indexState ? "2019-04-24T21:34:04Z"
3+
} :
4+
let
5+
# To avoid downloading more data than necessary this will provide a base.
6+
cachedState = runCommand "hackage-${builtins.substring 0 4 indexState}" {} ''
7+
mkdir -p $out
8+
HOME=$out ${cabal-install}/bin/cabal update --index-state='${builtins.substring 0 4 indexState}-01-01T00:00:00Z'
9+
'';
10+
in runCommand "hackage-${builtins.replaceStrings [":"] [""] indexState}" {} ''
11+
mkdir -p $out
12+
cp -r ${cachedState}/.cabal $out
13+
chmod +w -R $out/.cabal
14+
sed -i.back -e "s|${cachedState}|$out|g" $out/.cabal/config
15+
HOME=$out ${cabal-install}/bin/cabal update --index-state='${indexState}'
16+
''
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
{ stdenv, mkCabalProjectPkgSet, callCabalProjectToNix }:
2+
3+
with stdenv.lib;
4+
5+
let
6+
pkgSet = mkCabalProjectPkgSet {
7+
plan-pkgs = import (callCabalProjectToNix {
8+
hackageIndexState = "2019-04-24T21:34:04Z";
9+
# reuse the cabal-simple test project
10+
src = ../cabal-simple;
11+
});
12+
};
13+
packages = pkgSet.config.hsPkgs;
14+
in
15+
stdenv.mkDerivation {
16+
name = "callStackToNix-test";
17+
18+
buildCommand = ''
19+
exe="${packages.cabal-simple.components.exes.cabal-simple}/bin/cabal-simple"
20+
21+
printf "checking whether executable runs... " >& 2
22+
$exe
23+
24+
touch $out
25+
'';
26+
27+
meta.platforms = platforms.all;
28+
29+
passthru = {
30+
# Attributes used for debugging with nix repl
31+
inherit pkgSet packages;
32+
};
33+
}

test/default.nix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ in {
1616
builder-haddock = haskell.callPackage ./builder-haddock {};
1717
stack-simple = haskell.callPackage ./stack-simple {};
1818
callStackToNix = haskell.callPackage ./callStackToNix {};
19+
callCabalProjectToNix = haskell.callPackage ./call-cabal-project-to-nix {};
1920

2021
# Run unit tests with: nix-instantiate --eval --strict -A unit
2122
# An empty list means success.

0 commit comments

Comments
 (0)