1
1
mirror of https://github.com/nmattia/snack.git synced 2024-09-11 11:55:36 +03:00

Add snack ghci

This commit is contained in:
Nicolas Mattia 2018-05-12 00:48:10 +02:00
parent ac18afcf88
commit 54944a8cd8
18 changed files with 75 additions and 94 deletions

View File

@ -6,7 +6,15 @@ import (import nixpkgs) {
(self: super: { snack-lib-with = extra: import ../snack extra;} )
(self: super: { snack = self.writeScriptBin "snack"
''
${self.nix}/bin/nix-build snack.nix
set -e
case $1 in
build)
${self.nix}/bin/nix-build snack.nix
;;
ghci)
res=$(${self.nix}/bin/nix-build -A ghci snack.nix)
$res/bin/ghci
esac
''; })
];
}

View File

@ -7,18 +7,6 @@
set -eux
pushd tests/flat
snack build
popd
pushd tests/nested
snack build
popd
pushd tests/dependencies
snack build
popd
pushd tests/packages
snack build
popd

View File

@ -6,13 +6,8 @@
# change: when a module is built with its dependencies, the flags for the
# dependencies change as well, which causes them to be recompiled
{ pkgs ? import (../nix) {} # nixpkgs
, dependencies ? [] # The list of haskell dependencies, e.g. conduit, mtl, etc
, ghcWith ? pkgs.haskellPackages.ghcWithPackages
}:
let
# GHC with the package DB
ghc = ghcWith (ps: map (p: ps.${p}) dependencies);
# Takes a (string) filepath and creates a derivation for that file (and for
# that file only)
singleOut = base: file:
@ -77,13 +72,10 @@ let
isMain
) true;
buildFrom = base: modName: linkModuleObjects base
(makeModuleSpecRec base modName);
buildModule = base: mod:
buildModule = ghc: base: mod:
let
objectName = mod.moduleName;
builtDeps = map (buildModule base) mod.moduleDependencies;
builtDeps = map (buildModule ghc base) mod.moduleDependencies;
depsDirs = map (x: x + "/") builtDeps;
makeSymtree =
if pkgs.lib.lists.length depsDirs >= 1
@ -176,7 +168,7 @@ let
# Returns an attribute set where the keys are the module names and the values
# are the '.o's
flattenModuleObjects = base: mod':
flattenModuleObjects = ghc: base: mod':
let
go = mod: attrs0:
let
@ -193,15 +185,16 @@ let
then acc
else acc //
{ "${elem.moduleName}" =
"${buildModule base elem}/${objectName elem}";
"${buildModule ghc base elem}/${objectName elem}";
};
in
pkgs.lib.lists.foldl f attrs1 mod.moduleDependencies;
in go mod' {};
linkModuleObjects = base: mod:
# TODO: it's sad that we pass ghcWithDeps + dependencies
linkModuleObjects = ghc: dependencies: base: mod:
let
objAttrs = flattenModuleObjects base mod;
objAttrs = flattenModuleObjects ghc base mod;
objList = pkgs.lib.attrsets.mapAttrsToList (x: y: y) objAttrs;
packageList = map (p: "-package ${p}") dependencies;
in pkgs.stdenv.mkDerivation
@ -211,22 +204,65 @@ let
''
source $stdenv/setup
mkdir -p $out
ghc \
${ghc}/bin/ghc \
${pkgs.lib.strings.escapeShellArgs packageList} \
${pkgs.lib.strings.escapeShellArgs objList} \
-o $out/out
'';
buildInputs =
[ ghc
];
};
# Returns a list of all module names depended on in the module spec
allModuleNames = modSpec:
[ modSpec.moduleName ] ++ (pkgs.lib.lists.concatMap allModuleNames modSpec.moduleDependencies);
# Write a new ghci executable that loads all the modules defined in the
# module spec
ghciExecutable = ghc: base: modSpec:
let
ghciArgs = pkgs.lib.strings.escapeShellArgs absoluteModuleFiles;
absoluteModuleFiles = map prependBase moduleFiles;
moduleFiles = map moduleToFile modules;
modules = allModuleNames modSpec;
prependBase = f: builtins.toString base + "/${f}";
in
pkgs.symlinkJoin
{ name = "ghci";
paths = [ ghc ];
postBuild =
''
source $stdenv/setup
wrapProgram "$out/bin/ghci" \
--add-flags "${ghciArgs}"
'';
buildInputs = [pkgs.makeWrapper];
};
snack = executable:
{
build =
let
ghc = pkgs.haskellPackages.ghcWithPackages
(ps: map (p: ps.${p}) deps);
deps = executable.dependencies;
base = executable.src;
mainModName = executable.main;
in linkModuleObjects ghc deps base (makeModuleSpecRec base mainModName);
ghci =
let
ghc = pkgs.haskellPackages.ghcWithPackages
(ps: map (p: ps.${p}) deps);
deps = executable.dependencies;
base = executable.src;
mainModName = executable.main;
in ghciExecutable ghc base (makeModuleSpecRec base mainModName);
};
## TODO: use ghc -M for module dependencies
in
{
inherit
buildFrom
linkModuleObjects
makeModuleSpec
snack
;
}

View File

@ -1,3 +0,0 @@
# This "snack" passing is ugly, figure out a nice way of passing snack-lib
with (import ../../nix {}).snack-lib;
buildFrom ./src "Foo"

View File

@ -1,3 +0,0 @@
module BarFoo where
barFoo = "bar foo"

View File

@ -1,2 +0,0 @@
module Data.Foo where
dataFoo = "Data, Foo."

View File

@ -1,10 +0,0 @@
import Data.Foo
import Haskell
import qualified FooBar
import qualified BarFoo as F
main = do
putStrLn dataFoo
putStrLn haskell
putStrLn FooBar.fooBar
putStrLn F.barFoo

View File

@ -1,2 +0,0 @@
module FooBar where
fooBar = "foo bar"

View File

@ -1,2 +0,0 @@
module Haskell where
haskell = "haskell!"

View File

@ -1,4 +0,0 @@
import B
main :: IO ()
main = putStrLn (var ++ ", world!")

View File

@ -1,4 +0,0 @@
module B where
var :: String
var = "Hello"

View File

@ -1,3 +0,0 @@
# Flat
Makes sure that two modules, `A.hs` and `B.hs`, can be built into an executable

View File

@ -1,6 +0,0 @@
# This "snack" passing is ugly, figure out a nice way of passing snack-lib
with (import ../../nix {}).snack-lib;
let
modB = makeModuleSpec "B" [] false;
modA = makeModuleSpec "A" [modB] true;
in linkModuleObjects ./. modA

View File

@ -1,3 +0,0 @@
import Foo.B.C
main = putStrLn var

View File

@ -1,3 +0,0 @@
module Foo.B.C where
var = "Hello, World!"

View File

@ -1,4 +0,0 @@
# Nested
Make sure that nested modules (e.g. `Foo.A` and `Foo.B.C`) can be built into an
executable

View File

@ -1,6 +0,0 @@
# This "snack" passing is ugly, figure out a nice way of passing snack-lib
with (import ../../nix {}).snack-lib;
let
modB = makeModuleSpec "Foo.B.C" [] false;
modA = makeModuleSpec "Foo.A" [modB] true;
in linkModuleObjects ./. modA

View File

@ -1,5 +1,9 @@
# This "snack" passing is ugly, figure out a nice way of passing snack-lib
with (import ../../nix {}).snack-lib-with
{ dependencies = ["conduit"] ;
};
buildFrom ./src "Foo"
let
pkgs = import ../../nix {};
snack = pkgs.snack-lib.snack;
in
snack
{ main = "Foo";
src = ./src;
dependencies = ["conduit"];
}