diff --git a/bin/snack b/bin/snack index 621437e..38cebad 100755 --- a/bin/snack +++ b/bin/snack @@ -3,8 +3,10 @@ set -euo pipefail ## Defaults +NIXPKGS= NIX_BUILD=nix-build -SNACK_NIX=snack.nix +SNACK_NIX="./snack.nix" +WRAPPER_NIX= COMMAND= ## Functions @@ -21,6 +23,12 @@ Snack is a Haskell build tool Options: -f | --snack-nix : sets the path ot the "snack.nix" file. Default: "./snack.nix" + -w | --wrapper-nix : sets the path ot the nix wrapper file. This file + should take at least one argument, "snackNix", which is the path to the + "snack.nix". + -n | --nixpkgs : use the path to import nixpkgs. The expression should + take no arguments and evaluate to a set containing at least + "snack-lib". -h | --help: Shows this help Commands: @@ -41,6 +49,16 @@ while [[ $# -gt 0 ]]; do shift shift ;; + -w | --wrapper-nix) + WRAPPER_NIX="$2" + shift + shift + ;; + -n | --nixpkgs) + NIXPKGS="$2" + shift + shift + ;; -h | --help) show_usage exit 0 @@ -62,16 +80,31 @@ if [[ -z "$COMMAND" ]]; then exit 1 fi +if [[ -z "$WRAPPER_NIX" ]]; then + log_error "missing \n" + show_usage + exit 1 +fi + +call_snack() { + "$NIX_BUILD" \ + --no-out-link \ + -A $1 \ + "$WRAPPER_NIX" \ + --arg snackNix "$SNACK_NIX" \ + --arg nixpkgs "$NIXPKGS" +} + case $COMMAND in build) - "$NIX_BUILD" --no-out-link --show-trace -A build "$SNACK_NIX" + call_snack build ;; ghci) - res=$("$NIX_BUILD" --no-out-link -A ghci "$SNACK_NIX") + res=$(call_snack ghci) "$res" ;; run) - res=$("$NIX_BUILD" --no-out-link -A build "$SNACK_NIX") + res=$(call_snack build) "$res/out" ;; esac diff --git a/nix/overlay.nix b/nix/overlay.nix index 98c74d9..ac3ebd2 100644 --- a/nix/overlay.nix +++ b/nix/overlay.nix @@ -3,7 +3,7 @@ _: pkgs: { snack-exe = pkgs.writeScriptBin "snack" (builtins.replaceStrings - ["NIX_BUILD=nix-build"] - ["NIX_BUILD=${pkgs.nix}/bin/nix-build"] + ["NIX_BUILD=nix-build" "WRAPPER_NIX="] + ["NIX_BUILD=${pkgs.nix}/bin/nix-build" "WRAPPER_NIX=${../snack-lib/wrapper.nix}"] (builtins.readFile ../bin/snack)); } diff --git a/script/test b/script/test index fe7787f..658d340 100755 --- a/script/test +++ b/script/test @@ -33,6 +33,8 @@ END_HEREDOC export -f capture_io +export SNACK="snack -n $(readlink -f ./nix)" + fail() { echo "ERROR: $*" exit 1 diff --git a/snack-lib/default.nix b/snack-lib/default.nix index c3d1b22..7f450eb 100644 --- a/snack-lib/default.nix +++ b/snack-lib/default.nix @@ -204,58 +204,50 @@ let executable = pkgDescr: let topPkgSpec = mkPackageSpec pkgDescr; - pkgs = flattenPackages topPkgSpec; baseByModuleName = modName: - (pkgSpecByModuleName pkgs topPkgSpec modName).packageBase; + let res = pkgSpecByModuleName topPkgSpec null modName; + in if res == null then null else res.packageBase; depsByModuleName = modName: - (pkgSpecByModuleName pkgs (abort "should not happen") modName).packageDependencies; + (pkgSpecByModuleName + topPkgSpec + (abort "asking dependencies for external module: ${modName}") + modName).packageDependencies; ghcOptsByModuleName = modName: - (pkgSpecByModuleName pkgs (abort "should not happen") modName).packageGhcOpts; + (pkgSpecByModuleName + topPkgSpec + (abort "asking ghc options for external module: ${modName}") + modName).packageGhcOpts; ghcWith = deps: haskellPackages.ghcWithPackages (ps: map (p: ps.${p}) deps); - ghcOpts = topPkgSpec.packageGhcOpts; base = topPkgSpec.packageBase; extraFiles = topPkgSpec.packageExtraFiles; extraDirs = topPkgSpec.packageExtraDirectories; mainModName = topPkgSpec.packageMain; + topModuleSpec = + makeModuleSpecRec + baseByModuleName + extraFiles + extraDirs + depsByModuleName + ghcOptsByModuleName + mainModName; in { build = linkModuleObjects ghcWith - (makeModuleSpecRec - baseByModuleName - extraFiles - extraDirs - depsByModuleName - ghcOptsByModuleName - mainModName); + topModuleSpec; ghci = ghciExecutable ghcWith - ghcOpts - (makeModuleSpecRec - baseByModuleName - extraFiles - extraDirs - depsByModuleName - ghcOptsByModuleName - mainModName); + (allTransitiveGhcOpts topPkgSpec) + topModuleSpec; }; - library = - { src - , dependencies ? [] # TODO: handle this - }: - { - inherit src dependencies; - # TODO: add build for libraries - }; in { inherit executable - library ; } diff --git a/snack-lib/module-spec.nix b/snack-lib/module-spec.nix index 27ff2f1..773eab1 100644 --- a/snack-lib/module-spec.nix +++ b/snack-lib/module-spec.nix @@ -42,7 +42,7 @@ rec { makeModuleSpec modName (map (f false) - (listModuleImports baseByModuleName modName) + (lib.lists.filter (mn: baseByModuleName mn != null) (listModuleImports baseByModuleName modName)) ) isMain (filesByModuleName modName) diff --git a/snack-lib/modules.nix b/snack-lib/modules.nix index 470ae3e..727427b 100644 --- a/snack-lib/modules.nix +++ b/snack-lib/modules.nix @@ -29,22 +29,17 @@ rec { singleOutModulePath = base: mod: "${singleOut base (moduleToFile mod)}/${moduleToFile mod}"; - # Generate a list of haskell module names needed by the haskell file, - # excluding modules that are not present in this project/base + # Generate a list of haskell module names needed by the haskell file listModuleImports = baseByModuleName: modName: - lib.filter - (doesModuleExist baseByModuleName) - (builtins.fromJSON - (builtins.readFile (listAllModuleImportsJSON (baseByModuleName modName) modName)) - ); + builtins.fromJSON + (builtins.readFile (listAllModuleImportsJSON (baseByModuleName modName) modName)) + ; listModulesInDir = dir: map fileToModule (listFilesInDir dir); - doesModuleExist = baseByModuleName: modName: doesFileExist (baseByModuleName modName) (moduleToFile modName); - # Lists all module dependencies, not limited to modules existing in this # project listAllModuleImportsJSON = base: modName: diff --git a/snack-lib/package-spec.nix b/snack-lib/package-spec.nix index e4329eb..c0369f7 100644 --- a/snack-lib/package-spec.nix +++ b/snack-lib/package-spec.nix @@ -43,10 +43,17 @@ rec { (pkgSpec: pkgSpec.packageDependencies) (flattenPackages topPkgSpec); + # TODO: nub + allTransitiveGhcOpts = topPkgSpec: + lib.lists.concatMap + (pkgSpec: pkgSpec.packageGhcOpts) + (flattenPackages topPkgSpec); - # Returns the first package spec that contains a module with given name. If - # none is found, returns the supplied default value. - pkgSpecByModuleName = pkgs: def: modName: + + # Traverses all transitive packages and returns the first package spec that + # contains a module with given name. If none is found, returns the supplied + # default value. + pkgSpecByModuleName = topPkgSpec: def: modName: ( lib.findFirst (pkgSpec: lib.lists.elem @@ -54,6 +61,6 @@ rec { (listModulesInDir pkgSpec.packageBase) ) def - pkgs + (flattenPackages topPkgSpec) ); } diff --git a/snack-lib/wrapper.nix b/snack-lib/wrapper.nix new file mode 100644 index 0000000..3dba784 --- /dev/null +++ b/snack-lib/wrapper.nix @@ -0,0 +1,14 @@ +{ snackNix +, nixpkgs ? null +}: + let + pkgs = + if nixpkgs == null + then import {} + else import nixpkgs {}; + snack = pkgs.snack-lib; + in +{ + build = (snack.executable (import snackNix)).build; + ghci = (snack.executable (import snackNix)).ghci; +} diff --git a/tests/library/snack.nix b/tests/library/snack.nix index 6401b9c..0e458cc 100644 --- a/tests/library/snack.nix +++ b/tests/library/snack.nix @@ -1,13 +1,10 @@ let - pkgs = import ../../nix {}; - snack = pkgs.snack-lib; - my-lib = snack.library + my-lib = { src = ./src; dependencies = [ "conduit" ]; }; in - snack.executable - { main = "Foo"; - src = ./app; - dependencies = [ my-lib "conduit" ]; - } + { main = "Foo"; + src = ./app; + dependencies = [ my-lib "conduit" ]; + } diff --git a/tests/library/test b/tests/library/test index 5dd0de1..71e3896 100755 --- a/tests/library/test +++ b/tests/library/test @@ -3,12 +3,12 @@ set -euo pipefail -snack build -snack run | diff golden - +$SNACK build +$SNACK run | diff golden - TMP_FILE=$(mktemp) -capture_io "$TMP_FILE" main | snack ghci +capture_io "$TMP_FILE" main | $SNACK ghci diff golden $TMP_FILE rm $TMP_FILE diff --git a/tests/packages/snack.nix b/tests/packages/snack.nix index 30a22f9..f74c554 100644 --- a/tests/packages/snack.nix +++ b/tests/packages/snack.nix @@ -1,9 +1,4 @@ -let - pkgs = import ../../nix {}; - snack = pkgs.snack-lib; -in - snack.executable - { main = "Foo"; - src = ./src; - dependencies = ["conduit"]; - } +{ main = "Foo"; + src = ./src; + dependencies = ["conduit"]; +} diff --git a/tests/packages/test b/tests/packages/test index 5dd0de1..71e3896 100755 --- a/tests/packages/test +++ b/tests/packages/test @@ -3,12 +3,12 @@ set -euo pipefail -snack build -snack run | diff golden - +$SNACK build +$SNACK run | diff golden - TMP_FILE=$(mktemp) -capture_io "$TMP_FILE" main | snack ghci +capture_io "$TMP_FILE" main | $SNACK ghci diff golden $TMP_FILE rm $TMP_FILE diff --git a/tests/template-haskell-2/code/snack.nix b/tests/template-haskell-2/code/snack.nix index 508b905..292fe3a 100644 --- a/tests/template-haskell-2/code/snack.nix +++ b/tests/template-haskell-2/code/snack.nix @@ -1,11 +1,6 @@ -let - pkgs = import ../../../nix {}; - snack = pkgs.snack-lib; -in - snack.executable - { main = "Main"; - src = ./.; - dependencies = ["file-embed"]; - extra-directories = - (modName: if modName == "Main" then [ ../. ] else []); - } +{ main = "Main"; + src = ./.; + dependencies = ["file-embed"]; + extra-directories = + (modName: if modName == "Main" then [ ../. ] else []); +} diff --git a/tests/template-haskell-2/test b/tests/template-haskell-2/test index 81788c3..7e39d4e 100755 --- a/tests/template-haskell-2/test +++ b/tests/template-haskell-2/test @@ -3,12 +3,12 @@ set -euo pipefail -snack build -f code/snack.nix -snack run -f code/snack.nix | diff golden - +$SNACK build -f code/snack.nix +$SNACK run -f code/snack.nix | diff golden - TMP_FILE=$(mktemp) -capture_io "$TMP_FILE" main | snack -f code/snack.nix ghci +capture_io "$TMP_FILE" main | $SNACK -f code/snack.nix ghci diff golden $TMP_FILE rm $TMP_FILE diff --git a/tests/template-haskell-3/snack.nix b/tests/template-haskell-3/snack.nix index b98b5bf..a0c45d1 100644 --- a/tests/template-haskell-3/snack.nix +++ b/tests/template-haskell-3/snack.nix @@ -1,11 +1,6 @@ -let - pkgs = import ../../nix {}; - snack = pkgs.snack-lib; -in - snack.executable - { main = "Main"; - src = ./.; - dependencies = ["file-embed"]; - extra-files = - (modName: if modName == "Main" then [ "assets/foo.txt" ] else []); - } +{ main = "Main"; + src = ./.; + dependencies = ["file-embed"]; + extra-files = + (modName: if modName == "Main" then [ "assets/foo.txt" ] else []); +} diff --git a/tests/template-haskell-3/test b/tests/template-haskell-3/test index 5dd0de1..71e3896 100755 --- a/tests/template-haskell-3/test +++ b/tests/template-haskell-3/test @@ -3,12 +3,12 @@ set -euo pipefail -snack build -snack run | diff golden - +$SNACK build +$SNACK run | diff golden - TMP_FILE=$(mktemp) -capture_io "$TMP_FILE" main | snack ghci +capture_io "$TMP_FILE" main | $SNACK ghci diff golden $TMP_FILE rm $TMP_FILE diff --git a/tests/template-haskell-4/snack.nix b/tests/template-haskell-4/snack.nix index 3c5c305..3011fe3 100644 --- a/tests/template-haskell-4/snack.nix +++ b/tests/template-haskell-4/snack.nix @@ -1,11 +1,6 @@ -let - pkgs = import ../../nix {}; - snack = pkgs.snack-lib; -in - snack.executable - { main = "Main"; - src = ./src; - dependencies = ["file-embed"]; - extra-directories = - (modName: if modName == "Main" then [ ./assets ] else []); - } +{ main = "Main"; + src = ./src; + dependencies = ["file-embed"]; + extra-directories = + (modName: if modName == "Main" then [ ./assets ] else []); +} diff --git a/tests/template-haskell-4/test b/tests/template-haskell-4/test index 5dd0de1..71e3896 100755 --- a/tests/template-haskell-4/test +++ b/tests/template-haskell-4/test @@ -3,12 +3,12 @@ set -euo pipefail -snack build -snack run | diff golden - +$SNACK build +$SNACK run | diff golden - TMP_FILE=$(mktemp) -capture_io "$TMP_FILE" main | snack ghci +capture_io "$TMP_FILE" main | $SNACK ghci diff golden $TMP_FILE rm $TMP_FILE diff --git a/tests/template-haskell/snack.nix b/tests/template-haskell/snack.nix index 36418dc..2c9dd3d 100644 --- a/tests/template-haskell/snack.nix +++ b/tests/template-haskell/snack.nix @@ -1,11 +1,6 @@ -let - pkgs = import ../../nix {}; - snack = pkgs.snack-lib; -in - snack.executable - { main = "Main"; - src = ./.; - dependencies = ["file-embed"]; - extra-files = - (modName: if modName == "Main" then [ "foo.txt" ] else []); - } +{ main = "Main"; + src = ./.; + dependencies = ["file-embed"]; + extra-files = + (modName: if modName == "Main" then [ "foo.txt" ] else []); +} diff --git a/tests/template-haskell/test b/tests/template-haskell/test index 5dd0de1..71e3896 100755 --- a/tests/template-haskell/test +++ b/tests/template-haskell/test @@ -3,12 +3,12 @@ set -euo pipefail -snack build -snack run | diff golden - +$SNACK build +$SNACK run | diff golden - TMP_FILE=$(mktemp) -capture_io "$TMP_FILE" main | snack ghci +capture_io "$TMP_FILE" main | $SNACK ghci diff golden $TMP_FILE rm $TMP_FILE