[ifd] cabalProjectToNix (#122)

This commit is contained in:
Moritz Angermann 2019-05-18 22:00:28 +08:00 committed by GitHub
parent 66bf69ec9e
commit 52280f2324
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 131 additions and 0 deletions

View File

@ -38,6 +38,11 @@ let
# overridden with NIX_PATH.
fetchExternal = import ./lib/fetch-external.nix;
mkHackageIndex = indexState: import ./lib/hackageIndex.nix {
inherit (pkgs) runCommand cabal-install;
inherit indexState;
};
# All packages from Hackage as Nix expressions
hackage = import (fetchExternal {
name = "hackage-exprs-source";
@ -123,6 +128,20 @@ let
update-stackage = self.callPackage ./scripts/update-stackage.nix {};
update-pins = self.callPackage ./scripts/update-pins.nix {};
};
# Make this handy overridable fetch function available.
inherit fetchExternal;
# Takes a haskell src directory runs cabal new-configure and plan-to-nix.
# Resulting nix files are added to nix-plan subdirectory.
callCabalProjectToNix = import ./lib/cabalProjectToNix.nix {
inherit mkHackageIndex;
inherit pkgs;
inherit (pkgs) runCommand cabal-install ghc;
inherit (pkgs.haskellPackages) hpack;
inherit (self) nix-tools;
inherit (pkgs) symlinkJoin;
};
});
in

62
lib/cabalProjectToNix.nix Normal file
View File

@ -0,0 +1,62 @@
{ mkHackageIndex, pkgs, runCommand, nix-tools, cabal-install, ghc, hpack, symlinkJoin }:
let defaultGhc = ghc;
defaultCabalInstall = cabal-install;
in { hackageIndexState, src, ghc ? defaultGhc, cabal-install ? defaultCabalInstall }:
let
cabalFiles =
pkgs.lib.cleanSourceWith {
inherit src;
filter = path: type:
type == "directory" ||
pkgs.lib.any (i: (pkgs.lib.hasSuffix i path)) [ ".project" ".cabal" "package.yaml" ];
};
plan = if (builtins.compareVersions cabal-install.version "2.4.0.0") < 0
# cabal-install versions before 2.4 will generate insufficient plan information.
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"
else runCommand "plan" {
nativeBuildInputs = [ nix-tools ghc hpack cabal-install pkgs.rsync ];
} ''
tmp=$(mktemp -d)
cd $tmp
cp -r ${cabalFiles}/* .
chmod +w -R .
# warning: this may not generate the proper cabal file.
# hpack allows globbing, and turns that into module lists
# without the source available (we cleaneSourceWith'd it),
# this may not produce the right result.
find . -name package.yaml -exec hpack "{}" \;
HOME=${mkHackageIndex hackageIndexState} cabal new-configure
export LANG=C.utf8 # Needed or stack-to-nix will die on unicode inputs
mkdir -p $out
# ensure we have all our .cabal files (also those generated from package.yaml) files.
# otherwise we'd need to be careful about putting the `cabal-generator = hpack` into
# the nix expression. As we already called `hpack` on all `package.yaml` files we can
# skip that step and just package the .cabal files up as well.
#
# This is also important as `plan-to-nix` will look for the .cabal files when generating
# the relevant `pkgs.nix` file with the local .cabal expressions.
rsync -a --prune-empty-dirs \
--include '*/' --include '*.cabal' --include 'package.yaml' \
--exclude '*' \
$tmp/ $out/
# make sure the path's in the plan.json are relative to $out instead of $tmp
# this is necessary so that plan-to-nix relative path logic can work.
substituteInPlace $tmp/dist-newstyle/cache/plan.json --replace "$tmp" "$out"
# run `plan-to-nix` in $out. This should produce files right there with the
# proper relative paths.
(cd $out && plan-to-nix --plan-json $tmp/dist-newstyle/cache/plan.json -o .)
# move pkgs.nix to default.nix ensure we can just nix `import` the result.
mv $out/pkgs.nix $out/default.nix
'';
in
runCommand "plan-and-src" { nativeBuildInputs = [ pkgs.xorg.lndir pkgs.rsync ]; } ''
mkdir $out
# todo: should we clean `src` to drop any .git, .nix, ... other irelevant files?
lndir -silent "${src}" "$out"
rsync -a ${plan}/ $out/
''

16
lib/hackageIndex.nix Normal file
View File

@ -0,0 +1,16 @@
{ runCommand, cabal-install
, indexState ? "2019-04-24T21:34:04Z"
} :
let
# To avoid downloading more data than necessary this will provide a base.
cachedState = runCommand "hackage-${builtins.substring 0 4 indexState}" {} ''
mkdir -p $out
HOME=$out ${cabal-install}/bin/cabal update --index-state='${builtins.substring 0 4 indexState}-01-01T00:00:00Z'
'';
in runCommand "hackage-${builtins.replaceStrings [":"] [""] indexState}" {} ''
mkdir -p $out
cp -r ${cachedState}/.cabal $out
chmod +w -R $out/.cabal
sed -i.back -e "s|${cachedState}|$out|g" $out/.cabal/config
HOME=$out ${cabal-install}/bin/cabal update --index-state='${indexState}'
''

View File

@ -0,0 +1,33 @@
{ stdenv, mkCabalProjectPkgSet, callCabalProjectToNix }:
with stdenv.lib;
let
pkgSet = mkCabalProjectPkgSet {
plan-pkgs = import (callCabalProjectToNix {
hackageIndexState = "2019-04-24T21:34:04Z";
# reuse the cabal-simple test project
src = ../cabal-simple;
});
};
packages = pkgSet.config.hsPkgs;
in
stdenv.mkDerivation {
name = "callStackToNix-test";
buildCommand = ''
exe="${packages.cabal-simple.components.exes.cabal-simple}/bin/cabal-simple"
printf "checking whether executable runs... " >& 2
$exe
touch $out
'';
meta.platforms = platforms.all;
passthru = {
# Attributes used for debugging with nix repl
inherit pkgSet packages;
};
}

View File

@ -16,6 +16,7 @@ in {
builder-haddock = haskell.callPackage ./builder-haddock {};
stack-simple = haskell.callPackage ./stack-simple {};
callStackToNix = haskell.callPackage ./callStackToNix {};
callCabalProjectToNix = haskell.callPackage ./call-cabal-project-to-nix {};
# Run unit tests with: nix-instantiate --eval --strict -A unit
# An empty list means success.