From 9bd50052e8e5b4d77b34ee8114b904611aac5806 Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Sun, 26 Aug 2018 18:25:44 +0300 Subject: [PATCH 01/43] Always build with all available cores --- bin/Snack.hs | 78 ++++++++++++++++++++++------------- tests/any-paths/test | 4 +- tests/extensions/test | 6 +-- tests/hpack/test | 2 +- tests/library-2/test | 4 +- tests/library/test | 6 +-- tests/magichash/test | 6 +-- tests/nested/test | 6 +-- tests/packages/test | 6 +-- tests/readme/test | 6 +-- tests/strips-versions/test | 6 +-- tests/template-haskell-2/test | 6 +-- tests/template-haskell-3/test | 6 +-- tests/template-haskell-4/test | 6 +-- tests/template-haskell/test | 6 +-- tests/trans-imp/test | 4 +- 16 files changed, 90 insertions(+), 68 deletions(-) diff --git a/bin/Snack.hs b/bin/Snack.hs index 05630d9..c49f3d9 100644 --- a/bin/Snack.hs +++ b/bin/Snack.hs @@ -31,13 +31,17 @@ import qualified Shelly as S data Mode = Standalone FilePath -- Reads a snack.nix file - | HPack FilePath + | HPack FilePath -- Reads a package.yaml -- | Like a FilePath, but Nix friendly newtype SnackNix = SnackNix { unSnackNix :: FilePath } newtype PackageYaml = PackageYaml { unPackageYaml :: FilePath } +-- | How to call @nix-build@ +newtype NixConfig = NixConfig + { nixNCores :: Int } + mkSnackNix :: FilePath -> IO SnackNix mkSnackNix = fmap SnackNix . makeAbsolute @@ -52,13 +56,25 @@ data Command main :: IO () main = do opts <- Opts.execParser (Opts.info (options <**> Opts.helper) mempty) - runCommand (mode opts) (command opts) + runCommand (nixConfig opts) (mode opts) (command opts) data Options = Options - { mode :: Mode + { nixConfig :: NixConfig + , mode :: Mode , command :: Command } +parseNixConfig :: Opts.Parser NixConfig +parseNixConfig = + (NixConfig <$> + Opts.option Opts.auto + (Opts.long "cores" + <> Opts.short 'j' + <> Opts.value 1 + <> Opts.metavar "INT" + <> Opts.help "How many cores to use during the build") + ) + parseMode :: Opts.Parser Mode parseMode = (Standalone <$> @@ -78,7 +94,7 @@ parseMode = ) options :: Opts.Parser Options -options = Options <$> parseMode <*> parseCommand +options = Options <$> parseNixConfig <*> parseMode <*> parseCommand newtype ModuleName = ModuleName { unModuleName :: T.Text } deriving newtype (Ord, Eq, Aeson.FromJSONKey) @@ -162,8 +178,8 @@ decodeOrFail bs = case Aeson.decodeStrict' bs of Nothing -> throwIO $ userError $ unlines [ "could not decode " <> show bs ] -nixBuild :: [NixArg] -> NixExpr -> Sh NixPath -nixBuild extraNixArgs nixExpr = +nixBuild :: NixConfig -> [NixArg] -> NixExpr -> Sh NixPath +nixBuild nixCfg extraNixArgs nixExpr = NixPath <$> runStdin1 (T.pack [i| { #{ intercalate "," funArgs } }: @@ -194,6 +210,8 @@ nixBuild extraNixArgs nixExpr = cliArgs = [ "-" -- read expression from stdin , "--no-out-link" -- no need for roots + -- how many cores to use (-j) + , "--cores", T.pack (show (nixNCores nixCfg)) ] <> (concatMap toCliArgs nixArgs) funArgs :: [String] funArgs = toFunArg <$> nixArgs @@ -209,9 +227,9 @@ nixBuild extraNixArgs nixExpr = { Arg -> "--arg"; ArgStr -> "--argstr" } : [ argName narg , argValue narg ] -snackBuild :: SnackNix -> Sh BuildResult -snackBuild snackNix = do - NixPath out <- nixBuild +snackBuild :: NixConfig -> SnackNix -> Sh BuildResult +snackBuild nixCfg snackNix = do + NixPath out <- nixBuild nixCfg [ NixArg { argName = "snackNix" , argValue = T.pack $ unSnackNix snackNix @@ -221,9 +239,9 @@ snackBuild snackNix = do $ NixExpr "snack.inferSnackBuild snackNix" decodeOrFail =<< liftIO (BS.readFile $ T.unpack out) -snackGhci :: SnackNix -> Sh GhciBuild -snackGhci snackNix = do - NixPath out <- nixBuild +snackGhci :: NixConfig -> SnackNix -> Sh GhciBuild +snackGhci nixCfg snackNix = do + NixPath out <- nixBuild nixCfg [ NixArg { argName = "snackNix" , argValue = T.pack $ unSnackNix snackNix @@ -235,9 +253,9 @@ snackGhci snackNix = do BuiltGhci g -> pure g b -> throwIO $ userError $ "Expected GHCi build, got " <> show b -snackBuildHPack :: PackageYaml -> Sh BuildResult -snackBuildHPack packageYaml = do - NixPath out <- nixBuild +snackBuildHPack :: NixConfig -> PackageYaml -> Sh BuildResult +snackBuildHPack nixCfg packageYaml = do + NixPath out <- nixBuild nixCfg [ NixArg { argName = "packageYaml" , argValue = T.pack $ unPackageYaml packageYaml @@ -247,9 +265,9 @@ snackBuildHPack packageYaml = do $ NixExpr "snack.inferHPackBuild packageYaml" decodeOrFail =<< liftIO (BS.readFile (T.unpack out)) -snackGhciHPack :: PackageYaml -> Sh GhciBuild -snackGhciHPack packageYaml = do - NixPath out <- nixBuild +snackGhciHPack :: NixConfig -> PackageYaml -> Sh GhciBuild +snackGhciHPack nixCfg packageYaml = do + NixPath out <- nixBuild nixCfg [ NixArg { argName = "packageYaml" , argValue = T.pack $ unPackageYaml packageYaml @@ -261,19 +279,23 @@ snackGhciHPack packageYaml = do BuiltGhci g -> pure g b -> throwIO $ userError $ "Expected GHCi build, got " <> show b -runCommand :: Mode -> Command -> IO () -runCommand (Standalone snackNix) = \case - Build -> mkSnackNix snackNix >>= S.shelly . void . snackBuild - Run -> mkSnackNix snackNix >>= quiet . snackBuild >>= runBuildResult +runCommand :: NixConfig -> Mode -> Command -> IO () +runCommand nixCfg (Standalone snackNix) = \case + Build -> mkSnackNix snackNix >>= S.shelly . void . snackBuild nixCfg + Run -> mkSnackNix snackNix >>= quiet . snackBuild nixCfg >>= runBuildResult Ghci -> - runExe =<< ghciExePath <$> (mkSnackNix snackNix >>= quiet . snackGhci) -runCommand (HPack packageYaml) = \case - Build -> mkPackageYaml packageYaml >>= S.shelly . void . snackBuildHPack + runExe =<< ghciExePath <$> + (mkSnackNix snackNix >>= quiet . snackGhci nixCfg) +runCommand nixCfg (HPack packageYaml) = \case + Build -> + mkPackageYaml packageYaml >>= S.shelly . void . snackBuildHPack nixCfg Run -> - mkPackageYaml packageYaml >>= quiet . snackBuildHPack >>= runBuildResult + mkPackageYaml packageYaml + >>= quiet . snackBuildHPack nixCfg + >>= runBuildResult Ghci -> - runExe =<< - ghciExePath <$> (mkPackageYaml packageYaml >>= quiet . snackGhciHPack) + runExe =<< ghciExePath <$> + (mkPackageYaml packageYaml >>= quiet . snackGhciHPack nixCfg) runBuildResult :: BuildResult -> IO () runBuildResult = \case diff --git a/tests/any-paths/test b/tests/any-paths/test index 9f3f180..d5c350d 100755 --- a/tests/any-paths/test +++ b/tests/any-paths/test @@ -8,5 +8,5 @@ test() { $SNACK run } -SNACK="snack -s snack.nix" test -SNACK="snack --package-yaml package.yaml" test +SNACK="snack -j4 -s snack.nix" test +SNACK="snack -j4 --package-yaml package.yaml" test diff --git a/tests/extensions/test b/tests/extensions/test index 7ff56f4..35e6d70 100755 --- a/tests/extensions/test +++ b/tests/extensions/test @@ -15,6 +15,6 @@ test() { rm $TMP_FILE } -SNACK="snack" test -SNACK="snack -s ./snack.nix" test -SNACK="snack --package-yaml ./package.yaml" test +SNACK="snack -j4" test +SNACK="snack -j4 -s ./snack.nix" test +SNACK="snack -j4 --package-yaml ./package.yaml" test diff --git a/tests/hpack/test b/tests/hpack/test index 7ad1251..69f47e8 100755 --- a/tests/hpack/test +++ b/tests/hpack/test @@ -8,6 +8,6 @@ TMP_DIR=$(mktemp -d) git clone http://github.com/2mol/pboy.git $TMP_DIR git -C $TMP_DIR reset --hard a2458d6984930a33a3b1972cb6d5c167d2511b06 -snack build --package-yaml $TMP_DIR/package.yaml +snack -j4 build --package-yaml $TMP_DIR/package.yaml rm -rf $TMP_DIR diff --git a/tests/library-2/test b/tests/library-2/test index 05615b7..30e8101 100755 --- a/tests/library-2/test +++ b/tests/library-2/test @@ -15,6 +15,6 @@ test() { rm $TMP_FILE } -SNACK="snack" test -SNACK="snack -s ./snack.nix" test +SNACK="snack -j4" test +SNACK="snack -j4 -s ./snack.nix" test # Note: no HPack test, because HPack doesn't support multi library diff --git a/tests/library/test b/tests/library/test index 7ff56f4..35e6d70 100755 --- a/tests/library/test +++ b/tests/library/test @@ -15,6 +15,6 @@ test() { rm $TMP_FILE } -SNACK="snack" test -SNACK="snack -s ./snack.nix" test -SNACK="snack --package-yaml ./package.yaml" test +SNACK="snack -j4" test +SNACK="snack -j4 -s ./snack.nix" test +SNACK="snack -j4 --package-yaml ./package.yaml" test diff --git a/tests/magichash/test b/tests/magichash/test index 7ff56f4..35e6d70 100755 --- a/tests/magichash/test +++ b/tests/magichash/test @@ -15,6 +15,6 @@ test() { rm $TMP_FILE } -SNACK="snack" test -SNACK="snack -s ./snack.nix" test -SNACK="snack --package-yaml ./package.yaml" test +SNACK="snack -j4" test +SNACK="snack -j4 -s ./snack.nix" test +SNACK="snack -j4 --package-yaml ./package.yaml" test diff --git a/tests/nested/test b/tests/nested/test index e823825..f92c517 100755 --- a/tests/nested/test +++ b/tests/nested/test @@ -16,6 +16,6 @@ test() { rm $TMP_FILE } -SNACK="snack" test -SNACK="snack -s ./snack.nix" test -SNACK="snack --package-yaml ./package.yaml" test +SNACK="snack -j4" test +SNACK="snack -j4 -s ./snack.nix" test +SNACK="snack -j4 --package-yaml ./package.yaml" test diff --git a/tests/packages/test b/tests/packages/test index e823825..f92c517 100755 --- a/tests/packages/test +++ b/tests/packages/test @@ -16,6 +16,6 @@ test() { rm $TMP_FILE } -SNACK="snack" test -SNACK="snack -s ./snack.nix" test -SNACK="snack --package-yaml ./package.yaml" test +SNACK="snack -j4" test +SNACK="snack -j4 -s ./snack.nix" test +SNACK="snack -j4 --package-yaml ./package.yaml" test diff --git a/tests/readme/test b/tests/readme/test index 95a88a5..bd472b2 100755 --- a/tests/readme/test +++ b/tests/readme/test @@ -8,6 +8,6 @@ test() { $SNACK run } -SNACK="snack" test -SNACK="snack -s ./snack.nix" test -SNACK="snack --package-yaml ./package.yaml" test +SNACK="snack -j4" test +SNACK="snack -j4 -s ./snack.nix" test +SNACK="snack -j4 --package-yaml ./package.yaml" test diff --git a/tests/strips-versions/test b/tests/strips-versions/test index 7ff56f4..35e6d70 100755 --- a/tests/strips-versions/test +++ b/tests/strips-versions/test @@ -15,6 +15,6 @@ test() { rm $TMP_FILE } -SNACK="snack" test -SNACK="snack -s ./snack.nix" test -SNACK="snack --package-yaml ./package.yaml" test +SNACK="snack -j4" test +SNACK="snack -j4 -s ./snack.nix" test +SNACK="snack -j4 --package-yaml ./package.yaml" test diff --git a/tests/template-haskell-2/test b/tests/template-haskell-2/test index 233dd86..2a9be58 100755 --- a/tests/template-haskell-2/test +++ b/tests/template-haskell-2/test @@ -3,12 +3,12 @@ set -euo pipefail -snack build -s code/snack.nix -snack run -s code/snack.nix | diff golden - +snack -j4 build -s code/snack.nix +snack -j4 run -s code/snack.nix | diff golden - TMP_FILE=$(mktemp) -capture_io "$TMP_FILE" main | snack -s code/snack.nix ghci +capture_io "$TMP_FILE" main | snack -j4 -s code/snack.nix ghci diff golden $TMP_FILE rm $TMP_FILE diff --git a/tests/template-haskell-3/test b/tests/template-haskell-3/test index 5dd0de1..50fcce4 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 -j4 build +snack -j4 run | diff golden - TMP_FILE=$(mktemp) -capture_io "$TMP_FILE" main | snack ghci +capture_io "$TMP_FILE" main | snack -j4 ghci diff golden $TMP_FILE rm $TMP_FILE diff --git a/tests/template-haskell-4/test b/tests/template-haskell-4/test index 5dd0de1..50fcce4 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 -j4 build +snack -j4 run | diff golden - TMP_FILE=$(mktemp) -capture_io "$TMP_FILE" main | snack ghci +capture_io "$TMP_FILE" main | snack -j4 ghci diff golden $TMP_FILE rm $TMP_FILE diff --git a/tests/template-haskell/test b/tests/template-haskell/test index 5dd0de1..50fcce4 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 -j4 build +snack -j4 run | diff golden - TMP_FILE=$(mktemp) -capture_io "$TMP_FILE" main | snack ghci +capture_io "$TMP_FILE" main | snack -j4 ghci diff golden $TMP_FILE rm $TMP_FILE diff --git a/tests/trans-imp/test b/tests/trans-imp/test index 3df8b70..36dea7d 100755 --- a/tests/trans-imp/test +++ b/tests/trans-imp/test @@ -15,6 +15,6 @@ test() { rm $TMP_FILE } -SNACK="snack" test -SNACK="snack -s ./snack.nix" test +SNACK="snack -j4" test +SNACK="snack -j4 -s ./snack.nix" test # Note: no HPack test, because HPack doesn't support multi library From 6860bac91f1bb09839f20c8651cd1da27fb781d5 Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Sat, 1 Sep 2018 20:39:51 +0300 Subject: [PATCH 02/43] Don't use store paths as attr names --- snack-lib/module-spec.nix | 47 +++++++++++-------------- tests/template-haskell-2/code/snack.nix | 2 +- 2 files changed, 22 insertions(+), 27 deletions(-) diff --git a/snack-lib/module-spec.nix b/snack-lib/module-spec.nix index 275450d..127218e 100644 --- a/snack-lib/module-spec.nix +++ b/snack-lib/module-spec.nix @@ -73,33 +73,28 @@ rec { [ modSpec ] ++ ( lib.lists.concatMap flattenModuleSpec modSpec.moduleImports ); - allTransitiveDeps = allTransitiveLists "moduleDependencies" lib.id; - allTransitiveGhcOpts = allTransitiveLists "moduleGhcOpts" lib.id; - allTransitiveExtensions = allTransitiveLists "moduleExtensions" lib.id; - allTransitiveDirectories = - allTransitiveLists - "moduleDirectories" - builtins.toString; # XXX: is toString correct? - allTransitiveImports = - allTransitiveLists - "moduleImports" - (modSpec: modSpec.moduleName); - - allTransitiveLists = attr: toLabel: modSpecs: - lib.attrsets.attrValues - ( foldDAG - { f = modSpec: - lib.lists.foldl - (x: y: x // { ${toLabel y} = y;}) - {} modSpec.${attr}; - empty = {}; - elemLabel = modSpec: modSpec.moduleName; - reduce = a: b: a // b; - elemChildren = modSpec: modSpec.moduleImports; - } - modSpecs - ); + allTransitiveDeps = allTransitiveLists "moduleDependencies"; + allTransitiveGhcOpts = allTransitiveLists "moduleGhcOpts"; + allTransitiveExtensions = allTransitiveLists "moduleExtensions"; + allTransitiveDirectories = allTransitiveLists "moduleDirectories"; + allTransitiveImports = allTransitiveLists "moduleImports"; + allTransitiveLists = attr: modSpecs: + foldDAG + { f = modSpec: + lib.lists.foldl + # calling "unique" is not super efficient but a set-based + # approach breaks because not all values can be used as attribute + # names (in particular strings referring to a store path) + (x: y: lib.lists.unique (x ++ [y])) + [] modSpec.${attr}; + empty = []; + elemLabel = modSpec: modSpec.moduleName; + reduce = a: b: a ++ b; + elemChildren = modSpec: modSpec.moduleImports; + } + modSpecs + ; # Takes a package spec and returns (modSpecs -> Fold) modSpecFoldFromPackageSpec = pkgSpec: diff --git a/tests/template-haskell-2/code/snack.nix b/tests/template-haskell-2/code/snack.nix index 292fe3a..354b573 100644 --- a/tests/template-haskell-2/code/snack.nix +++ b/tests/template-haskell-2/code/snack.nix @@ -2,5 +2,5 @@ src = ./.; dependencies = ["file-embed"]; extra-directories = - (modName: if modName == "Main" then [ ../. ] else []); + { Main = [../.]; }; } From ee31bb94cbf6e61803370db869903a0efeefc9d9 Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Mon, 10 Sep 2018 21:04:40 +0300 Subject: [PATCH 03/43] Fix nubbing --- snack-lib/module-spec.nix | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/snack-lib/module-spec.nix b/snack-lib/module-spec.nix index 127218e..aaf8074 100644 --- a/snack-lib/module-spec.nix +++ b/snack-lib/module-spec.nix @@ -80,13 +80,12 @@ rec { allTransitiveImports = allTransitiveLists "moduleImports"; allTransitiveLists = attr: modSpecs: + lib.lists.unique + ( foldDAG { f = modSpec: lib.lists.foldl - # calling "unique" is not super efficient but a set-based - # approach breaks because not all values can be used as attribute - # names (in particular strings referring to a store path) - (x: y: lib.lists.unique (x ++ [y])) + (x: y: x ++ [y]) [] modSpec.${attr}; empty = []; elemLabel = modSpec: modSpec.moduleName; @@ -94,6 +93,7 @@ rec { elemChildren = modSpec: modSpec.moduleImports; } modSpecs + ) ; # Takes a package spec and returns (modSpecs -> Fold) From b90d9ee54630ec33893a52a7027eec4670a90791 Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Sat, 22 Sep 2018 13:32:56 +0300 Subject: [PATCH 04/43] Allow alternative library --- README.md | 7 +- bin/Snack.hs | 195 ++++++++++++++++++++++++++++++------------ tests/any-paths/test | 6 +- tests/extensions/test | 1 + tests/library-2/test | 1 + tests/library-3/test | 1 + tests/readme/test | 1 + 7 files changed, 153 insertions(+), 59 deletions(-) diff --git a/README.md b/README.md index 63d5cd7..32a755b 100644 --- a/README.md +++ b/README.md @@ -71,9 +71,14 @@ The _snack_ executable is now in your `PATH`: ``` shell $ snack --help -Usage: snack ([-s|--snack-nix PATH] | [-p|--package-yaml PATH]) COMMAND +Usage: snack [-l|--lib DIR] [-j|--cores INT] ([-s|--snack-nix PATH] | + [-p|--package-yaml PATH]) COMMAND Available options: + -l,--lib DIR Path to the directory to use as the Nix library + instead of the default one bundled with the snack + executable. + -j,--cores INT How many cores to use during the build -h,--help Show this help text Available commands: diff --git a/bin/Snack.hs b/bin/Snack.hs index c49f3d9..9718825 100644 --- a/bin/Snack.hs +++ b/bin/Snack.hs @@ -5,7 +5,10 @@ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE QuasiQuotes #-} +{-# LANGUAGE KindSignatures #-} {-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE TypeFamilies #-} module Main where @@ -29,24 +32,63 @@ import qualified Data.Text as T import qualified Options.Applicative as Opts import qualified Shelly as S -data Mode - = Standalone FilePath -- Reads a snack.nix file - | HPack FilePath -- Reads a package.yaml +--- +--- Some config helpers + +data ConfigStage + = ConfigRaw + | ConfigReady + +type family Config (c :: ConfigStage) ty1 ty2 where + Config 'ConfigRaw ty1 _= ty1 + Config 'ConfigReady _ ty2 = ty2 + +--- +--- Configuration proper + +type Mode = Mode_ 'ConfigReady + +type ModeRaw = Mode_ 'ConfigRaw + +data Mode_ c + = Standalone (Config c FilePath SnackNix) -- Reads a snack.nix file + | HPack (Config c FilePath PackageYaml) -- Reads a package.yaml + +prepareMode :: ModeRaw -> IO Mode +prepareMode = \case + Standalone fp -> Standalone <$> mkSnackNix fp + HPack fp -> HPack <$> mkPackageYaml fp -- | Like a FilePath, but Nix friendly newtype SnackNix = SnackNix { unSnackNix :: FilePath } +mkSnackNix :: FilePath -> IO SnackNix +mkSnackNix = fmap SnackNix . makeAbsolute + +-- | Like a FilePath, but Nix friendly +newtype SnackLib = SnackLib { unSnackLib :: FilePath } + +mkSnackLib :: FilePath -> IO SnackLib +mkSnackLib = fmap SnackLib . makeAbsolute + +-- | Like a FilePath, but Nix friendly newtype PackageYaml = PackageYaml { unPackageYaml :: FilePath } +mkPackageYaml :: FilePath -> IO PackageYaml +mkPackageYaml = fmap PackageYaml . makeAbsolute + -- | How to call @nix-build@ newtype NixConfig = NixConfig { nixNCores :: Int } -mkSnackNix :: FilePath -> IO SnackNix -mkSnackNix = fmap SnackNix . makeAbsolute +type SnackConfig = SnackConfig_ 'ConfigReady +type SnackConfigRaw = SnackConfig_ 'ConfigRaw -mkPackageYaml :: FilePath -> IO PackageYaml -mkPackageYaml = fmap PackageYaml . makeAbsolute +-- | Extra configuration for snack +data SnackConfig_ c = SnackConfig + { snackLib :: Config c (Maybe FilePath) (Maybe SnackLib) + , snackNixCfg :: NixConfig + } data Command = Build @@ -55,15 +97,36 @@ data Command main :: IO () main = do - opts <- Opts.execParser (Opts.info (options <**> Opts.helper) mempty) - runCommand (nixConfig opts) (mode opts) (command opts) + opts <- + prepareOptions =<< + Opts.execParser (Opts.info (parseOptions <**> Opts.helper) mempty) + runCommand (snackConfig opts) (mode opts) (command opts) + +type OptionsRaw = Options_ 'ConfigRaw +type Options = Options_ 'ConfigReady -data Options = Options - { nixConfig :: NixConfig - , mode :: Mode +data Options_ c = Options + { snackConfig :: SnackConfig_ c + , mode :: Mode_ c , command :: Command } +prepareOptions :: OptionsRaw -> IO Options +prepareOptions raw = + Options <$> + prepareSnackConfig (snackConfig raw) <*> + prepareMode (mode raw) <*> + pure (command raw) + +prepareSnackConfig :: SnackConfigRaw -> IO SnackConfig +prepareSnackConfig raw = + SnackConfig <$> + (case snackLib raw of + Nothing -> pure Nothing + Just fp -> Just <$> mkSnackLib fp + ) <*> + pure (snackNixCfg raw) + parseNixConfig :: Opts.Parser NixConfig parseNixConfig = (NixConfig <$> @@ -75,7 +138,23 @@ parseNixConfig = <> Opts.help "How many cores to use during the build") ) -parseMode :: Opts.Parser Mode +parseSnackConfig :: Opts.Parser SnackConfigRaw +parseSnackConfig = SnackConfig <$> Opts.optional + (Opts.strOption + (Opts.long "lib" + <> Opts.short 'l' + <> Opts.metavar "DIR" + <> Opts.help + (unwords + [ "Path to the directory to use as the Nix library" + , "instead of the default one bundled with the snack executable." + ] + ) + ) + ) <*> + parseNixConfig + +parseMode :: Opts.Parser ModeRaw parseMode = (Standalone <$> Opts.strOption @@ -93,8 +172,12 @@ parseMode = <> Opts.metavar "PATH") ) -options :: Opts.Parser Options -options = Options <$> parseNixConfig <*> parseMode <*> parseCommand +parseOptions :: Opts.Parser OptionsRaw +parseOptions = + Options <$> + parseSnackConfig <*> + parseMode <*> + parseCommand newtype ModuleName = ModuleName { unModuleName :: T.Text } deriving newtype (Ord, Eq, Aeson.FromJSONKey) @@ -178,8 +261,8 @@ decodeOrFail bs = case Aeson.decodeStrict' bs of Nothing -> throwIO $ userError $ unlines [ "could not decode " <> show bs ] -nixBuild :: NixConfig -> [NixArg] -> NixExpr -> Sh NixPath -nixBuild nixCfg extraNixArgs nixExpr = +nixBuild :: SnackConfig -> [NixArg] -> NixExpr -> Sh NixPath +nixBuild snackCfg extraNixArgs nixExpr = NixPath <$> runStdin1 (T.pack [i| { #{ intercalate "," funArgs } }: @@ -189,23 +272,29 @@ nixBuild nixCfg extraNixArgs nixExpr = { url = "https://github.com/${spec.owner}/${spec.repo}/archive/${spec.rev}.tar.gz"; sha256 = spec.sha256; }) {} ; - libDir = - let - b64 = pkgs.writeTextFile { name = "lib-b64"; text = lib64; }; - in - pkgs.runCommand "snack-lib" {} - '' - cat ${b64} | base64 --decode > out.tar.gz - mkdir -p $out - tar -C $out -xzf out.tar.gz - chmod +w $out - ''; + libDir = #{ libDir }; snack = pkgs.callPackage libDir {}; in #{ T.unpack $ unNixExpr $ nixExpr } |]) "nix-build" cliArgs where + libDir :: String + libDir = case snackLib snackCfg of + Just (SnackLib fp) -> fp + Nothing -> + [i| + let + b64 = pkgs.writeTextFile { name = "lib-b64"; text = lib64; }; + in + pkgs.runCommand "snack-lib" {} + '' + cat ${b64} | base64 --decode > out.tar.gz + mkdir -p $out + tar -C $out -xzf out.tar.gz + chmod +w $out + '' + |] cliArgs :: [T.Text] cliArgs = [ "-" -- read expression from stdin @@ -226,10 +315,11 @@ nixBuild nixCfg extraNixArgs nixExpr = toCliArgs narg = case argType narg of { Arg -> "--arg"; ArgStr -> "--argstr" } : [ argName narg , argValue narg ] + nixCfg = snackNixCfg snackCfg -snackBuild :: NixConfig -> SnackNix -> Sh BuildResult -snackBuild nixCfg snackNix = do - NixPath out <- nixBuild nixCfg +snackBuild :: SnackConfig -> SnackNix -> Sh BuildResult +snackBuild snackCfg snackNix = do + NixPath out <- nixBuild snackCfg [ NixArg { argName = "snackNix" , argValue = T.pack $ unSnackNix snackNix @@ -239,9 +329,9 @@ snackBuild nixCfg snackNix = do $ NixExpr "snack.inferSnackBuild snackNix" decodeOrFail =<< liftIO (BS.readFile $ T.unpack out) -snackGhci :: NixConfig -> SnackNix -> Sh GhciBuild -snackGhci nixCfg snackNix = do - NixPath out <- nixBuild nixCfg +snackGhci :: SnackConfig -> SnackNix -> Sh GhciBuild +snackGhci snackCfg snackNix = do + NixPath out <- nixBuild snackCfg [ NixArg { argName = "snackNix" , argValue = T.pack $ unSnackNix snackNix @@ -253,9 +343,9 @@ snackGhci nixCfg snackNix = do BuiltGhci g -> pure g b -> throwIO $ userError $ "Expected GHCi build, got " <> show b -snackBuildHPack :: NixConfig -> PackageYaml -> Sh BuildResult -snackBuildHPack nixCfg packageYaml = do - NixPath out <- nixBuild nixCfg +snackBuildHPack :: SnackConfig -> PackageYaml -> Sh BuildResult +snackBuildHPack snackCfg packageYaml = do + NixPath out <- nixBuild snackCfg [ NixArg { argName = "packageYaml" , argValue = T.pack $ unPackageYaml packageYaml @@ -265,9 +355,9 @@ snackBuildHPack nixCfg packageYaml = do $ NixExpr "snack.inferHPackBuild packageYaml" decodeOrFail =<< liftIO (BS.readFile (T.unpack out)) -snackGhciHPack :: NixConfig -> PackageYaml -> Sh GhciBuild -snackGhciHPack nixCfg packageYaml = do - NixPath out <- nixBuild nixCfg +snackGhciHPack :: SnackConfig -> PackageYaml -> Sh GhciBuild +snackGhciHPack snackCfg packageYaml = do + NixPath out <- nixBuild snackCfg [ NixArg { argName = "packageYaml" , argValue = T.pack $ unPackageYaml packageYaml @@ -279,23 +369,16 @@ snackGhciHPack nixCfg packageYaml = do BuiltGhci g -> pure g b -> throwIO $ userError $ "Expected GHCi build, got " <> show b -runCommand :: NixConfig -> Mode -> Command -> IO () -runCommand nixCfg (Standalone snackNix) = \case - Build -> mkSnackNix snackNix >>= S.shelly . void . snackBuild nixCfg - Run -> mkSnackNix snackNix >>= quiet . snackBuild nixCfg >>= runBuildResult - Ghci -> - runExe =<< ghciExePath <$> - (mkSnackNix snackNix >>= quiet . snackGhci nixCfg) -runCommand nixCfg (HPack packageYaml) = \case - Build -> - mkPackageYaml packageYaml >>= S.shelly . void . snackBuildHPack nixCfg - Run -> - mkPackageYaml packageYaml - >>= quiet . snackBuildHPack nixCfg - >>= runBuildResult +runCommand :: SnackConfig -> Mode -> Command -> IO () +runCommand snackCfg (Standalone snackNix) = \case + Build -> S.shelly $ void $ snackBuild snackCfg snackNix + Run -> quiet (snackBuild snackCfg snackNix) >>= runBuildResult + Ghci -> runExe =<< ghciExePath <$> (quiet (snackGhci snackCfg snackNix)) +runCommand snackCfg (HPack packageYaml) = \case + Build -> S.shelly $ void $ snackBuildHPack snackCfg packageYaml + Run -> quiet (snackBuildHPack snackCfg packageYaml) >>= runBuildResult Ghci -> - runExe =<< ghciExePath <$> - (mkPackageYaml packageYaml >>= quiet . snackGhciHPack nixCfg) + runExe =<< ghciExePath <$> (quiet (snackGhciHPack snackCfg packageYaml)) runBuildResult :: BuildResult -> IO () runBuildResult = \case diff --git a/tests/any-paths/test b/tests/any-paths/test index d5c350d..d5146a4 100755 --- a/tests/any-paths/test +++ b/tests/any-paths/test @@ -8,5 +8,7 @@ test() { $SNACK run } -SNACK="snack -j4 -s snack.nix" test -SNACK="snack -j4 --package-yaml package.yaml" test +SNACK="snack -j4" test +SNACK="snack -j4 -s ./snack.nix" test +SNACK="snack -j4 -s ./snack.nix -l ../../snack-lib" test +SNACK="snack -j4 --package-yaml ./package.yaml" test diff --git a/tests/extensions/test b/tests/extensions/test index 35e6d70..0e23cb1 100755 --- a/tests/extensions/test +++ b/tests/extensions/test @@ -17,4 +17,5 @@ test() { SNACK="snack -j4" test SNACK="snack -j4 -s ./snack.nix" test +SNACK="snack -j4 -s ./snack.nix -l ../../snack-lib" test SNACK="snack -j4 --package-yaml ./package.yaml" test diff --git a/tests/library-2/test b/tests/library-2/test index 30e8101..eb87715 100755 --- a/tests/library-2/test +++ b/tests/library-2/test @@ -17,4 +17,5 @@ test() { SNACK="snack -j4" test SNACK="snack -j4 -s ./snack.nix" test +SNACK="snack -j4 -s ./snack.nix -l ../../snack-lib" test # Note: no HPack test, because HPack doesn't support multi library diff --git a/tests/library-3/test b/tests/library-3/test index 05615b7..bd833c2 100755 --- a/tests/library-3/test +++ b/tests/library-3/test @@ -17,4 +17,5 @@ test() { SNACK="snack" test SNACK="snack -s ./snack.nix" test +SNACK="snack -j4 -s ./snack.nix -l ../../snack-lib" test # Note: no HPack test, because HPack doesn't support multi library diff --git a/tests/readme/test b/tests/readme/test index bd472b2..d5146a4 100755 --- a/tests/readme/test +++ b/tests/readme/test @@ -10,4 +10,5 @@ test() { SNACK="snack -j4" test SNACK="snack -j4 -s ./snack.nix" test +SNACK="snack -j4 -s ./snack.nix -l ../../snack-lib" test SNACK="snack -j4 --package-yaml ./package.yaml" test From 162f92987cdca43e91cab063eaa394d7093baa87 Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Sun, 23 Sep 2018 19:31:57 +0300 Subject: [PATCH 05/43] Improve hacking experience --- README.md | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ bin/Snack.hs | 8 ++++---- 2 files changed, 52 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 32a755b..0ebafa6 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,8 @@ _snack_ is a build tool that uses the power of Nix to build Haskell projects. +***Snack requires Nix >= 2.0*** + It will * use your existing [Hpack][hpack] file or a Nix-based config (described @@ -60,6 +62,8 @@ Now that this is out of the way, install _snack_, break it, and help me improve ## Install +_See the [Hacking](#hacking) section if you want to hack on snack_ + Assuming that [Nix][nix] is installed on your machine, clone this repo and run: @@ -228,6 +232,50 @@ _snack_ builds itself, so its [`snack.nix`](./bin/snack.nix) is a good example of an advanced configuration. You can also check out the [test folder](./tests). +## Hacking + +There are two different components you can hack: + +* The snack executable in [`bin/Snack.hs`](./bin/Snack.hs) +* The snack library in [`snack-lib/`](./snack-lib) + +Make sure you have a working version of snack installed, e.g. + +``` shell +$ git co master +$ nix-env -f ./default.nix -iA snack-exe +``` + +If you are hacking on the _snack_ executable, just start _snack_ in a GHCi +session: + +``` shell +$ snack ghci -s ./bin/snack.nix +Temporarily symlinking /nix/store/j1x5vkxjr2ibabddfkdih4sm4kwinfda-spec-json/spec.json to spec.json... +done. +Temporarily symlinking /nix/store/w42y6dzgfmli9r8kmgh8akqk6kyda31x-lib64/lib.tar.gz.b64 to lib.tar.gz.b64... +done. +GHCi, version 8.2.2: http://www.haskell.org/ghc/ :? for help +[1 of 1] Compiling Main ( /home/nicolas/projects/nmattia/snack/bin/Snack.hs, interpreted ) +Ok, one module loaded. +*Main> +``` + +If you are hacking on the library, specify `-l/--lib` when running snack (this +works in GHCi too): + +``` shell +*Main> :main ghci -l ./snack-lib/ -s ./tests/readme/snack.nix +GHCi, version 8.2.2: http://www.haskell.org/ghc/ :? for help +[1 of 2] Compiling Lib ( /home/nicolas/projects/nmattia/snack/tests/readme/src/Lib.hs, interpreted ) +[2 of 2] Compiling Main ( /home/nicolas/projects/nmattia/snack/tests/readme/app/Main.hs, interpreted ) +Ok, two modules loaded. +*Main> :main +"\"Category Theory for Programmers\" has been finished!" +``` + +> Mustn't be afraid to dream a little bigger, darling. + ## Thanks Big thanks to diff --git a/bin/Snack.hs b/bin/Snack.hs index 9718825..922014f 100644 --- a/bin/Snack.hs +++ b/bin/Snack.hs @@ -21,7 +21,7 @@ import Data.List (intercalate) import Data.Semigroup ((<>)) import Data.String.Interpolate import Shelly (Sh) -import System.Directory (makeAbsolute) +import System.Directory (canonicalizePath) import System.Posix.Process (executeFile) import UnliftIO.Exception import qualified Data.Aeson as Aeson @@ -63,19 +63,19 @@ prepareMode = \case newtype SnackNix = SnackNix { unSnackNix :: FilePath } mkSnackNix :: FilePath -> IO SnackNix -mkSnackNix = fmap SnackNix . makeAbsolute +mkSnackNix = fmap SnackNix . canonicalizePath -- | Like a FilePath, but Nix friendly newtype SnackLib = SnackLib { unSnackLib :: FilePath } mkSnackLib :: FilePath -> IO SnackLib -mkSnackLib = fmap SnackLib . makeAbsolute +mkSnackLib = fmap SnackLib . canonicalizePath -- | Like a FilePath, but Nix friendly newtype PackageYaml = PackageYaml { unPackageYaml :: FilePath } mkPackageYaml :: FilePath -> IO PackageYaml -mkPackageYaml = fmap PackageYaml . makeAbsolute +mkPackageYaml = fmap PackageYaml . canonicalizePath -- | How to call @nix-build@ newtype NixConfig = NixConfig From d7e19511444b1c76a15b0bd4aa16d33f8b509a0c Mon Sep 17 00:00:00 2001 From: Rupert Horlick Date: Tue, 25 Sep 2018 16:53:35 -0400 Subject: [PATCH 06/43] Correctly propagate build dependencies --- snack-lib/build.nix | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/snack-lib/build.nix b/snack-lib/build.nix index c054ed5..87d2060 100644 --- a/snack-lib/build.nix +++ b/snack-lib/build.nix @@ -69,7 +69,8 @@ rec { buildModule = ghcWith: modSpec: let - ghc = ghcWith modSpec.moduleDependencies; + ghc = ghcWith deps; + deps = allTransitiveDeps [modSpec]; exts = modSpec.moduleExtensions; ghcOpts = modSpec.moduleGhcOpts ++ (map (x: "-X${x}") exts); ghcOptsArgs = lib.strings.escapeShellArgs ghcOpts; From 10630e0971b6088b18d138907e572b32a9c1a528 Mon Sep 17 00:00:00 2001 From: Rupert Horlick Date: Thu, 20 Sep 2018 18:05:47 -0400 Subject: [PATCH 07/43] Change args to snack-lib and add mkPackage --- snack-lib/default.nix | 47 ++++++++++++++++++++----------------------- 1 file changed, 22 insertions(+), 25 deletions(-) diff --git a/snack-lib/default.nix b/snack-lib/default.nix index 1947389..84f9323 100644 --- a/snack-lib/default.nix +++ b/snack-lib/default.nix @@ -1,18 +1,11 @@ # This is the entry point of the library, and badly needs documentation. # TODO: currently single out derivations prepend the PWD to the path # TODO: make sure that filters for "base" are airtight -{ lib -, haskellPackages -, makeWrapper -, rsync -, stdenv -, symlinkJoin -, writeScriptBin -, writeText -, runCommand -, callPackage +{ pkgs }: +with pkgs; + with (callPackage ./build.nix {}); with (callPackage ./files.nix {}); with (callPackage ./ghci.nix {}); @@ -72,21 +65,7 @@ let exe_path = "${drv.out}/${drv.relExePath}"; }; - inferSnackBuild = snackNix: writeText "snack-build-json" - ( builtins.toJSON ( - let - pkgSpec = mkPackageSpec (import snackNix); - in - if builtins.isNull pkgSpec.packageMain - then - { "build_type" = "library"; - "result" = buildAsLibrary pkgSpec; - } - else - { "build_type" = "executable"; - "result" = buildAsExecutable pkgSpec; - } - )); + inferSnackBuild = snackNix: mkPackage (import snackNix); inferSnackGhci = snackNix: writeText "snack-ghci-json" ( builtins.toJSON ( @@ -151,6 +130,23 @@ let executables = lib.attrsets.mapAttrs (k: v: mkPackageSpec v) descrs.executables; }; + + mkPackage = snackNixExpr: writeText "snack-build-json" + ( builtins.toJSON ( + let + pkgSpec = mkPackageSpec snackNixExpr; + in + if builtins.isNull pkgSpec.packageMain + then + { "build_type" = "library"; + "result" = buildAsLibrary pkgSpec; + } + else + { "build_type" = "executable"; + "result" = buildAsExecutable pkgSpec; + } + )); + in { inherit @@ -163,5 +159,6 @@ in buildAsLibrary snackSpec hpackSpec + mkPackage ; } From 1daa19c7b3067454a1fad23c88fcb70aa92995da Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Wed, 3 Oct 2018 19:38:18 +0300 Subject: [PATCH 08/43] changelog: add --- CHANGELOG.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..e1ca254 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,12 @@ +# Changelog +All notable changes to this project will be documented in this file. + +The format is based on [Keep a +Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to +[Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] +### Added +- This CHANGELOG file to track changes to the command line and library APIs. + +[Unreleased]: https://github.com/nmattia/snack/compare/51987daf76cffc31289e6913174dfb46b93df36b...HEAD From 00efa851b716e5b9e2837bfaaa8aa6f595844ede Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Wed, 3 Oct 2018 20:27:22 +0300 Subject: [PATCH 09/43] run: forward arguments --- bin/Snack.hs | 32 ++++++++++++++++++-------------- tests/run-args/Main.hs | 9 +++++++++ tests/run-args/golden | 4 ++++ tests/run-args/snack.nix | 4 ++++ tests/run-args/test | 11 +++++++++++ 5 files changed, 46 insertions(+), 14 deletions(-) create mode 100644 tests/run-args/Main.hs create mode 100644 tests/run-args/golden create mode 100644 tests/run-args/snack.nix create mode 100755 tests/run-args/test diff --git a/bin/Snack.hs b/bin/Snack.hs index 922014f..8676a3b 100644 --- a/bin/Snack.hs +++ b/bin/Snack.hs @@ -92,7 +92,7 @@ data SnackConfig_ c = SnackConfig data Command = Build - | Run + | Run [String] -- Run with extra args | Ghci main :: IO () @@ -372,31 +372,35 @@ snackGhciHPack snackCfg packageYaml = do runCommand :: SnackConfig -> Mode -> Command -> IO () runCommand snackCfg (Standalone snackNix) = \case Build -> S.shelly $ void $ snackBuild snackCfg snackNix - Run -> quiet (snackBuild snackCfg snackNix) >>= runBuildResult - Ghci -> runExe =<< ghciExePath <$> (quiet (snackGhci snackCfg snackNix)) + Run args -> quiet (snackBuild snackCfg snackNix) >>= runBuildResult args + Ghci -> flip runExe [] =<< + ghciExePath <$> (quiet (snackGhci snackCfg snackNix)) runCommand snackCfg (HPack packageYaml) = \case Build -> S.shelly $ void $ snackBuildHPack snackCfg packageYaml - Run -> quiet (snackBuildHPack snackCfg packageYaml) >>= runBuildResult - Ghci -> - runExe =<< ghciExePath <$> (quiet (snackGhciHPack snackCfg packageYaml)) - -runBuildResult :: BuildResult -> IO () -runBuildResult = \case - BuiltExecutable (ExecutableBuild p) -> runExe p + Run args -> + quiet (snackBuildHPack snackCfg packageYaml) >>= runBuildResult args + Ghci -> flip runExe [] =<< + ghciExePath <$> quiet (snackGhciHPack snackCfg packageYaml) + +runBuildResult :: [String] -> BuildResult -> IO () +runBuildResult args = \case + BuiltExecutable (ExecutableBuild p) -> runExe p args BuiltMulti b - | [ExecutableBuild exe] <- Map.elems (executableBuilds b) -> runExe exe + | [ExecutableBuild exe] <- Map.elems (executableBuilds b) -> + runExe exe args b -> fail $ "Unexpected build type: " <> show b quiet :: Sh a -> IO a quiet = S.shelly . S.print_stdout False -runExe :: NixPath -> IO () -runExe (NixPath fp) = executeFile (T.unpack fp) True [] Nothing + +runExe :: NixPath -> [String] -> IO () +runExe (NixPath fp) args = executeFile (T.unpack fp) True args Nothing parseCommand :: Opts.Parser Command parseCommand = Opts.hsubparser $ ( Opts.command "build" (Opts.info (pure Build) mempty) - <> Opts.command "run" (Opts.info (pure Run) mempty) + <> Opts.command "run" (Opts.info (Run <$> Opts.many (Opts.argument Opts.str mempty)) mempty) <> Opts.command "ghci" (Opts.info (pure Ghci) mempty) ) diff --git a/tests/run-args/Main.hs b/tests/run-args/Main.hs new file mode 100644 index 0000000..7a4336d --- /dev/null +++ b/tests/run-args/Main.hs @@ -0,0 +1,9 @@ + +-- Simple program that turns CLI args into lines + +module Main (main) where + +import System.Environment (getArgs) + +main :: IO () +main = getArgs >>= mapM_ putStrLn diff --git a/tests/run-args/golden b/tests/run-args/golden new file mode 100644 index 0000000..eeba921 --- /dev/null +++ b/tests/run-args/golden @@ -0,0 +1,4 @@ +hello +this is +the answer +bye diff --git a/tests/run-args/snack.nix b/tests/run-args/snack.nix new file mode 100644 index 0000000..c1aac2a --- /dev/null +++ b/tests/run-args/snack.nix @@ -0,0 +1,4 @@ +{ + main = "Main"; + src = ./.; +} diff --git a/tests/run-args/test b/tests/run-args/test new file mode 100755 index 0000000..4f051c9 --- /dev/null +++ b/tests/run-args/test @@ -0,0 +1,11 @@ +#!/usr/bin/env bash +# vim: ft=sh sw=2 et + +set -euo pipefail + +test() { + $SNACK build + $SNACK run -- hello 'this is' 'the answer' bye | diff golden - +} + +SNACK="snack -j4" test From e214fcfa61f332a633cfc68e4e51f7f68aea3d17 Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Wed, 3 Oct 2018 20:51:10 +0300 Subject: [PATCH 10/43] changelog: update --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e1ca254..d57f708 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,12 @@ Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] + ### Added - This CHANGELOG file to track changes to the command line and library APIs. +### Changed +- The `snack run` function to accept arguments that will be passed to the built + executable. + [Unreleased]: https://github.com/nmattia/snack/compare/51987daf76cffc31289e6913174dfb46b93df36b...HEAD From e19b30ab3dd4b87224dde7f6529c1e698dd2e041 Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Wed, 3 Oct 2018 20:51:22 +0300 Subject: [PATCH 11/43] run: display args in --help --- bin/Snack.hs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bin/Snack.hs b/bin/Snack.hs index 8676a3b..b47848b 100644 --- a/bin/Snack.hs +++ b/bin/Snack.hs @@ -400,7 +400,9 @@ parseCommand :: Opts.Parser Command parseCommand = Opts.hsubparser $ ( Opts.command "build" (Opts.info (pure Build) mempty) - <> Opts.command "run" (Opts.info (Run <$> Opts.many (Opts.argument Opts.str mempty)) mempty) + <> Opts.command "run" (Opts.info + ( Run <$> Opts.many (Opts.argument Opts.str (Opts.metavar "ARG")) + ) mempty) <> Opts.command "ghci" (Opts.info (pure Ghci) mempty) ) From e8841fff021613edf16fd6097a6e37e480ae569e Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Wed, 3 Oct 2018 18:48:07 +0300 Subject: [PATCH 12/43] Fix CPP pragma parsing --- snack-lib/Imports.hs | 41 ++++++++++++++++++++++++++++++----------- snack-lib/modules.nix | 2 +- tests/cpp/Main.hs | 6 ++++++ tests/cpp/golden | 1 + tests/cpp/snack.nix | 3 +++ tests/cpp/test | 18 ++++++++++++++++++ 6 files changed, 59 insertions(+), 12 deletions(-) create mode 100644 tests/cpp/Main.hs create mode 100644 tests/cpp/golden create mode 100644 tests/cpp/snack.nix create mode 100755 tests/cpp/test diff --git a/snack-lib/Imports.hs b/snack-lib/Imports.hs index 82f4e4e..96ca8fd 100644 --- a/snack-lib/Imports.hs +++ b/snack-lib/Imports.hs @@ -1,13 +1,18 @@ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE MagicHash #-} +module Main (main) where + import Control.Monad.IO.Class import Data.Semigroup import System.Environment +import Control.Exception import qualified DriverPipeline import qualified DynFlags import qualified FastString import qualified GHC +import qualified ErrUtils +import qualified Bag import qualified GHC.IO.Handle.Text as Handle import qualified HsImpExp import qualified HsSyn @@ -19,6 +24,7 @@ import qualified Parser import qualified SrcLoc import qualified StringBuffer import qualified System.Process as Process +import System.IO (stderr) main :: IO () main = do @@ -33,26 +39,39 @@ main = do libdir <- filter (/= '\n') <$> Handle.hGetContents ho1 _ <- Process.waitForProcess hdl - -- Read the file that we want to parse - str <- readFile fp - -- Some gymnastics to make the parser happy res <- GHC.runGhc (Just libdir) $ do + -- Without this line GHC parsing fails with the following error + -- message: + -- : unknown package: rts + _ <- GHC.setSessionDynFlags =<< GHC.getSessionDynFlags + hsc_env <- GHC.getSession + -- XXX: We need to preprocess the file so that all extensions are -- loaded - (dflags, _) <- liftIO $ DriverPipeline.preprocess hsc_env (fp, Nothing) - hsc_env <- GHC.setSession hsc_env { HscTypes.hsc_dflags = dflags } + (dflags, fp2) <- liftIO $ + DriverPipeline.preprocess hsc_env (fp, Nothing) + _ <- GHC.setSessionDynFlags dflags + + -- Read the file that we want to parse + str <- liftIO $ readFile fp2 - runParser fp str (Parser.parseModule) >>= \case + runParser fp2 str Parser.parseModule >>= \case Lexer.POk _ (SrcLoc.L _ res) -> pure res - Lexer.PFailed _ e -> fail $ unlines - [ "Could not parse module: " - , fp - , " because " <> Outputable.showSDocUnsafe e - ] + Lexer.PFailed spn e -> liftIO $ do + Handle.hPutStrLn stderr $ unlines + [ "Could not parse module: " + , fp2 + , " (originally " <> fp <> ")" + , " because " <> Outputable.showSDocUnsafe e + , " src span " + , show spn + ] + throwIO $ HscTypes.mkSrcErr $ + Bag.unitBag $ ErrUtils.mkPlainErrMsg dflags spn e -- Extract the imports from the parsed module let imports' = diff --git a/snack-lib/modules.nix b/snack-lib/modules.nix index 1e3b8ad..024dd86 100644 --- a/snack-lib/modules.nix +++ b/snack-lib/modules.nix @@ -56,7 +56,7 @@ rec { ghc = haskellPackages.ghcWithPackages (ps: [ ps.ghc ]); importParser = runCommand "import-parser" { buildInputs = [ ghc ]; - } "ghc -package ghc ${./Imports.hs} -o $out" ; + } "ghc -Wall -Werror -package ghc ${./Imports.hs} -o $out" ; # XXX: this command needs ghc in the environment so that it can call "ghc # --print-libdir"... in runCommand "dependencies-json" diff --git a/tests/cpp/Main.hs b/tests/cpp/Main.hs new file mode 100644 index 0000000..6e6cc5e --- /dev/null +++ b/tests/cpp/Main.hs @@ -0,0 +1,6 @@ +{-# LANGUAGE CPP #-} + +module Main where + +main :: IO () +main = putStrLn "hello" diff --git a/tests/cpp/golden b/tests/cpp/golden new file mode 100644 index 0000000..ce01362 --- /dev/null +++ b/tests/cpp/golden @@ -0,0 +1 @@ +hello diff --git a/tests/cpp/snack.nix b/tests/cpp/snack.nix new file mode 100644 index 0000000..3b4022a --- /dev/null +++ b/tests/cpp/snack.nix @@ -0,0 +1,3 @@ +{ main = "Main"; + src = ./.; +} diff --git a/tests/cpp/test b/tests/cpp/test new file mode 100755 index 0000000..f958d6b --- /dev/null +++ b/tests/cpp/test @@ -0,0 +1,18 @@ +#!/usr/bin/env bash +# vim: ft=sh sw=2 et + +set -euo pipefail + +test() { + $SNACK build + $SNACK run | diff golden - + + TMP_FILE=$(mktemp) + + capture_io "$TMP_FILE" main | $SNACK ghci + + diff golden $TMP_FILE + rm $TMP_FILE +} + +SNACK="snack -j4" test From 95985a6e6ada3aa6e76632c4d14a12689a59614a Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Wed, 3 Oct 2018 21:11:29 +0300 Subject: [PATCH 13/43] changelog: update --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d57f708..96350ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,4 +14,7 @@ Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to - The `snack run` function to accept arguments that will be passed to the built executable. +### Fixed +- The module import parsing when the CPP extension is enabled. + [Unreleased]: https://github.com/nmattia/snack/compare/51987daf76cffc31289e6913174dfb46b93df36b...HEAD From d733661f3106212219b9b53627108981b1a90fd4 Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Tue, 11 Sep 2018 08:17:57 -0400 Subject: [PATCH 14/43] Add utf-8-BOM test, #50. --- tests/utf-8-BOM/Main.hs | 12 ++++++++++++ tests/utf-8-BOM/golden | 1 + tests/utf-8-BOM/package.yaml | 4 ++++ tests/utf-8-BOM/snack.nix | 3 +++ tests/utf-8-BOM/test | 20 ++++++++++++++++++++ 5 files changed, 40 insertions(+) create mode 100644 tests/utf-8-BOM/Main.hs create mode 100644 tests/utf-8-BOM/golden create mode 100644 tests/utf-8-BOM/package.yaml create mode 100644 tests/utf-8-BOM/snack.nix create mode 100755 tests/utf-8-BOM/test diff --git a/tests/utf-8-BOM/Main.hs b/tests/utf-8-BOM/Main.hs new file mode 100644 index 0000000..921dffa --- /dev/null +++ b/tests/utf-8-BOM/Main.hs @@ -0,0 +1,12 @@ +{-| +Copyright: + © 2018 Nicolas Mattia +-} +module Main + ( main + ) where + +import Numeric.Natural (Natural) + +main :: IO () +main = putStrLn "hello" diff --git a/tests/utf-8-BOM/golden b/tests/utf-8-BOM/golden new file mode 100644 index 0000000..ce01362 --- /dev/null +++ b/tests/utf-8-BOM/golden @@ -0,0 +1 @@ +hello diff --git a/tests/utf-8-BOM/package.yaml b/tests/utf-8-BOM/package.yaml new file mode 100644 index 0000000..90066c0 --- /dev/null +++ b/tests/utf-8-BOM/package.yaml @@ -0,0 +1,4 @@ +name: snack-strips-versions +executable: + main: Main.hs + source-dirs: . diff --git a/tests/utf-8-BOM/snack.nix b/tests/utf-8-BOM/snack.nix new file mode 100644 index 0000000..3b4022a --- /dev/null +++ b/tests/utf-8-BOM/snack.nix @@ -0,0 +1,3 @@ +{ main = "Main"; + src = ./.; +} diff --git a/tests/utf-8-BOM/test b/tests/utf-8-BOM/test new file mode 100755 index 0000000..7ff56f4 --- /dev/null +++ b/tests/utf-8-BOM/test @@ -0,0 +1,20 @@ +#!/usr/bin/env bash +# vim: ft=sh sw=2 et + +set -euo pipefail + +test() { + $SNACK build + $SNACK run | diff golden - + + TMP_FILE=$(mktemp) + + capture_io "$TMP_FILE" main | $SNACK ghci + + diff golden $TMP_FILE + rm $TMP_FILE +} + +SNACK="snack" test +SNACK="snack -s ./snack.nix" test +SNACK="snack --package-yaml ./package.yaml" test From 1ee068411687d6acba58a6744414d60b5aec8d57 Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Wed, 3 Oct 2018 21:34:59 +0300 Subject: [PATCH 15/43] imports: filter out BOM --- CHANGELOG.md | 1 + snack-lib/Imports.hs | 12 +++++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 96350ec..2219ed0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,5 +16,6 @@ Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to ### Fixed - The module import parsing when the CPP extension is enabled. +- The module import parsing when a BOM is present [Unreleased]: https://github.com/nmattia/snack/compare/51987daf76cffc31289e6913174dfb46b93df36b...HEAD diff --git a/snack-lib/Imports.hs b/snack-lib/Imports.hs index 96ca8fd..54633bd 100644 --- a/snack-lib/Imports.hs +++ b/snack-lib/Imports.hs @@ -4,6 +4,7 @@ module Main (main) where import Control.Monad.IO.Class +import Data.List (stripPrefix) import Data.Semigroup import System.Environment import Control.Exception @@ -57,7 +58,7 @@ main = do _ <- GHC.setSessionDynFlags dflags -- Read the file that we want to parse - str <- liftIO $ readFile fp2 + str <- liftIO $ filterBOM <$> readFile fp2 runParser fp2 str Parser.parseModule >>= \case Lexer.POk _ (SrcLoc.L _ res) -> pure res @@ -82,6 +83,15 @@ main = do -- here we pretend that @show :: [String] -> String@ outputs JSON print imports' +-- | Filter out the Byte Order Mark to avoid the following error: +-- lexical error at character '\65279' +filterBOM :: String -> String +filterBOM = \case + [] -> [] + str@(x:xs) -> case stripPrefix "\65279" str of + Just str' -> filterBOM str' + Nothing -> x : filterBOM xs + runParser :: FilePath -> String -> Lexer.P a -> GHC.Ghc (Lexer.ParseResult a) runParser filename str parser = do dynFlags <- DynFlags.getDynFlags From 6904d6b4f5798e142102a1b9f2236da2db56086b Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Thu, 27 Sep 2018 19:56:16 +0300 Subject: [PATCH 16/43] Rename snack.nix -> package.nix --- README.md | 10 ++--- bin/Snack.hs | 42 +++++++++---------- bin/{snack.nix => package.nix} | 0 nix/overlay.nix | 2 +- snack-lib/default.nix | 10 ++--- snack-lib/wrapper.nix | 4 +- tests/any-paths/{snack.nix => package.nix} | 0 tests/any-paths/test | 4 +- tests/cpp/{snack.nix => package.nix} | 0 tests/extensions/{snack.nix => package.nix} | 0 tests/extensions/test | 4 +- tests/hpack/test | 2 +- tests/library-2/{snack.nix => package.nix} | 0 tests/library-2/test | 4 +- tests/library-3/{snack.nix => package.nix} | 0 tests/library-3/test | 4 +- tests/library/{snack.nix => package.nix} | 0 tests/library/test | 2 +- tests/magichash/{snack.nix => package.nix} | 0 tests/magichash/test | 2 +- tests/nested/{snack.nix => package.nix} | 0 tests/nested/test | 2 +- tests/packages/{snack.nix => package.nix} | 0 tests/packages/test | 2 +- tests/readme/{snack.nix => package.nix} | 0 tests/readme/test | 4 +- tests/run-args/{snack.nix => package.nix} | 0 .../{snack.nix => package.nix} | 0 tests/strips-versions/test | 2 +- .../code/{snack.nix => package.nix} | 0 tests/template-haskell-2/test | 6 +-- .../{snack.nix => package.nix} | 0 .../{snack.nix => package.nix} | 0 .../{snack.nix => package.nix} | 0 tests/trans-imp/{snack.nix => package.nix} | 0 tests/trans-imp/test | 2 +- tests/utf-8-BOM/{snack.nix => package.nix} | 0 tests/utf-8-BOM/test | 2 +- tests/utf-8/{snack.nix => package.nix} | 0 tests/utf-8/test | 2 +- 40 files changed, 56 insertions(+), 56 deletions(-) rename bin/{snack.nix => package.nix} (100%) rename tests/any-paths/{snack.nix => package.nix} (100%) rename tests/cpp/{snack.nix => package.nix} (100%) rename tests/extensions/{snack.nix => package.nix} (100%) rename tests/library-2/{snack.nix => package.nix} (100%) rename tests/library-3/{snack.nix => package.nix} (100%) rename tests/library/{snack.nix => package.nix} (100%) rename tests/magichash/{snack.nix => package.nix} (100%) rename tests/nested/{snack.nix => package.nix} (100%) rename tests/packages/{snack.nix => package.nix} (100%) rename tests/readme/{snack.nix => package.nix} (100%) rename tests/run-args/{snack.nix => package.nix} (100%) rename tests/strips-versions/{snack.nix => package.nix} (100%) rename tests/template-haskell-2/code/{snack.nix => package.nix} (100%) rename tests/template-haskell-3/{snack.nix => package.nix} (100%) rename tests/template-haskell-4/{snack.nix => package.nix} (100%) rename tests/template-haskell/{snack.nix => package.nix} (100%) rename tests/trans-imp/{snack.nix => package.nix} (100%) rename tests/utf-8-BOM/{snack.nix => package.nix} (100%) rename tests/utf-8/{snack.nix => package.nix} (100%) diff --git a/README.md b/README.md index 0ebafa6..b77cef9 100644 --- a/README.md +++ b/README.md @@ -75,7 +75,7 @@ The _snack_ executable is now in your `PATH`: ``` shell $ snack --help -Usage: snack [-l|--lib DIR] [-j|--cores INT] ([-s|--snack-nix PATH] | +Usage: snack [-l|--lib DIR] [-j|--cores INT] ([-s|--package-nix PATH] | [-p|--package-yaml PATH]) COMMAND Available options: @@ -215,7 +215,7 @@ in Building and running the project is as simple as ``` shell -$ snack run # looks for a file called snack.nix by default +$ snack run # looks for a file called package.nix by default ``` Alternatively, use `$ snack build` or `$ snack ghci` if you only want to build, @@ -228,7 +228,7 @@ You may want custom builds that involve things such as [archiving and base64 encoding entire directories](https://github.com/nmattia/snack/blob/c8e9e2d5ddaba2e0aa3e6c68a26bdc1063d387f3/bin/snack.nix#L10). -_snack_ builds itself, so its [`snack.nix`](./bin/snack.nix) is a good example +_snack_ builds itself, so its [`package.nix`](./bin/package.nix) is a good example of an advanced configuration. You can also check out the [test folder](./tests). @@ -250,7 +250,7 @@ If you are hacking on the _snack_ executable, just start _snack_ in a GHCi session: ``` shell -$ snack ghci -s ./bin/snack.nix +$ snack ghci -s ./bin/package.nix Temporarily symlinking /nix/store/j1x5vkxjr2ibabddfkdih4sm4kwinfda-spec-json/spec.json to spec.json... done. Temporarily symlinking /nix/store/w42y6dzgfmli9r8kmgh8akqk6kyda31x-lib64/lib.tar.gz.b64 to lib.tar.gz.b64... @@ -265,7 +265,7 @@ If you are hacking on the library, specify `-l/--lib` when running snack (this works in GHCi too): ``` shell -*Main> :main ghci -l ./snack-lib/ -s ./tests/readme/snack.nix +*Main> :main ghci -l ./snack-lib/ -s ./tests/readme/package.nix GHCi, version 8.2.2: http://www.haskell.org/ghc/ :? for help [1 of 2] Compiling Lib ( /home/nicolas/projects/nmattia/snack/tests/readme/src/Lib.hs, interpreted ) [2 of 2] Compiling Main ( /home/nicolas/projects/nmattia/snack/tests/readme/app/Main.hs, interpreted ) diff --git a/bin/Snack.hs b/bin/Snack.hs index b47848b..ff1410e 100644 --- a/bin/Snack.hs +++ b/bin/Snack.hs @@ -51,19 +51,19 @@ type Mode = Mode_ 'ConfigReady type ModeRaw = Mode_ 'ConfigRaw data Mode_ c - = Standalone (Config c FilePath SnackNix) -- Reads a snack.nix file + = Standalone (Config c FilePath PackageNix) -- Reads a package.nix file | HPack (Config c FilePath PackageYaml) -- Reads a package.yaml prepareMode :: ModeRaw -> IO Mode prepareMode = \case - Standalone fp -> Standalone <$> mkSnackNix fp + Standalone fp -> Standalone <$> mkPackageNix fp HPack fp -> HPack <$> mkPackageYaml fp -- | Like a FilePath, but Nix friendly -newtype SnackNix = SnackNix { unSnackNix :: FilePath } +newtype PackageNix = PackageNix { unPackageNix :: FilePath } -mkSnackNix :: FilePath -> IO SnackNix -mkSnackNix = fmap SnackNix . canonicalizePath +mkPackageNix :: FilePath -> IO PackageNix +mkPackageNix = fmap PackageNix . canonicalizePath -- | Like a FilePath, but Nix friendly newtype SnackLib = SnackLib { unSnackLib :: FilePath } @@ -158,9 +158,9 @@ parseMode :: Opts.Parser ModeRaw parseMode = (Standalone <$> Opts.strOption - (Opts.long "snack-nix" + (Opts.long "package-nix" <> Opts.short 's' - <> Opts.value "./snack.nix" + <> Opts.value "./package.nix" <> Opts.metavar "PATH") ) <|> @@ -317,28 +317,28 @@ nixBuild snackCfg extraNixArgs nixExpr = : [ argName narg , argValue narg ] nixCfg = snackNixCfg snackCfg -snackBuild :: SnackConfig -> SnackNix -> Sh BuildResult -snackBuild snackCfg snackNix = do +snackBuild :: SnackConfig -> PackageNix -> Sh BuildResult +snackBuild snackCfg packageNix = do NixPath out <- nixBuild snackCfg [ NixArg - { argName = "snackNix" - , argValue = T.pack $ unSnackNix snackNix + { argName = "packageNix" + , argValue = T.pack $ unPackageNix packageNix , argType = Arg } ] - $ NixExpr "snack.inferSnackBuild snackNix" + $ NixExpr "snack.inferSnackBuild packageNix" decodeOrFail =<< liftIO (BS.readFile $ T.unpack out) -snackGhci :: SnackConfig -> SnackNix -> Sh GhciBuild -snackGhci snackCfg snackNix = do +snackGhci :: SnackConfig -> PackageNix -> Sh GhciBuild +snackGhci snackCfg packageNix = do NixPath out <- nixBuild snackCfg [ NixArg - { argName = "snackNix" - , argValue = T.pack $ unSnackNix snackNix + { argName = "packageNix" + , argValue = T.pack $ unPackageNix packageNix , argType = Arg } ] - $ NixExpr "snack.inferSnackGhci snackNix" + $ NixExpr "snack.inferSnackGhci packageNix" liftIO (BS.readFile (T.unpack out)) >>= decodeOrFail >>= \case BuiltGhci g -> pure g b -> throwIO $ userError $ "Expected GHCi build, got " <> show b @@ -370,11 +370,11 @@ snackGhciHPack snackCfg packageYaml = do b -> throwIO $ userError $ "Expected GHCi build, got " <> show b runCommand :: SnackConfig -> Mode -> Command -> IO () -runCommand snackCfg (Standalone snackNix) = \case - Build -> S.shelly $ void $ snackBuild snackCfg snackNix - Run args -> quiet (snackBuild snackCfg snackNix) >>= runBuildResult args +runCommand snackCfg (Standalone packageNix) = \case + Build -> S.shelly $ void $ snackBuild snackCfg packageNix + Run args -> quiet (snackBuild snackCfg packageNix) >>= runBuildResult args Ghci -> flip runExe [] =<< - ghciExePath <$> (quiet (snackGhci snackCfg snackNix)) + ghciExePath <$> (quiet (snackGhci snackCfg packageNix)) runCommand snackCfg (HPack packageYaml) = \case Build -> S.shelly $ void $ snackBuildHPack snackCfg packageYaml Run args -> diff --git a/bin/snack.nix b/bin/package.nix similarity index 100% rename from bin/snack.nix rename to bin/package.nix diff --git a/nix/overlay.nix b/nix/overlay.nix index 20b3edb..89210ea 100644 --- a/nix/overlay.nix +++ b/nix/overlay.nix @@ -1,5 +1,5 @@ _: pkgs: rec { snack-lib = pkgs.callPackage ../snack-lib/default.nix { }; snack-exe = - (snack-lib.buildAsExecutable (snack-lib.snackSpec ../bin/snack.nix)).out; + (snack-lib.buildAsExecutable (snack-lib.snackSpec ../bin/package.nix)).out; } diff --git a/snack-lib/default.nix b/snack-lib/default.nix index 84f9323..f355331 100644 --- a/snack-lib/default.nix +++ b/snack-lib/default.nix @@ -65,12 +65,12 @@ let exe_path = "${drv.out}/${drv.relExePath}"; }; - inferSnackBuild = snackNix: mkPackage (import snackNix); + inferSnackBuild = packageNix: mkPackage (import packageNix); - inferSnackGhci = snackNix: writeText "snack-ghci-json" + inferSnackGhci = packageNix: writeText "snack-ghci-json" ( builtins.toJSON ( let - pkgSpec = mkPackageSpec (import snackNix); + pkgSpec = mkPackageSpec (import packageNix); drv = if builtins.isNull pkgSpec.packageMain then ghciWithModules ghcWith (libraryModSpecs pkgSpec) @@ -102,7 +102,7 @@ let ( builtins.toJSON ( let pkgSpecs = hpackSpecs packageYaml; - pkgSpec = mkPackageSpec (import snackNix); + pkgSpec = mkPackageSpec (import packageNix); drv = let exeSpecs = builtins.attrValues pkgSpecs.executables; in @@ -120,7 +120,7 @@ let } )); - snackSpec = snackNix: mkPackageSpec (import snackNix); + snackSpec = packageNix: mkPackageSpec (import packageNix); hpackSpecs = packageYaml: let descrs = pkgDescrsFromHPack packageYaml; diff --git a/snack-lib/wrapper.nix b/snack-lib/wrapper.nix index 4bcd1f1..24dc350 100644 --- a/snack-lib/wrapper.nix +++ b/snack-lib/wrapper.nix @@ -1,4 +1,4 @@ -{ snackNix +{ packageNix , nixpkgs ? null }: let @@ -7,7 +7,7 @@ then import {} else import nixpkgs {}; snack = pkgs.snack-lib; - snackDef = import snackNix; + snackDef = import packageNix; in { build = (snack.executable snackDef).build; diff --git a/tests/any-paths/snack.nix b/tests/any-paths/package.nix similarity index 100% rename from tests/any-paths/snack.nix rename to tests/any-paths/package.nix diff --git a/tests/any-paths/test b/tests/any-paths/test index d5146a4..a2e5fcd 100755 --- a/tests/any-paths/test +++ b/tests/any-paths/test @@ -9,6 +9,6 @@ test() { } SNACK="snack -j4" test -SNACK="snack -j4 -s ./snack.nix" test -SNACK="snack -j4 -s ./snack.nix -l ../../snack-lib" test +SNACK="snack -j4 -s ./package.nix" test +SNACK="snack -j4 -s ./package.nix -l ../../snack-lib" test SNACK="snack -j4 --package-yaml ./package.yaml" test diff --git a/tests/cpp/snack.nix b/tests/cpp/package.nix similarity index 100% rename from tests/cpp/snack.nix rename to tests/cpp/package.nix diff --git a/tests/extensions/snack.nix b/tests/extensions/package.nix similarity index 100% rename from tests/extensions/snack.nix rename to tests/extensions/package.nix diff --git a/tests/extensions/test b/tests/extensions/test index 0e23cb1..62b4e14 100755 --- a/tests/extensions/test +++ b/tests/extensions/test @@ -16,6 +16,6 @@ test() { } SNACK="snack -j4" test -SNACK="snack -j4 -s ./snack.nix" test -SNACK="snack -j4 -s ./snack.nix -l ../../snack-lib" test +SNACK="snack -j4 -s ./package.nix" test +SNACK="snack -j4 -s ./package.nix -l ../../snack-lib" test SNACK="snack -j4 --package-yaml ./package.yaml" test diff --git a/tests/hpack/test b/tests/hpack/test index 69f47e8..fbb5104 100755 --- a/tests/hpack/test +++ b/tests/hpack/test @@ -5,7 +5,7 @@ set -euo pipefail TMP_DIR=$(mktemp -d) -git clone http://github.com/2mol/pboy.git $TMP_DIR +git clone http://github.com/nmattia/pboy.git $TMP_DIR git -C $TMP_DIR reset --hard a2458d6984930a33a3b1972cb6d5c167d2511b06 snack -j4 build --package-yaml $TMP_DIR/package.yaml diff --git a/tests/library-2/snack.nix b/tests/library-2/package.nix similarity index 100% rename from tests/library-2/snack.nix rename to tests/library-2/package.nix diff --git a/tests/library-2/test b/tests/library-2/test index eb87715..66914ad 100755 --- a/tests/library-2/test +++ b/tests/library-2/test @@ -16,6 +16,6 @@ test() { } SNACK="snack -j4" test -SNACK="snack -j4 -s ./snack.nix" test -SNACK="snack -j4 -s ./snack.nix -l ../../snack-lib" test +SNACK="snack -j4 -s ./package.nix" test +SNACK="snack -j4 -s ./package.nix -l ../../snack-lib" test # Note: no HPack test, because HPack doesn't support multi library diff --git a/tests/library-3/snack.nix b/tests/library-3/package.nix similarity index 100% rename from tests/library-3/snack.nix rename to tests/library-3/package.nix diff --git a/tests/library-3/test b/tests/library-3/test index bd833c2..2d4bfe9 100755 --- a/tests/library-3/test +++ b/tests/library-3/test @@ -16,6 +16,6 @@ test() { } SNACK="snack" test -SNACK="snack -s ./snack.nix" test -SNACK="snack -j4 -s ./snack.nix -l ../../snack-lib" test +SNACK="snack -s ./package.nix" test +SNACK="snack -j4 -s ./package.nix -l ../../snack-lib" test # Note: no HPack test, because HPack doesn't support multi library diff --git a/tests/library/snack.nix b/tests/library/package.nix similarity index 100% rename from tests/library/snack.nix rename to tests/library/package.nix diff --git a/tests/library/test b/tests/library/test index 35e6d70..ebfd06d 100755 --- a/tests/library/test +++ b/tests/library/test @@ -16,5 +16,5 @@ test() { } SNACK="snack -j4" test -SNACK="snack -j4 -s ./snack.nix" test +SNACK="snack -j4 -s ./package.nix" test SNACK="snack -j4 --package-yaml ./package.yaml" test diff --git a/tests/magichash/snack.nix b/tests/magichash/package.nix similarity index 100% rename from tests/magichash/snack.nix rename to tests/magichash/package.nix diff --git a/tests/magichash/test b/tests/magichash/test index 35e6d70..ebfd06d 100755 --- a/tests/magichash/test +++ b/tests/magichash/test @@ -16,5 +16,5 @@ test() { } SNACK="snack -j4" test -SNACK="snack -j4 -s ./snack.nix" test +SNACK="snack -j4 -s ./package.nix" test SNACK="snack -j4 --package-yaml ./package.yaml" test diff --git a/tests/nested/snack.nix b/tests/nested/package.nix similarity index 100% rename from tests/nested/snack.nix rename to tests/nested/package.nix diff --git a/tests/nested/test b/tests/nested/test index f92c517..0f7c742 100755 --- a/tests/nested/test +++ b/tests/nested/test @@ -17,5 +17,5 @@ test() { } SNACK="snack -j4" test -SNACK="snack -j4 -s ./snack.nix" test +SNACK="snack -j4 -s ./package.nix" test SNACK="snack -j4 --package-yaml ./package.yaml" test diff --git a/tests/packages/snack.nix b/tests/packages/package.nix similarity index 100% rename from tests/packages/snack.nix rename to tests/packages/package.nix diff --git a/tests/packages/test b/tests/packages/test index f92c517..0f7c742 100755 --- a/tests/packages/test +++ b/tests/packages/test @@ -17,5 +17,5 @@ test() { } SNACK="snack -j4" test -SNACK="snack -j4 -s ./snack.nix" test +SNACK="snack -j4 -s ./package.nix" test SNACK="snack -j4 --package-yaml ./package.yaml" test diff --git a/tests/readme/snack.nix b/tests/readme/package.nix similarity index 100% rename from tests/readme/snack.nix rename to tests/readme/package.nix diff --git a/tests/readme/test b/tests/readme/test index d5146a4..a2e5fcd 100755 --- a/tests/readme/test +++ b/tests/readme/test @@ -9,6 +9,6 @@ test() { } SNACK="snack -j4" test -SNACK="snack -j4 -s ./snack.nix" test -SNACK="snack -j4 -s ./snack.nix -l ../../snack-lib" test +SNACK="snack -j4 -s ./package.nix" test +SNACK="snack -j4 -s ./package.nix -l ../../snack-lib" test SNACK="snack -j4 --package-yaml ./package.yaml" test diff --git a/tests/run-args/snack.nix b/tests/run-args/package.nix similarity index 100% rename from tests/run-args/snack.nix rename to tests/run-args/package.nix diff --git a/tests/strips-versions/snack.nix b/tests/strips-versions/package.nix similarity index 100% rename from tests/strips-versions/snack.nix rename to tests/strips-versions/package.nix diff --git a/tests/strips-versions/test b/tests/strips-versions/test index 35e6d70..ebfd06d 100755 --- a/tests/strips-versions/test +++ b/tests/strips-versions/test @@ -16,5 +16,5 @@ test() { } SNACK="snack -j4" test -SNACK="snack -j4 -s ./snack.nix" test +SNACK="snack -j4 -s ./package.nix" test SNACK="snack -j4 --package-yaml ./package.yaml" test diff --git a/tests/template-haskell-2/code/snack.nix b/tests/template-haskell-2/code/package.nix similarity index 100% rename from tests/template-haskell-2/code/snack.nix rename to tests/template-haskell-2/code/package.nix diff --git a/tests/template-haskell-2/test b/tests/template-haskell-2/test index 2a9be58..cbebe94 100755 --- a/tests/template-haskell-2/test +++ b/tests/template-haskell-2/test @@ -3,12 +3,12 @@ set -euo pipefail -snack -j4 build -s code/snack.nix -snack -j4 run -s code/snack.nix | diff golden - +snack -j4 build -s code/package.nix +snack -j4 run -s code/package.nix | diff golden - TMP_FILE=$(mktemp) -capture_io "$TMP_FILE" main | snack -j4 -s code/snack.nix ghci +capture_io "$TMP_FILE" main | snack -j4 -s code/package.nix ghci diff golden $TMP_FILE rm $TMP_FILE diff --git a/tests/template-haskell-3/snack.nix b/tests/template-haskell-3/package.nix similarity index 100% rename from tests/template-haskell-3/snack.nix rename to tests/template-haskell-3/package.nix diff --git a/tests/template-haskell-4/snack.nix b/tests/template-haskell-4/package.nix similarity index 100% rename from tests/template-haskell-4/snack.nix rename to tests/template-haskell-4/package.nix diff --git a/tests/template-haskell/snack.nix b/tests/template-haskell/package.nix similarity index 100% rename from tests/template-haskell/snack.nix rename to tests/template-haskell/package.nix diff --git a/tests/trans-imp/snack.nix b/tests/trans-imp/package.nix similarity index 100% rename from tests/trans-imp/snack.nix rename to tests/trans-imp/package.nix diff --git a/tests/trans-imp/test b/tests/trans-imp/test index 36dea7d..ca79687 100755 --- a/tests/trans-imp/test +++ b/tests/trans-imp/test @@ -16,5 +16,5 @@ test() { } SNACK="snack -j4" test -SNACK="snack -j4 -s ./snack.nix" test +SNACK="snack -j4 -s ./package.nix" test # Note: no HPack test, because HPack doesn't support multi library diff --git a/tests/utf-8-BOM/snack.nix b/tests/utf-8-BOM/package.nix similarity index 100% rename from tests/utf-8-BOM/snack.nix rename to tests/utf-8-BOM/package.nix diff --git a/tests/utf-8-BOM/test b/tests/utf-8-BOM/test index 7ff56f4..a301102 100755 --- a/tests/utf-8-BOM/test +++ b/tests/utf-8-BOM/test @@ -16,5 +16,5 @@ test() { } SNACK="snack" test -SNACK="snack -s ./snack.nix" test +SNACK="snack -s ./package.nix" test SNACK="snack --package-yaml ./package.yaml" test diff --git a/tests/utf-8/snack.nix b/tests/utf-8/package.nix similarity index 100% rename from tests/utf-8/snack.nix rename to tests/utf-8/package.nix diff --git a/tests/utf-8/test b/tests/utf-8/test index 7ff56f4..a301102 100755 --- a/tests/utf-8/test +++ b/tests/utf-8/test @@ -16,5 +16,5 @@ test() { } SNACK="snack" test -SNACK="snack -s ./snack.nix" test +SNACK="snack -s ./package.nix" test SNACK="snack --package-yaml ./package.yaml" test From c21ab0b58c28350d88e8d9bc47e8cc2eb2a1ee68 Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Mon, 1 Oct 2018 20:34:24 +0300 Subject: [PATCH 17/43] exe: split pkg and source --- bin/Snack.hs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/bin/Snack.hs b/bin/Snack.hs index ff1410e..4e71176 100644 --- a/bin/Snack.hs +++ b/bin/Snack.hs @@ -268,12 +268,13 @@ nixBuild snackCfg extraNixArgs nixExpr = { #{ intercalate "," funArgs } }: let spec = builtins.fromJSON specJson; - pkgs = import (builtins.fetchTarball + pkgsSrc = (builtins.fetchTarball { url = "https://github.com/${spec.owner}/${spec.repo}/archive/${spec.rev}.tar.gz"; sha256 = spec.sha256; - }) {} ; + }); + pkgs = import pkgsSrc {}; libDir = #{ libDir }; - snack = pkgs.callPackage libDir {}; + snack = (import libDir) { inherit pkgs; }; in #{ T.unpack $ unNixExpr $ nixExpr } |]) "nix-build" From 20a242f5928770c1f1445653cb818ef30c704114 Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Mon, 1 Oct 2018 21:30:30 +0300 Subject: [PATCH 18/43] Source nixpkgs from snack.nix --- bin/Snack.hs | 55 ++++++++++++++----- tests/extended-config/golden | 1 + tests/extended-config/nix/default.nix | 24 ++++++++ .../extended-config/nix/extra-hs/Something.hs | 4 ++ .../something-that-doesnt-exist.cabal | 13 +++++ tests/extended-config/package.nix | 7 +++ tests/extended-config/package.yaml | 8 +++ tests/extended-config/snack.nix | 3 + tests/extended-config/src/Foo.hs | 4 ++ tests/extended-config/test | 21 +++++++ 10 files changed, 125 insertions(+), 15 deletions(-) create mode 100644 tests/extended-config/golden create mode 100644 tests/extended-config/nix/default.nix create mode 100644 tests/extended-config/nix/extra-hs/Something.hs create mode 100644 tests/extended-config/nix/extra-hs/something-that-doesnt-exist.cabal create mode 100644 tests/extended-config/package.nix create mode 100644 tests/extended-config/package.yaml create mode 100644 tests/extended-config/snack.nix create mode 100644 tests/extended-config/src/Foo.hs create mode 100755 tests/extended-config/test diff --git a/bin/Snack.hs b/bin/Snack.hs index 4e71176..aeac751 100644 --- a/bin/Snack.hs +++ b/bin/Snack.hs @@ -10,7 +10,7 @@ {-# LANGUAGE DataKinds #-} {-# LANGUAGE TypeFamilies #-} -module Main where +module Main (main) where import Control.Applicative import Control.Monad @@ -66,7 +66,13 @@ mkPackageNix :: FilePath -> IO PackageNix mkPackageNix = fmap PackageNix . canonicalizePath -- | Like a FilePath, but Nix friendly -newtype SnackLib = SnackLib { unSnackLib :: FilePath } +newtype SnackNix = SnackNix { _unSnackNix :: FilePath } + +mkSnackNix :: FilePath -> IO SnackNix +mkSnackNix = fmap SnackNix . canonicalizePath + +-- | Like a FilePath, but Nix friendly +newtype SnackLib = SnackLib { _unSnackLib :: FilePath } mkSnackLib :: FilePath -> IO SnackLib mkSnackLib = fmap SnackLib . canonicalizePath @@ -87,6 +93,7 @@ type SnackConfigRaw = SnackConfig_ 'ConfigRaw -- | Extra configuration for snack data SnackConfig_ c = SnackConfig { snackLib :: Config c (Maybe FilePath) (Maybe SnackLib) + , snackNix :: Maybe (Config c FilePath SnackNix) , snackNixCfg :: NixConfig } @@ -107,6 +114,7 @@ type Options = Options_ 'ConfigReady data Options_ c = Options { snackConfig :: SnackConfig_ c + -- , snackNix :: Maybe (Config c FilePath SnackNix) , mode :: Mode_ c , command :: Command } @@ -125,6 +133,7 @@ prepareSnackConfig raw = Nothing -> pure Nothing Just fp -> Just <$> mkSnackLib fp ) <*> + (forM (snackNix raw) mkSnackNix) <*> pure (snackNixCfg raw) parseNixConfig :: Opts.Parser NixConfig @@ -152,6 +161,12 @@ parseSnackConfig = SnackConfig <$> Opts.optional ) ) ) <*> + Opts.optional ( + Opts.strOption + (Opts.long "snack-nix" + <> Opts.short 'b' + <> Opts.metavar "PATH") + ) <*> parseNixConfig parseMode :: Opts.Parser ModeRaw @@ -176,10 +191,16 @@ parseOptions :: Opts.Parser OptionsRaw parseOptions = Options <$> parseSnackConfig <*> + -- Opts.optional ( + -- Opts.strOption + -- (Opts.long "snack-nix" + -- <> Opts.short 'b' + -- <> Opts.metavar "PATH") + -- ) <*> parseMode <*> parseCommand -newtype ModuleName = ModuleName { unModuleName :: T.Text } +newtype ModuleName = ModuleName { _unModuleName :: T.Text } deriving newtype (Ord, Eq, Aeson.FromJSONKey) deriving stock Show @@ -215,12 +236,12 @@ instance FromJSON GhciBuild where -- The kinds of builds: library, executable, or a mix of both (currently only -- for HPack) newtype LibraryBuild = LibraryBuild - { unLibraryBuild :: Map.Map ModuleName NixPath } + { _unLibraryBuild :: Map.Map ModuleName NixPath } deriving newtype FromJSON deriving stock Show newtype ExecutableBuild = ExecutableBuild - { exePath :: NixPath } + { _exePath :: NixPath } deriving stock Show instance FromJSON ExecutableBuild where @@ -228,7 +249,7 @@ instance FromJSON ExecutableBuild where ExecutableBuild <$> o .: "exe_path" data MultiBuild = MultiBuild - { librayBuild :: Maybe LibraryBuild + { _librayBuild :: Maybe LibraryBuild , executableBuilds :: Map.Map T.Text ExecutableBuild } deriving stock Show @@ -251,7 +272,7 @@ data NixArgType newtype NixExpr = NixExpr { unNixExpr :: T.Text } -newtype NixPath = NixPath { unNixPath :: T.Text } +newtype NixPath = NixPath { _unNixPath :: T.Text } deriving newtype FromJSON deriving stock Show @@ -268,11 +289,7 @@ nixBuild snackCfg extraNixArgs nixExpr = { #{ intercalate "," funArgs } }: let spec = builtins.fromJSON specJson; - pkgsSrc = (builtins.fetchTarball - { url = "https://github.com/${spec.owner}/${spec.repo}/archive/${spec.rev}.tar.gz"; - sha256 = spec.sha256; - }); - pkgs = import pkgsSrc {}; + pkgs = import #{ pkgsSrc } {}; libDir = #{ libDir }; snack = (import libDir) { inherit pkgs; }; in #{ T.unpack $ unNixExpr $ nixExpr } @@ -280,6 +297,17 @@ nixBuild snackCfg extraNixArgs nixExpr = "nix-build" cliArgs where + pkgsSrc :: String + pkgsSrc = case snackNix snackCfg of + Just (SnackNix fp) -> + [i|(import #{ fp }).pkgs|] + Nothing -> + [i| + (builtins.fetchTarball + { url = "https://github.com/${spec.owner}/${spec.repo}/archive/${spec.rev}.tar.gz"; + sha256 = spec.sha256; + }) + |] libDir :: String libDir = case snackLib snackCfg of Just (SnackLib fp) -> fp @@ -417,9 +445,6 @@ runStdin1 stin p args = do [out] -> pure out xs -> throwIO $ userError $ "unexpected output: " <> show xs -run_ :: S.FilePath -> [T.Text] -> Sh () -run_ p args = void $ run p args - specJson :: T.Text specJson = $(embedStringFile "spec.json") diff --git a/tests/extended-config/golden b/tests/extended-config/golden new file mode 100644 index 0000000..ce01362 --- /dev/null +++ b/tests/extended-config/golden @@ -0,0 +1 @@ +hello diff --git a/tests/extended-config/nix/default.nix b/tests/extended-config/nix/default.nix new file mode 100644 index 0000000..5881fb9 --- /dev/null +++ b/tests/extended-config/nix/default.nix @@ -0,0 +1,24 @@ +{}: +let + spec = builtins.fromJSON (builtins.readFile ../../../nix/nixpkgs/nixpkgs-src.json); + nixpkgs = + (builtins.fetchTarball + { url = "https://github.com/${spec.owner}/${spec.repo}/archive/${spec.rev}.tar.gz"; + sha256 = spec.sha256; + }); +in + import nixpkgs + { overlays = + [ + (self: super: + { haskellPackages = super.haskellPackages.extend + (super.haskell.lib.packageSourceOverrides + { something-that-doesnt-exist = + self.lib.cleanSource ./extra-hs; + } + ); + } + ) + ]; + } + diff --git a/tests/extended-config/nix/extra-hs/Something.hs b/tests/extended-config/nix/extra-hs/Something.hs new file mode 100644 index 0000000..36732fa --- /dev/null +++ b/tests/extended-config/nix/extra-hs/Something.hs @@ -0,0 +1,4 @@ +module Something where + +foo :: String +foo = "hello" diff --git a/tests/extended-config/nix/extra-hs/something-that-doesnt-exist.cabal b/tests/extended-config/nix/extra-hs/something-that-doesnt-exist.cabal new file mode 100644 index 0000000..dcc4a6e --- /dev/null +++ b/tests/extended-config/nix/extra-hs/something-that-doesnt-exist.cabal @@ -0,0 +1,13 @@ +name: something-that-doesnt-exist +version: 1.0.0.1 +description: Foo Bar +license: MIT +author: Nicolas Mattia +maintainer: nicolas@nmattia.com +build-type: Simple +cabal-version: >=1.2 + +library + build-depends: base >= 3 && < 5 + exposed-modules: Something + ghc-options: -Wall diff --git a/tests/extended-config/package.nix b/tests/extended-config/package.nix new file mode 100644 index 0000000..1cd8f1d --- /dev/null +++ b/tests/extended-config/package.nix @@ -0,0 +1,7 @@ +{ main = "Foo"; + src = ./src; + dependencies = [ + "conduit" + "something-that-doesnt-exist" + ]; +} diff --git a/tests/extended-config/package.yaml b/tests/extended-config/package.yaml new file mode 100644 index 0000000..2b71397 --- /dev/null +++ b/tests/extended-config/package.yaml @@ -0,0 +1,8 @@ +name: snack-packages-test +dependencies: + - conduit + - something-that-doesnt-exist + +executable: + main: Foo.hs + source-dirs: src diff --git a/tests/extended-config/snack.nix b/tests/extended-config/snack.nix new file mode 100644 index 0000000..124487a --- /dev/null +++ b/tests/extended-config/snack.nix @@ -0,0 +1,3 @@ +{ + pkgs = ./nix; +} diff --git a/tests/extended-config/src/Foo.hs b/tests/extended-config/src/Foo.hs new file mode 100644 index 0000000..3ab7125 --- /dev/null +++ b/tests/extended-config/src/Foo.hs @@ -0,0 +1,4 @@ +import qualified Something + +main :: IO () +main = putStrLn Something.foo diff --git a/tests/extended-config/test b/tests/extended-config/test new file mode 100755 index 0000000..2ca1d4e --- /dev/null +++ b/tests/extended-config/test @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +# vim: ft=sh sw=2 et + +set -euo pipefail + + +test() { + $SNACK build + $SNACK run | diff golden - + + TMP_FILE=$(mktemp) + + capture_io "$TMP_FILE" main | $SNACK ghci + + diff golden $TMP_FILE + rm $TMP_FILE +} + +SNACK="snack -j4 -b snack.nix -s package.nix" test +SNACK="snack -j4 -b snack.nix" test +SNACK="snack -j4 -b snack.nix --package-yaml package.yaml" test From b17afb31c0b991a149ab8e8a9513c0265aea16e3 Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Mon, 1 Oct 2018 21:33:08 +0300 Subject: [PATCH 19/43] Clean up --- bin/Snack.hs | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/bin/Snack.hs b/bin/Snack.hs index aeac751..cdc00d4 100644 --- a/bin/Snack.hs +++ b/bin/Snack.hs @@ -66,13 +66,13 @@ mkPackageNix :: FilePath -> IO PackageNix mkPackageNix = fmap PackageNix . canonicalizePath -- | Like a FilePath, but Nix friendly -newtype SnackNix = SnackNix { _unSnackNix :: FilePath } +newtype SnackNix = SnackNix FilePath mkSnackNix :: FilePath -> IO SnackNix mkSnackNix = fmap SnackNix . canonicalizePath -- | Like a FilePath, but Nix friendly -newtype SnackLib = SnackLib { _unSnackLib :: FilePath } +newtype SnackLib = SnackLib FilePath mkSnackLib :: FilePath -> IO SnackLib mkSnackLib = fmap SnackLib . canonicalizePath @@ -114,7 +114,6 @@ type Options = Options_ 'ConfigReady data Options_ c = Options { snackConfig :: SnackConfig_ c - -- , snackNix :: Maybe (Config c FilePath SnackNix) , mode :: Mode_ c , command :: Command } @@ -133,7 +132,7 @@ prepareSnackConfig raw = Nothing -> pure Nothing Just fp -> Just <$> mkSnackLib fp ) <*> - (forM (snackNix raw) mkSnackNix) <*> + forM (snackNix raw) mkSnackNix <*> pure (snackNixCfg raw) parseNixConfig :: Opts.Parser NixConfig @@ -191,16 +190,10 @@ parseOptions :: Opts.Parser OptionsRaw parseOptions = Options <$> parseSnackConfig <*> - -- Opts.optional ( - -- Opts.strOption - -- (Opts.long "snack-nix" - -- <> Opts.short 'b' - -- <> Opts.metavar "PATH") - -- ) <*> parseMode <*> parseCommand -newtype ModuleName = ModuleName { _unModuleName :: T.Text } +newtype ModuleName = ModuleName T.Text deriving newtype (Ord, Eq, Aeson.FromJSONKey) deriving stock Show @@ -235,13 +228,11 @@ instance FromJSON GhciBuild where -- The kinds of builds: library, executable, or a mix of both (currently only -- for HPack) -newtype LibraryBuild = LibraryBuild - { _unLibraryBuild :: Map.Map ModuleName NixPath } +newtype LibraryBuild = LibraryBuild (Map.Map ModuleName NixPath) deriving newtype FromJSON deriving stock Show -newtype ExecutableBuild = ExecutableBuild - { _exePath :: NixPath } +newtype ExecutableBuild = ExecutableBuild NixPath deriving stock Show instance FromJSON ExecutableBuild where @@ -272,7 +263,7 @@ data NixArgType newtype NixExpr = NixExpr { unNixExpr :: T.Text } -newtype NixPath = NixPath { _unNixPath :: T.Text } +newtype NixPath = NixPath T.Text deriving newtype FromJSON deriving stock Show From 726690f4c8014b64aed0697ae2ab32939df17bde Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Tue, 2 Oct 2018 23:38:41 +0300 Subject: [PATCH 20/43] snack.nix: allow more overrides --- README.md | 54 +++++++++++++++++++++++++-- bin/Snack.hs | 19 ++++++---- script/install | 2 + snack-lib/default.nix | 4 +- tests/extended-config/nix/default.nix | 1 - tests/extended-config/snack.nix | 5 ++- tests/extended-config/test | 6 +-- 7 files changed, 74 insertions(+), 17 deletions(-) create mode 100755 script/install diff --git a/README.md b/README.md index b77cef9..81f54db 100644 --- a/README.md +++ b/README.md @@ -67,6 +67,12 @@ _See the [Hacking](#hacking) section if you want to hack on snack_ Assuming that [Nix][nix] is installed on your machine, clone this repo and run: +``` shell +$ ./script/install +``` + +which is equivalent to + ``` shell $ nix-env -f ./default.nix -iA snack-exe ``` @@ -75,8 +81,8 @@ The _snack_ executable is now in your `PATH`: ``` shell $ snack --help -Usage: snack [-l|--lib DIR] [-j|--cores INT] ([-s|--package-nix PATH] | - [-p|--package-yaml PATH]) COMMAND +Usage: snack [-l|--lib DIR] [-b|--snack-nix PATH] [-j|--cores INT] + ([-s|--package-nix PATH] | [-p|--package-yaml PATH]) COMMAND Available options: -l,--lib DIR Path to the directory to use as the Nix library @@ -221,8 +227,50 @@ $ snack run # looks for a file called package.nix by default Alternatively, use `$ snack build` or `$ snack ghci` if you only want to build, or fire up `ghci`, respectively. -### Advanced Nix Example +### Using other versions of GHC and nixpkgs + +The _snack_ executable comes with a [bundled version of +nixpkgs](./nix/nixpkgs/nixpkgs-src.json) and uses the GHC executable provided +by `haskell.packages.ghc822.ghcWithPackages`. You may override those default by +providing a `snack.nix`: +``` shell +$ snack --snack-nix ./snack.nix build +``` + +This file looks like the following: + + +``` nix +rec { + # If you only wish to change the version of GHC being used, set + # `ghc-version`. The following versions are currently available: + # * ghc7103 + # * ghc7103Binary + # * ghc802 + # * ghc821Binary + # * ghc822 + # * ghc841 + # * ghc842 + # * ghcHEAD + # * ghcjs + # * ghcjsHEAD + # * integer-simple + # NOTE: not all versions have been tested with snack. + ghc-version = "ghc802"; + + # Alternatively you can provide you own `ghcWithPackages`, which should have + # the same structure as that provided by + # `pkgs.haskell.packages..ghcWithPackages: + ghcWithPackages = pkgs.haskellPackages.ghcWithPackages; + + # Finally you can provide your own set of Nix packages, which should evaluate + # to an attribute set: + pkgs = import ./nix; +} +``` + +### Advanced Nix Example You may want custom builds that involve things such as [archiving and base64 encoding entire diff --git a/bin/Snack.hs b/bin/Snack.hs index cdc00d4..c4ed31e 100644 --- a/bin/Snack.hs +++ b/bin/Snack.hs @@ -280,9 +280,10 @@ nixBuild snackCfg extraNixArgs nixExpr = { #{ intercalate "," funArgs } }: let spec = builtins.fromJSON specJson; - pkgs = import #{ pkgsSrc } {}; + config = #{ pkgsSrc }; + pkgs = config.pkgs; libDir = #{ libDir }; - snack = (import libDir) { inherit pkgs; }; + snack = (import libDir) config; in #{ T.unpack $ unNixExpr $ nixExpr } |]) "nix-build" @@ -291,13 +292,17 @@ nixBuild snackCfg extraNixArgs nixExpr = pkgsSrc :: String pkgsSrc = case snackNix snackCfg of Just (SnackNix fp) -> - [i|(import #{ fp }).pkgs|] + [i|(import #{ fp })|] Nothing -> [i| - (builtins.fetchTarball - { url = "https://github.com/${spec.owner}/${spec.repo}/archive/${spec.rev}.tar.gz"; - sha256 = spec.sha256; - }) + { pkgs = import + ( + builtins.fetchTarball + { url = "https://github.com/${spec.owner}/${spec.repo}/archive/${spec.rev}.tar.gz"; + sha256 = spec.sha256; + } + ) {} ; + } |] libDir :: String libDir = case snackLib snackCfg of diff --git a/script/install b/script/install new file mode 100755 index 0000000..5a0e9b7 --- /dev/null +++ b/script/install @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +nix-env -f ./default.nix -iA snack-exe diff --git a/snack-lib/default.nix b/snack-lib/default.nix index f355331..fe7ec86 100644 --- a/snack-lib/default.nix +++ b/snack-lib/default.nix @@ -2,6 +2,8 @@ # TODO: currently single out derivations prepend the PWD to the path # TODO: make sure that filters for "base" are airtight { pkgs +, ghc-version ? "ghc822" +, ghcWithPackages ? pkgs.haskell.packages.${ghc-version}.ghcWithPackages }: with pkgs; @@ -16,7 +18,7 @@ with (callPackage ./package-spec.nix {}); with (callPackage ./hpack.nix {}); let - ghcWith = deps: haskellPackages.ghcWithPackages + ghcWith = deps: ghcWithPackages (ps: map (p: ps.${p}) deps); # Assumes the package description describes an executable diff --git a/tests/extended-config/nix/default.nix b/tests/extended-config/nix/default.nix index 5881fb9..12a09a1 100644 --- a/tests/extended-config/nix/default.nix +++ b/tests/extended-config/nix/default.nix @@ -1,4 +1,3 @@ -{}: let spec = builtins.fromJSON (builtins.readFile ../../../nix/nixpkgs/nixpkgs-src.json); nixpkgs = diff --git a/tests/extended-config/snack.nix b/tests/extended-config/snack.nix index 124487a..7ac1785 100644 --- a/tests/extended-config/snack.nix +++ b/tests/extended-config/snack.nix @@ -1,3 +1,4 @@ -{ - pkgs = ./nix; +rec { + pkgs = import ./nix; + ghcWithPackages = pkgs.haskellPackages.ghcWithPackages; } diff --git a/tests/extended-config/test b/tests/extended-config/test index 2ca1d4e..8fc298d 100755 --- a/tests/extended-config/test +++ b/tests/extended-config/test @@ -16,6 +16,6 @@ test() { rm $TMP_FILE } -SNACK="snack -j4 -b snack.nix -s package.nix" test -SNACK="snack -j4 -b snack.nix" test -SNACK="snack -j4 -b snack.nix --package-yaml package.yaml" test +SNACK="snack -j4 --snack-nix snack.nix --package-nix package.nix" test +SNACK="snack -j4 --snack-nix snack.nix" test +SNACK="snack -j4 --snack-nix snack.nix --package-yaml package.yaml" test From 4ab444202687b0553495fe3905b8102d4cf709fe Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Thu, 4 Oct 2018 21:33:26 +0300 Subject: [PATCH 21/43] changelog: update --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2219ed0..ff333e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,10 +9,14 @@ Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to ### Added - This CHANGELOG file to track changes to the command line and library APIs. +- Support for custom GHC version, `ghcWithPackages` and `pkgs` ### Changed - The `snack run` function to accept arguments that will be passed to the built executable. +- The `snack.nix` now describes the build environment and packages are + described through `package.nix` (i.e. to migrate: rename `snack.nix` to + `package.nix`) ### Fixed - The module import parsing when the CPP extension is enabled. From 426de770f4aee27d9d7347f54356b2dda29d17ac Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Wed, 10 Oct 2018 17:19:37 -0400 Subject: [PATCH 22/43] Add links to usage subsections, #82. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 81f54db..f1eb59a 100644 --- a/README.md +++ b/README.md @@ -99,8 +99,8 @@ Available commands: ## Usage -You can use Hpack (for simple builds or if you already have a `package.yaml`) -or Nix (if you need more control over your build). +You can use [Hpack](#hpack) (for simple builds or if you already have +a `package.yaml`) or [Nix](#nix) (if you need more control over your build). The next two sections show an example config for each option. They use the following example project which displays the title of the top-rated post on the From e6aa4586af8b5b003c83280d9929538484c004a9 Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Wed, 10 Oct 2018 17:39:31 -0400 Subject: [PATCH 23/43] Use the package options as the link label. --- README.md | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f1eb59a..2ee3b51 100644 --- a/README.md +++ b/README.md @@ -99,8 +99,15 @@ Available commands: ## Usage -You can use [Hpack](#hpack) (for simple builds or if you already have -a `package.yaml`) or [Nix](#nix) (if you need more control over your build). +Snack can be used to build, run and interact with packages. + +There are two ways to tell snack about a package; +* Use [`--package-nix`](#nix) if you need more control over your build. +* Use [`--package-yaml`](#hpack) for simple builds or if you already have +a `package.yaml` file. + +If a package option is not supplied then snack will run as if +`--package-nix=package.nix` was given as the package option. The next two sections show an example config for each option. They use the following example project which displays the title of the top-rated post on the From 736e5cf037ad714ea29c93a7611d76e09f96bedf Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Wed, 10 Oct 2018 17:40:25 -0400 Subject: [PATCH 24/43] Word wrap only, #82. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2ee3b51..ecf4ede 100644 --- a/README.md +++ b/README.md @@ -104,7 +104,7 @@ Snack can be used to build, run and interact with packages. There are two ways to tell snack about a package; * Use [`--package-nix`](#nix) if you need more control over your build. * Use [`--package-yaml`](#hpack) for simple builds or if you already have -a `package.yaml` file. + a `package.yaml` file. If a package option is not supplied then snack will run as if `--package-nix=package.nix` was given as the package option. From 615bb1b959ea553786880163e7c78c1212ea37f8 Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Thu, 11 Oct 2018 10:18:33 -0400 Subject: [PATCH 25/43] Mention test command absence, #84. --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ecf4ede..bb97b49 100644 --- a/README.md +++ b/README.md @@ -97,9 +97,11 @@ Available commands: ghci ``` -## Usage +Snack can be used to build, run and interact with packages. There is no +**test** command as we treat test suites as we do executables, giving each test +suite its own package description. -Snack can be used to build, run and interact with packages. +## Usage There are two ways to tell snack about a package; * Use [`--package-nix`](#nix) if you need more control over your build. From 537e712c9d3c126d55107359cc52c6f5d7a9f598 Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Thu, 11 Oct 2018 11:18:34 -0400 Subject: [PATCH 26/43] Add test as a faux command, #84. --- README.md | 3 +++ bin/Snack.hs | 12 +++++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index bb97b49..6f374a8 100644 --- a/README.md +++ b/README.md @@ -95,6 +95,9 @@ Available commands: build run ghci + +Unavailable commands: + test Use build, run or ghci commands with test suites. ``` Snack can be used to build, run and interact with packages. There is no diff --git a/bin/Snack.hs b/bin/Snack.hs index c4ed31e..968807f 100644 --- a/bin/Snack.hs +++ b/bin/Snack.hs @@ -101,6 +101,7 @@ data Command = Build | Run [String] -- Run with extra args | Ghci + | Test main :: IO () main = do @@ -400,12 +401,17 @@ runCommand snackCfg (Standalone packageNix) = \case Run args -> quiet (snackBuild snackCfg packageNix) >>= runBuildResult args Ghci -> flip runExe [] =<< ghciExePath <$> (quiet (snackGhci snackCfg packageNix)) + Test -> noTest runCommand snackCfg (HPack packageYaml) = \case Build -> S.shelly $ void $ snackBuildHPack snackCfg packageYaml Run args -> quiet (snackBuildHPack snackCfg packageYaml) >>= runBuildResult args Ghci -> flip runExe [] =<< ghciExePath <$> quiet (snackGhciHPack snackCfg packageYaml) + Test -> noTest + +noTest :: IO a +noTest = fail "There is no test command for test suites" runBuildResult :: [String] -> BuildResult -> IO () runBuildResult args = \case @@ -423,13 +429,17 @@ runExe (NixPath fp) args = executeFile (T.unpack fp) True args Nothing parseCommand :: Opts.Parser Command parseCommand = - Opts.hsubparser $ + Opts.hsubparser ( Opts.command "build" (Opts.info (pure Build) mempty) <> Opts.command "run" (Opts.info ( Run <$> Opts.many (Opts.argument Opts.str (Opts.metavar "ARG")) ) mempty) <> Opts.command "ghci" (Opts.info (pure Ghci) mempty) ) + <|> Opts.hsubparser + ( Opts.command "test" (Opts.info (pure Test) (Opts.progDesc "Use build, run or ghci commands with test suites.")) + <> Opts.commandGroup "Unavailable commands:" + ) run :: S.FilePath -> [T.Text] -> Sh [T.Text] run p args = T.lines <$> S.run p args From 9ed309a46300886fab5f682f8aa89a1b4869a65c Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Thu, 11 Oct 2018 13:10:56 -0400 Subject: [PATCH 27/43] Add a test of the use of hpack-dhall using a dhall import, #51. Also use dhall format --inplace on the existing *.dhall files. --- tests/readme/package-defaults.dhall | 1 + tests/readme/package-import.dhall | 18 ++++++++++++++++++ tests/readme/package.dhall | 19 ++++++++++--------- tests/readme/test | 1 + 4 files changed, 30 insertions(+), 9 deletions(-) create mode 100644 tests/readme/package-defaults.dhall create mode 100644 tests/readme/package-import.dhall diff --git a/tests/readme/package-defaults.dhall b/tests/readme/package-defaults.dhall new file mode 100644 index 0000000..e22f678 --- /dev/null +++ b/tests/readme/package-defaults.dhall @@ -0,0 +1 @@ +{ default-extensions = [ "OverloadedStrings" ] } diff --git a/tests/readme/package-import.dhall b/tests/readme/package-import.dhall new file mode 100644 index 0000000..eaf6c71 --- /dev/null +++ b/tests/readme/package-import.dhall @@ -0,0 +1,18 @@ + let defs = ./package-defaults.dhall + +in defs + ⫽ { name = + "snack-readme" + , dependencies = + [ "lens", "wreq" ] + , library = + { source-dirs = "./src" } + , executable = + { main = + "Main.hs" + , source-dirs = + "./app" + , dependencies = + [ "snack-readme" ] + } + } diff --git a/tests/readme/package.dhall b/tests/readme/package.dhall index ab0811d..7de86a0 100644 --- a/tests/readme/package.dhall +++ b/tests/readme/package.dhall @@ -1,16 +1,17 @@ -{ name = "snack-readme" - +{ name = + "snack-readme" , dependencies = [ "lens", "wreq" ] - , library = { source-dirs = "./src" } - , executable = - { main = "Main.hs" - , source-dirs = "./app" - , dependencies = [ "snack-readme" ] + { main = + "Main.hs" + , source-dirs = + "./app" + , dependencies = + [ "snack-readme" ] } - -, default-extensions = [ "OverloadedStrings" ] +, default-extensions = + [ "OverloadedStrings" ] } diff --git a/tests/readme/test b/tests/readme/test index d026806..76b6b2c 100755 --- a/tests/readme/test +++ b/tests/readme/test @@ -13,3 +13,4 @@ SNACK="snack -j4 -s ./package.nix" test SNACK="snack -j4 -s ./package.nix -l ../../snack-lib" test SNACK="snack -j4 --package-yaml ./package.yaml" test SNACK="snack -j4 --package-yaml ./package.dhall" test +SNACK="snack -j4 --package-yaml ./package-import.dhall" test From e42469b67f8c16ae5bef9b304592fe4bb918f19f Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Fri, 12 Oct 2018 15:15:00 -0400 Subject: [PATCH 28/43] Yaml.decode is deprecated, use decodeEither' instead, #87. --- snack-lib/YamlToJson.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/snack-lib/YamlToJson.hs b/snack-lib/YamlToJson.hs index 0d25d2c..dfcfc84 100644 --- a/snack-lib/YamlToJson.hs +++ b/snack-lib/YamlToJson.hs @@ -13,5 +13,5 @@ main :: IO () main = do [file] <- getArgs yaml <- BS8.readFile file - let Just value = Yaml.decode yaml :: Maybe Aeson.Value + let Right value = Yaml.decodeEither' yaml :: Either Yaml.ParseException Aeson.Value BL8.putStrLn $ Aeson.encode value From ebd34c343139556ac2c18bad2997baa824090fe2 Mon Sep 17 00:00:00 2001 From: 2mol <1773075+2mol@users.noreply.github.com> Date: Sun, 21 Oct 2018 13:02:28 +0200 Subject: [PATCH 29/43] Update Snack.hs --- bin/Snack.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/Snack.hs b/bin/Snack.hs index 968807f..39b1e1e 100644 --- a/bin/Snack.hs +++ b/bin/Snack.hs @@ -40,7 +40,7 @@ data ConfigStage | ConfigReady type family Config (c :: ConfigStage) ty1 ty2 where - Config 'ConfigRaw ty1 _= ty1 + Config 'ConfigRaw ty1 _ = ty1 Config 'ConfigReady _ ty2 = ty2 --- From d4ec01d2d935b3e69f34be3e31f81a808ffdf886 Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Wed, 10 Oct 2018 15:14:59 -0400 Subject: [PATCH 30/43] Reproduction for swp file as input, #80. --- tests/swp/package.nix | 3 +++ tests/swp/src/Foo/.Bar.hs.swp | Bin 0 -> 12288 bytes tests/swp/src/Foo/Bar.hs | 9 +++++++++ 3 files changed, 12 insertions(+) create mode 100644 tests/swp/package.nix create mode 100644 tests/swp/src/Foo/.Bar.hs.swp create mode 100644 tests/swp/src/Foo/Bar.hs diff --git a/tests/swp/package.nix b/tests/swp/package.nix new file mode 100644 index 0000000..ec5e4af --- /dev/null +++ b/tests/swp/package.nix @@ -0,0 +1,3 @@ +{ src = ./src; + dependencies = [ "conduit" ]; +} diff --git a/tests/swp/src/Foo/.Bar.hs.swp b/tests/swp/src/Foo/.Bar.hs.swp new file mode 100644 index 0000000000000000000000000000000000000000..61668ba4faf61e588ef6c3d553807c645163f080 GIT binary patch literal 12288 zcmeI&u}Z^07zgmLZVsa83w-L*4koQGrQl*gs8YIUmm-q(PJ1@Zh1|vJQhgMC5Pbw6 zL7g0Rck)tF5r=kG{vZ4~F5lh3{kD*F%k4&!t6qg%tq?sOK6c(-4+^)RM6r+;I=#v| zGb`v) Ur>7GQwMawB?#Fr_^<^UI3&iPS#{d8T literal 0 HcmV?d00001 diff --git a/tests/swp/src/Foo/Bar.hs b/tests/swp/src/Foo/Bar.hs new file mode 100644 index 0000000..604012a --- /dev/null +++ b/tests/swp/src/Foo/Bar.hs @@ -0,0 +1,9 @@ +module Bar where + +import Conduit + +spitOut :: ConduitT () Int IO () +spitOut = yieldMany [ 1 ..] + +digest :: ConduitT Int Void IO () +digest = mapM_C print From befe5cab69ea319402be11b0088c1863634cea27 Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Sun, 21 Oct 2018 13:47:32 +0200 Subject: [PATCH 31/43] Add test file for swp file repro --- tests/swp/test | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100755 tests/swp/test diff --git a/tests/swp/test b/tests/swp/test new file mode 100755 index 0000000..7eb46a1 --- /dev/null +++ b/tests/swp/test @@ -0,0 +1,11 @@ +#!/usr/bin/env bash +# vim: ft=sh sw=2 et + +set -euo pipefail + +test() { + echo | $SNACK ghci + $SNACK run +} + +SNACK="snack -j4" test From 217cd28b91f53e536db549ef88ea3d2c5ee1c6be Mon Sep 17 00:00:00 2001 From: 2mol <2mol@users.noreply.github.com> Date: Sun, 21 Oct 2018 13:38:41 +0200 Subject: [PATCH 32/43] improve matching against .hs files --- snack-lib/modules.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/snack-lib/modules.nix b/snack-lib/modules.nix index 024dd86..a2b62ea 100644 --- a/snack-lib/modules.nix +++ b/snack-lib/modules.nix @@ -39,7 +39,7 @@ rec { # Whether the file is a Haskell module or not. It uses very simple # heuristics: If the file starts with a capital letter, then yes. isHaskellModuleFile = f: - ! (builtins.isNull (builtins.match "[A-Z].*" f)); + ! (builtins.isNull (builtins.match "[a-zA-Z].*[.]hs$" (builtins.baseNameOf f))); listModulesInDir = dir: map fileToModule From bda96ca06b7ff4dd6062f021743e9a60386f22e8 Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Sun, 21 Oct 2018 13:51:42 +0200 Subject: [PATCH 33/43] Fix vim swp file repro --- tests/swp/golden.jq | 3 +++ tests/swp/test | 7 +++++-- 2 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 tests/swp/golden.jq diff --git a/tests/swp/golden.jq b/tests/swp/golden.jq new file mode 100644 index 0000000..9b668bb --- /dev/null +++ b/tests/swp/golden.jq @@ -0,0 +1,3 @@ +[ + "Foo.Bar" +] diff --git a/tests/swp/test b/tests/swp/test index 7eb46a1..cfefea5 100755 --- a/tests/swp/test +++ b/tests/swp/test @@ -4,8 +4,11 @@ set -euo pipefail test() { - echo | $SNACK ghci - $SNACK run + TMP_FILE=$(mktemp) + + cat $($SNACK build) | jq -M '.result | keys' > $TMP_FILE + + diff golden.jq $TMP_FILE } SNACK="snack -j4" test From e33133b394fabf2158db5c4aa5cde5101ce8559f Mon Sep 17 00:00:00 2001 From: 2mol <1773075+2mol@users.noreply.github.com> Date: Sun, 21 Oct 2018 14:03:21 +0200 Subject: [PATCH 34/43] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ff333e0..2d9dd15 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to ### Added - This CHANGELOG file to track changes to the command line and library APIs. - Support for custom GHC version, `ghcWithPackages` and `pkgs` +- Fix matching on Haskell files. Any file in any subdirectory ending in `.hs` will be matched, both lower- and uppercase filenames are accepted. ### Changed - The `snack run` function to accept arguments that will be passed to the built From 61540cfddd7ab6cdc9a0934f97577c6129784b54 Mon Sep 17 00:00:00 2001 From: 2mol <1773075+2mol@users.noreply.github.com> Date: Sun, 21 Oct 2018 14:13:05 +0200 Subject: [PATCH 35/43] Update CHANGELOG.md --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d9dd15..1821a5a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,6 @@ Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to ### Added - This CHANGELOG file to track changes to the command line and library APIs. - Support for custom GHC version, `ghcWithPackages` and `pkgs` -- Fix matching on Haskell files. Any file in any subdirectory ending in `.hs` will be matched, both lower- and uppercase filenames are accepted. ### Changed - The `snack run` function to accept arguments that will be passed to the built @@ -21,6 +20,7 @@ Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to ### Fixed - The module import parsing when the CPP extension is enabled. -- The module import parsing when a BOM is present +- The module import parsing when a BOM is present. +- The matching on Haskell files. Any file in any subdirectory ending in `.hs` will be matched, both lower- and uppercase filenames are accepted. [Unreleased]: https://github.com/nmattia/snack/compare/51987daf76cffc31289e6913174dfb46b93df36b...HEAD From 3f94647bd4f1693fefc40e571c2fe454032bb7d4 Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Sun, 21 Oct 2018 18:28:06 +0200 Subject: [PATCH 36/43] Add test for extension enabling in parser --- tests/cpp-2/Main.hs | 7 +++++++ tests/cpp-2/golden | 1 + tests/cpp-2/package.nix | 4 ++++ tests/cpp-2/test | 18 ++++++++++++++++++ 4 files changed, 30 insertions(+) create mode 100644 tests/cpp-2/Main.hs create mode 100644 tests/cpp-2/golden create mode 100644 tests/cpp-2/package.nix create mode 100755 tests/cpp-2/test diff --git a/tests/cpp-2/Main.hs b/tests/cpp-2/Main.hs new file mode 100644 index 0000000..cbf11c4 --- /dev/null +++ b/tests/cpp-2/Main.hs @@ -0,0 +1,7 @@ +module Main where + +main :: IO () +main = putStrLn "hello" + +foo :: forall a. a -> a +foo = id diff --git a/tests/cpp-2/golden b/tests/cpp-2/golden new file mode 100644 index 0000000..ce01362 --- /dev/null +++ b/tests/cpp-2/golden @@ -0,0 +1 @@ +hello diff --git a/tests/cpp-2/package.nix b/tests/cpp-2/package.nix new file mode 100644 index 0000000..f60a3c8 --- /dev/null +++ b/tests/cpp-2/package.nix @@ -0,0 +1,4 @@ +{ main = "Main"; + src = ./.; + extensions = ["RankNTypes" "CPP"]; +} diff --git a/tests/cpp-2/test b/tests/cpp-2/test new file mode 100755 index 0000000..f958d6b --- /dev/null +++ b/tests/cpp-2/test @@ -0,0 +1,18 @@ +#!/usr/bin/env bash +# vim: ft=sh sw=2 et + +set -euo pipefail + +test() { + $SNACK build + $SNACK run | diff golden - + + TMP_FILE=$(mktemp) + + capture_io "$TMP_FILE" main | $SNACK ghci + + diff golden $TMP_FILE + rm $TMP_FILE +} + +SNACK="snack -j4" test From 6bfd26649460fac5bf267fac4b358b859af3bed4 Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Sun, 21 Oct 2018 18:51:35 +0200 Subject: [PATCH 37/43] Fix CPP parsing with global extensions --- snack-lib/Imports.hs | 26 ++++++++++++++++---------- snack-lib/module-spec.nix | 2 +- snack-lib/modules.nix | 12 ++++++++---- 3 files changed, 25 insertions(+), 15 deletions(-) diff --git a/snack-lib/Imports.hs b/snack-lib/Imports.hs index 54633bd..b8e7a16 100644 --- a/snack-lib/Imports.hs +++ b/snack-lib/Imports.hs @@ -29,10 +29,9 @@ import System.IO (stderr) main :: IO () main = do - fp <- getArgs >>= \case - [fp] -> pure fp - [] -> fail "Please provide exactly one argument (got none)" - xs -> fail $ "Please provide exactly one argument, got: \n" <> unlines xs + (fp:exts) <- getArgs >>= \case + args@(_:_) -> pure args + [] -> fail "Please provide at least one argument (got none)" -- Read the output of @--print-libdir@ for 'runGhc' (_,Just ho1, _, hdl) <- Process.createProcess @@ -44,18 +43,25 @@ main = do res <- GHC.runGhc (Just libdir) $ do - -- Without this line GHC parsing fails with the following error - -- message: + -- We allow passing some extra extensions to be parsed by GHC. + -- Otherwise modules that have e.g. @RankNTypes@ enabled will fail to + -- parse. Note: if anybody gets rid of this: even without this it /is/ + -- necessary to run getSessionFlags/setSessionFlags at least once, + -- otherwise GHC parsing fails with the following error message: -- : unknown package: rts - _ <- GHC.setSessionDynFlags =<< GHC.getSessionDynFlags + dflags0 <- GHC.getSessionDynFlags + (dflags1, _leftovers, _warns) <- + DynFlags.parseDynamicFlagsCmdLine dflags0 (map (SrcLoc.mkGeneralLocated "on the commandline") exts) + _ <- GHC.setSessionDynFlags dflags1 hsc_env <- GHC.getSession + -- XXX: We need to preprocess the file so that all extensions are -- loaded - (dflags, fp2) <- liftIO $ + (dflags2, fp2) <- liftIO $ DriverPipeline.preprocess hsc_env (fp, Nothing) - _ <- GHC.setSessionDynFlags dflags + _ <- GHC.setSessionDynFlags dflags2 -- Read the file that we want to parse str <- liftIO $ filterBOM <$> readFile fp2 @@ -72,7 +78,7 @@ main = do , show spn ] throwIO $ HscTypes.mkSrcErr $ - Bag.unitBag $ ErrUtils.mkPlainErrMsg dflags spn e + Bag.unitBag $ ErrUtils.mkPlainErrMsg dflags2 spn e -- Extract the imports from the parsed module let imports' = diff --git a/snack-lib/module-spec.nix b/snack-lib/module-spec.nix index aaf8074..aada6c5 100644 --- a/snack-lib/module-spec.nix +++ b/snack-lib/module-spec.nix @@ -47,7 +47,7 @@ rec { modImportsNames = modName: lib.lists.filter (modName': ! builtins.isNull (baseByModuleName modName')) - (listModuleImports baseByModuleName modName); + (listModuleImports baseByModuleName extsByModuleName modName); in # TODO: DFS instead of Fold { f = modName: diff --git a/snack-lib/modules.nix b/snack-lib/modules.nix index a2b62ea..cd6293f 100644 --- a/snack-lib/modules.nix +++ b/snack-lib/modules.nix @@ -31,9 +31,9 @@ rec { "${singleOut base (moduleToFile mod)}/${moduleToFile mod}"; # Generate a list of haskell module names needed by the haskell file - listModuleImports = baseByModuleName: modName: + listModuleImports = baseByModuleName: extsByModuleName: modName: builtins.fromJSON - (builtins.readFile (listAllModuleImportsJSON (baseByModuleName modName) modName)) + (builtins.readFile (listAllModuleImportsJSON baseByModuleName extsByModuleName modName)) ; # Whether the file is a Haskell module or not. It uses very simple @@ -51,8 +51,12 @@ rec { # Lists all module dependencies, not limited to modules existing in this # project - listAllModuleImportsJSON = base: modName: + listAllModuleImportsJSON = baseByModuleName: extsByModuleName: modName: let + base = baseByModuleName modName; + modExts = + lib.strings.escapeShellArgs + (map (x: "-X${x}") (extsByModuleName modName)); ghc = haskellPackages.ghcWithPackages (ps: [ ps.ghc ]); importParser = runCommand "import-parser" { buildInputs = [ ghc ]; @@ -65,6 +69,6 @@ rec { } '' - ${importParser} ${singleOutModulePath base modName} > $out + ${importParser} ${singleOutModulePath base modName} ${modExts} > $out ''; } From 4f310e07834acefa0e3429303cf690b8b2baaa17 Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Fri, 9 Nov 2018 12:20:53 -0500 Subject: [PATCH 38/43] Add a failing test that should pass when source-dirs is a list, #93. --- tests/source-dirs/app/Bar.hs | 7 +++++++ tests/source-dirs/app/Foo.hs | 4 ++++ tests/source-dirs/golden | 5 +++++ tests/source-dirs/package.nix | 4 ++++ tests/source-dirs/package.yaml | 11 +++++++++++ tests/source-dirs/src/FooBar.hs | 9 +++++++++ tests/source-dirs/test | 25 +++++++++++++++++++++++++ 7 files changed, 65 insertions(+) create mode 100644 tests/source-dirs/app/Bar.hs create mode 100644 tests/source-dirs/app/Foo.hs create mode 100644 tests/source-dirs/golden create mode 100644 tests/source-dirs/package.nix create mode 100644 tests/source-dirs/package.yaml create mode 100644 tests/source-dirs/src/FooBar.hs create mode 100755 tests/source-dirs/test diff --git a/tests/source-dirs/app/Bar.hs b/tests/source-dirs/app/Bar.hs new file mode 100644 index 0000000..d499526 --- /dev/null +++ b/tests/source-dirs/app/Bar.hs @@ -0,0 +1,7 @@ +module Bar where + +import Conduit +import FooBar + +main :: IO () +main = runConduit $ spitOut .| takeC 5 .| digest diff --git a/tests/source-dirs/app/Foo.hs b/tests/source-dirs/app/Foo.hs new file mode 100644 index 0000000..bfe5d3c --- /dev/null +++ b/tests/source-dirs/app/Foo.hs @@ -0,0 +1,4 @@ +import qualified Bar + +main :: IO () +main = Bar.main diff --git a/tests/source-dirs/golden b/tests/source-dirs/golden new file mode 100644 index 0000000..8a1218a --- /dev/null +++ b/tests/source-dirs/golden @@ -0,0 +1,5 @@ +1 +2 +3 +4 +5 diff --git a/tests/source-dirs/package.nix b/tests/source-dirs/package.nix new file mode 100644 index 0000000..1fda159 --- /dev/null +++ b/tests/source-dirs/package.nix @@ -0,0 +1,4 @@ +{ main = "Foo"; + src = [ ./src ./app ]; + dependencies = [ "conduit" ]; +} diff --git a/tests/source-dirs/package.yaml b/tests/source-dirs/package.yaml new file mode 100644 index 0000000..6a88cb4 --- /dev/null +++ b/tests/source-dirs/package.yaml @@ -0,0 +1,11 @@ +name: snack-source-dirs-test + +dependencies: + - base + - conduit + +executable: + main: Foo.hs + source-dirs: + - src + - app diff --git a/tests/source-dirs/src/FooBar.hs b/tests/source-dirs/src/FooBar.hs new file mode 100644 index 0000000..78abe00 --- /dev/null +++ b/tests/source-dirs/src/FooBar.hs @@ -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 diff --git a/tests/source-dirs/test b/tests/source-dirs/test new file mode 100755 index 0000000..fbf7191 --- /dev/null +++ b/tests/source-dirs/test @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +# vim: ft=sh sw=2 et + +set -euo pipefail + +test() { + $SNACK build + $SNACK run | diff golden - + + TMP_FILE=$(mktemp) + + capture_io "$TMP_FILE" main | $SNACK ghci + + diff golden $TMP_FILE + rm $TMP_FILE +} + + +SNACK="snack -j4" test + +# TODO: Fix cannot coerce a list to a string, at /...-snack-lib/files.nix:66:12 +SNACK="snack -j4 -s ./package.nix" test + +# TODO: Fix cannot coerce a list to a string, at /...-snack-lib/hpack.nix:60:37 +SNACK="snack -j4 --package-yaml ./package.yaml" test From fa7c2ea102af0e9db9ba7ba1195c522d5e867802 Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Fri, 9 Nov 2018 15:20:45 -0500 Subject: [PATCH 39/43] Bump nixpkgs to pickup hpack-dhall-0.5.0, #51. --- nix/nixpkgs/nixpkgs-src.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nix/nixpkgs/nixpkgs-src.json b/nix/nixpkgs/nixpkgs-src.json index 424a210..c3e8f34 100644 --- a/nix/nixpkgs/nixpkgs-src.json +++ b/nix/nixpkgs/nixpkgs-src.json @@ -1,6 +1,6 @@ { "owner": "NixOS", "repo": "nixpkgs", - "rev": "584270e39743020d8aa056b40eea00fcaa2b595f", - "sha256": "1p8hrz7xz73gzcqw62f3zfkmf3pkwwn1b41xzs7ayxafkmzsh0hw" + "rev": "814ff37bef0ed8ef923b7e155455588ab3c7d22d", + "sha256": "04x094gkw49v31yqyx9i8097nkrlyw0r29wsq7f9v5h8xvrlgivw" } From 3b3d419e457f33a06dbbd47a54d83f1a6a448665 Mon Sep 17 00:00:00 2001 From: Phil de Joux Date: Fri, 9 Nov 2018 15:21:18 -0500 Subject: [PATCH 40/43] Use dhall-hpack-json, untested WIP, #51. --- snack-lib/hpack.nix | 36 ++++++++++++++++-------------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/snack-lib/hpack.nix b/snack-lib/hpack.nix index e9e9ba9..c01a422 100644 --- a/snack-lib/hpack.nix +++ b/snack-lib/hpack.nix @@ -1,4 +1,4 @@ -{ lib, dhall-json, glibcLocales, callPackage, writeText, runCommand, haskellPackages }: +{ lib, glibcLocales, callPackage, writeText, runCommand, haskellPackages }: with (callPackage ./lib.nix {}); with (callPackage ./files.nix {}); @@ -25,9 +25,10 @@ let let json = builtins.readFile (runCommand "d2j" - { buildInputs = [ dhall-json ]; } - # hack: dhall-to-json gets a path and then imports the contents - "dhall-to-json <<< ${writeText "d2j" text} > $out" + { buildInputs = + [ (haskellPackages.ghcWithPackages (ps: [ ps.hpack-dhall ])) glibcLocales ]; + } + "dhall-hpack-json ${text} > $out" ); in builtins.fromJSON json; in @@ -35,23 +36,18 @@ in # Returns an attribute set with two fields: # - library: a package spec # - executable: an attr set of executable name to package spec - pkgDescrsFromHPack = packageYaml: + pkgDescrsFromHPack = pkgHpackSrc: let package = let - ext = fileExtension packageYaml; - fromFile = - if ext == null - then abort "File ${packageYaml} has no extension!" - else if ext == "yaml" - then fromYAML - else if ext == "yml" - then fromYAML - else if ext == "dhall" - then fromDhall - else - abort "File ${packageYaml} has an unknown extension (${ext})!"; - in fromFile (builtins.readFile packageYaml); + ext = fileExtension pkgHpackSrc; + contents = builtins.readFile pkgHpackSrc; + in + if ext == null then abort "File ${pkgHpackSrc} has no extension!" + else if ext == "yaml" then fromYAML contents + else if ext == "yml" then fromYAML contents + else if ext == "dhall" then fromDhall pkgHpackSrc + else abort "File ${pkgHpackSrc} has an unknown extension (${ext})!"; # Snack drops the version bounds because here it has no meaning dropVersionBounds = @@ -61,7 +57,7 @@ in topExtensions = optAttr package "default-extensions" []; packageLib = withAttr package "library" null (component: { src = - let base = builtins.dirOf packageYaml; + let base = builtins.dirOf pkgHpackSrc; in builtins.toPath "${builtins.toString base}/${component.source-dirs}"; dependencies = topDeps ++ mkDeps component; extensions = topExtensions ++ (optAttr component "extensions" []); @@ -81,7 +77,7 @@ in { main = fileToModule component.main; src = let - base = builtins.dirOf packageYaml; + base = builtins.dirOf pkgHpackSrc; in builtins.toPath "${builtins.toString base}/${component.source-dirs}"; dependencies = topDeps ++ dropVersionBounds depOrPack.wrong; extensions = topExtensions ++ (optAttr component "extensions" []); From 1337e520947f04e0e5b534e7a3949b0fcf9a9e55 Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Sun, 18 Nov 2018 13:02:10 +0400 Subject: [PATCH 41/43] Clean up files.nix --- snack-lib/files.nix | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/snack-lib/files.nix b/snack-lib/files.nix index 4e4f377..4653af0 100644 --- a/snack-lib/files.nix +++ b/snack-lib/files.nix @@ -10,16 +10,17 @@ rec { singleOut = base: file: let basePrefix = (builtins.toString base) + "/"; - pred = file: path: type: + pred = path: type: let actual = lib.strings.removePrefix basePrefix path; expected = file; in (expected == actual) || (type == "directory" && (lib.strings.hasPrefix actual expected)); - # TODO: even though we're doing a lot of cleaning, there's sitll some + # TODO: even though we're doing a lot of cleaning, there's still some # 'does-file-exist' happening - src0 = lib.cleanSource base; + src = lib.cleanSourceWith + { filter = pred; src = lib.cleanSource base; }; name = # Makes the file name derivation friendly lib.stringAsChars (x: @@ -30,8 +31,7 @@ rec { ) file; in stdenv.mkDerivation { - inherit name; - src = lib.cleanSourceWith { filter = (pred file); src = src0; }; + inherit name src; builder = writeScript (name + "-single-out") # TODO: make sure the file actually exists and that there's only one '' From ed7f604ecf6c2ddf4f37ef41ccee5ced7a5b3294 Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Sun, 18 Nov 2018 13:59:53 +0400 Subject: [PATCH 42/43] Allow multiple source directories --- snack-lib/default.nix | 2 +- snack-lib/hpack.nix | 19 +++++++++++++++---- snack-lib/module-spec.nix | 4 ++-- snack-lib/package-spec.nix | 34 ++++++++++++++++++++++++---------- 4 files changed, 42 insertions(+), 17 deletions(-) diff --git a/snack-lib/default.nix b/snack-lib/default.nix index fe7ec86..fc20899 100644 --- a/snack-lib/default.nix +++ b/snack-lib/default.nix @@ -40,7 +40,7 @@ let libraryModSpecs = pkgSpec: let moduleSpecFold' = modSpecFoldFromPackageSpec pkgSpec; - modNames = listModulesInDir pkgSpec.packageBase; + modNames = pkgs.lib.concatMap listModulesInDir pkgSpec.packageSourceDirs; fld = moduleSpecFold' modSpecs'; modSpecs' = foldDAG fld modNames; modSpecs = builtins.attrValues modSpecs'; diff --git a/snack-lib/hpack.nix b/snack-lib/hpack.nix index fa62816..9c1811f 100644 --- a/snack-lib/hpack.nix +++ b/snack-lib/hpack.nix @@ -37,7 +37,13 @@ in packageLib = withAttr package "library" null (component: { src = let base = builtins.dirOf packageYaml; - in builtins.toPath "${builtins.toString base}/${component.source-dirs}"; + in + if builtins.isList component.source-dirs + then builtins.map (sourceDir: + builtins.toPath "${builtins.toString base}/${sourceDir}" + ) component.source-dirs + else + builtins.toPath "${builtins.toString base}/${component.source-dirs}"; dependencies = topDeps ++ mkDeps component; extensions = topExtensions ++ (optAttr component "extensions" []); } @@ -55,9 +61,14 @@ in in { main = fileToModule component.main; src = - let - base = builtins.dirOf packageYaml; - in builtins.toPath "${builtins.toString base}/${component.source-dirs}"; + let base = builtins.dirOf packageYaml; + in + if builtins.isList component.source-dirs + then builtins.map (sourceDir: + builtins.toPath "${builtins.toString base}/${sourceDir}" + ) component.source-dirs + else + builtins.toPath "${builtins.toString base}/${component.source-dirs}"; dependencies = topDeps ++ dropVersionBounds depOrPack.wrong; extensions = topExtensions ++ (optAttr component "extensions" []); packages = map (_: packageLib) depOrPack.right; diff --git a/snack-lib/module-spec.nix b/snack-lib/module-spec.nix index aada6c5..b12b543 100644 --- a/snack-lib/module-spec.nix +++ b/snack-lib/module-spec.nix @@ -100,8 +100,8 @@ rec { modSpecFoldFromPackageSpec = pkgSpec: let baseByModuleName = modName: - let res = pkgSpecByModuleName pkgSpec null modName; - in if res == null then null else res.packageBase; + let res = pkgSpecAndBaseByModuleName pkgSpec modName; + in if res == null then null else res.base; depsByModuleName = modName: (pkgSpecByModuleName pkgSpec diff --git a/snack-lib/package-spec.nix b/snack-lib/package-spec.nix index 22f6d89..adb269f 100644 --- a/snack-lib/package-spec.nix +++ b/snack-lib/package-spec.nix @@ -18,7 +18,10 @@ rec { , packages ? [] }: { packageMain = main; - packageBase = src; + packageSourceDirs = + if builtins.isList src + then src + else [src]; packageGhcOpts = ghcOpts; packageExtensions = extensions; packageDependencies = mkPerModuleAttr dependencies; @@ -45,14 +48,25 @@ rec { # 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 + pkgSpecAndBaseByModuleName = topPkgSpec: modName: + let + foo = pkgSpec: + lib.findFirst + (base: lib.lists.elem modName (listModulesInDir base)) + null + pkgSpec.packageSourceDirs; + bar = lib.concatMap (pkgSpec: - lib.lists.elem - modName - (listModulesInDir pkgSpec.packageBase) - ) - def - (flattenPackages topPkgSpec) - ); + let base = foo pkgSpec; + in if base == null then [] else [ { inherit pkgSpec base; } ]) + (flattenPackages topPkgSpec); + in if lib.length bar <= 0 then null else + if lib.length bar == 1 then lib.head bar + else abort + "Refusing to return base, module name was found more than once: ${modName}"; + + pkgSpecByModuleName = topPkgSpec: def: modName: + let + res = pkgSpecAndBaseByModuleName topPkgSpec modName; + in if res == null then def else res.pkgSpec; } From 1b8f83460138e59cf339a6d6a9d5b5505de71b6c Mon Sep 17 00:00:00 2001 From: Nicolas Mattia Date: Sun, 18 Nov 2018 14:00:38 +0400 Subject: [PATCH 43/43] changelog: update --- CHANGELOG.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1821a5a..fbba314 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,14 +9,15 @@ Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to ### Added - This CHANGELOG file to track changes to the command line and library APIs. -- Support for custom GHC version, `ghcWithPackages` and `pkgs` +- Support for custom GHC version, `ghcWithPackages` and `pkgs`. +- Support for more than one directory in `src` (and HPack's `source-dirs`). ### Changed - The `snack run` function to accept arguments that will be passed to the built executable. - The `snack.nix` now describes the build environment and packages are described through `package.nix` (i.e. to migrate: rename `snack.nix` to - `package.nix`) + `package.nix`). ### Fixed - The module import parsing when the CPP extension is enabled.