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

Add support for libraries

This commit is contained in:
Nicolas Mattia 2018-06-15 22:21:23 +02:00
parent 4c6df0a57d
commit c955ae4741
8 changed files with 106 additions and 32 deletions

View File

@ -67,6 +67,11 @@ pushd tests/template-haskell-4
./test
popd
banner "Test library"
pushd tests/library
./test
popd
banner "Test stack-exe formatting"
list=$(shfmt -i 2 -l bin/snack)
if [[ -n "$list" ]]; then

View File

@ -59,6 +59,7 @@ let
moduleDirectories = modDirs;
};
# Turns a module name to a file
moduleToFile = mod:
(lib.strings.replaceChars ["."] ["/"] mod) + ".hs";
@ -76,23 +77,26 @@ let
# Create a module spec by following the dependencies. This assumes that the
# specified module is a "Main" module.
makeModuleSpecRec = base: filesByModuleName: dirsByModuleName:
makeModuleSpecRec = baseByModuleName: filesByModuleName: dirsByModuleName:
lib.fix
(f: isMain: modName:
makeModuleSpec
modName
(map (f false) (listModuleDependencies base modName))
(map (f false)
(listModuleDependencies baseByModuleName modName)
)
isMain
(filesByModuleName modName)
(dirsByModuleName modName)
) true;
buildModule = ghc: ghcOpts: base: mod:
buildModule = ghc: ghcOpts: baseByModuleName: mod:
let
ghcOptsArgs = lib.strings.escapeShellArgs ghcOpts;
objectName = mod.moduleName;
builtDeps = map (buildModule ghc ghcOpts base) mod.moduleDependencies;
builtDeps = map (buildModule ghc ghcOpts baseByModuleName) mod.moduleDependencies;
depsDirs = map (x: x + "/") builtDeps;
base = baseByModuleName mod.moduleName;
makeSymtree =
if lib.lists.length depsDirs >= 1
# TODO: symlink instead of copy
@ -110,8 +114,6 @@ let
(expected == actual) ||
(type == "directory" && (lib.strings.hasPrefix actual expected));
extraFiles = builtins.filterSource
(p: t:
lib.lists.length
@ -161,16 +163,18 @@ let
# Generate a list of haskell module names needed by the haskell file,
# excluding modules that are not present in this project/base
listModuleDependencies = base: modName:
listModuleDependencies = baseByModuleName: modName:
lib.filter
(doesModuleExist base)
(doesModuleExist baseByModuleName)
(builtins.fromJSON
(builtins.readFile (listAllModuleDependenciesJSON base modName))
(builtins.readFile (listAllModuleDependenciesJSON (baseByModuleName modName) modName))
);
doesFileExist = base: filename:
lib.lists.elem filename (listFilesInDir base);
listModulesInDir = dir: map fileToModule (listFilesInDir dir);
listFilesInDir = dir:
let
go = dir: dirName:
@ -188,9 +192,10 @@ let
);
in go dir "";
doesModuleExist = base: modName: doesFileExist base (moduleToFile modName);
doesModuleExist = baseByModuleName: modName:
doesFileExist (baseByModuleName modName) (moduleToFile modName);
# TODO: if module doesn't exist locally, don't include it
# Lists all module dependencies, not limited to modules existing in this
# project
listAllModuleDependenciesJSON = base: modName:
@ -206,7 +211,7 @@ let
# Returns an attribute set where the keys are the module names and the values
# are the '.o's
flattenModuleObjects = ghc: ghcOpts: base: mod':
flattenModuleObjects = ghc: ghcOpts: baseByModuleName: mod':
let
go = mod: attrs0:
let
@ -223,16 +228,16 @@ let
then acc
else acc //
{ "${elem.moduleName}" =
"${buildModule ghc ghcOpts base elem}/${objectName elem}";
"${buildModule ghc ghcOpts baseByModuleName elem}/${objectName elem}";
};
in
lib.lists.foldl f attrs1 mod.moduleDependencies;
in go mod' {};
# TODO: it's sad that we pass ghcWithDeps + dependencies
linkModuleObjects = ghc: ghcOpts: dependencies: base: mod:
linkModuleObjects = ghc: ghcOpts: dependencies: baseByModuleName: mod:
let
objAttrs = flattenModuleObjects ghc ghcOpts base mod;
objAttrs = flattenModuleObjects ghc ghcOpts baseByModuleName mod;
objList = lib.attrsets.mapAttrsToList (x: y: y) objAttrs;
ghcOptsArgs = lib.strings.escapeShellArgs ghcOpts;
packageList = map (p: "-package ${p}") dependencies;
@ -264,15 +269,21 @@ let
# Write a new ghci executable that loads all the modules defined in the
# module spec
ghciExecutable = ghc: ghcOpts: base: modSpec:
ghciExecutable = ghc: ghcOpts: baseByModuleName: modSpec:
let
ghciArgs = lib.strings.escapeShellArgs
(ghcOpts ++ absoluteModuleFiles);
absoluteModuleFiles = map prependBase moduleFiles;
moduleFiles = map moduleToFile modules;
absoluteModuleFiles =
map
(modName:
builtins.toString (baseByModuleName modName) + "/${moduleToFile modName}"
)
modules;
#absoluteModuleFiles = map prependBase moduleFiles;
#moduleFiles = map moduleToFile modules;
modules = allModuleNames modSpec;
dirs = allModuleDirectories modSpec;
prependBase = f: builtins.toString base + "/${f}";
newGhc =
symlinkJoin
{ name = "ghci";
@ -315,11 +326,21 @@ let
, extra-directories ? []
}:
let
deps = dependencies;
deps = lib.filter (x: builtins.typeOf x == "string") dependencies;
snackDeps = lib.filter (x: builtins.typeOf x != "string" && x._type == "snack_lib_def") dependencies;
baseByModuleName = modName:
lib.findFirst
(dir:
lib.lists.elem modName (listModulesInDir dir)
)
base # default to base
((map (snackDep: snackDep.src) snackDeps) ++ [ base ] )
;
ghc = haskellPackages.ghcWithPackages
(ps: map (p: ps.${p}) deps);
ghcOpts = ghc-options;
base = src; # lib.cleanSource src;
base = src;
extraFiles = if builtins.isList extra-files
then (_x: extra-files)
else extra-files;
@ -335,19 +356,27 @@ let
ghc
ghcOpts
deps
base
(makeModuleSpecRec base extraFiles extraDirs mainModName);
baseByModuleName
(makeModuleSpecRec baseByModuleName extraFiles extraDirs mainModName);
ghci =
ghciExecutable
ghc
ghcOpts
base
(makeModuleSpecRec base extraFiles extraDirs mainModName);
baseByModuleName
(makeModuleSpecRec baseByModuleName extraFiles extraDirs mainModName);
};
library =
{ src
, dependencies ? [] # TODO: handle this
}:
{ _type = "snack_lib_def";
inherit src;
# TODO: add build for libraries
};
in
{
inherit
executable
library
;
}

5
tests/library/app/Foo.hs Normal file
View File

@ -0,0 +1,5 @@
import Conduit
import FooBar
main :: IO ()
main = runConduit $ spitOut .| takeC 5 .| digest

5
tests/library/golden Normal file
View File

@ -0,0 +1,5 @@
1
2
3
4
5

13
tests/library/snack.nix Normal file
View File

@ -0,0 +1,13 @@
let
pkgs = import ../../nix {};
snack = pkgs.snack-lib;
my-lib = snack.library
{ src = ./src;
#dependencies = [ "conduit" ];
};
in
snack.executable
{ main = "Foo";
src = ./app;
dependencies = [ my-lib "conduit" ];
}

View File

@ -0,0 +1,9 @@
module FooBar where
import Conduit
spitOut :: ConduitT () Int IO ()
spitOut = yieldMany [ 1 ..]
digest :: ConduitT Int Void IO ()
digest = mapM_C print

14
tests/library/test Executable file
View File

@ -0,0 +1,14 @@
#!/usr/bin/env bash
# vim: ft=sh sw=2 et
set -euo pipefail
snack build
snack run | diff golden -
TMP_FILE=$(mktemp)
capture_io "$TMP_FILE" main | snack ghci
diff golden $TMP_FILE
rm $TMP_FILE

View File

@ -1,6 +0,0 @@
1
2
3
4
5
Leaving GHCi.