diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..fbba314 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,27 @@ +# 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. +- 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`). + +### Fixed +- The module import parsing when the CPP extension is enabled. +- 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 diff --git a/README.md b/README.md index 63d5cd7..6f374a8 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,9 +62,17 @@ 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: +``` shell +$ ./script/install +``` + +which is equivalent to + ``` shell $ nix-env -f ./default.nix -iA snack-exe ``` @@ -71,21 +81,38 @@ 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] [-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 + 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: 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 +**test** command as we treat test suites as we do executables, giving each test +suite its own package description. + ## 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). +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 @@ -206,23 +233,109 @@ 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, 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 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). +## 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/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... +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/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 ) +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 05630d9..39b1e1e 100644 --- a/bin/Snack.hs +++ b/bin/Snack.hs @@ -5,9 +5,12 @@ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE QuasiQuotes #-} +{-# LANGUAGE KindSignatures #-} {-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE TypeFamilies #-} -module Main where +module Main (main) where import Control.Applicative import Control.Monad @@ -18,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 @@ -29,43 +32,150 @@ 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 +--- +--- 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 PackageNix) -- Reads a package.nix file + | HPack (Config c FilePath PackageYaml) -- Reads a package.yaml + +prepareMode :: ModeRaw -> IO Mode +prepareMode = \case + 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 } -newtype PackageYaml = PackageYaml { unPackageYaml :: FilePath } +mkPackageNix :: FilePath -> IO PackageNix +mkPackageNix = fmap PackageNix . canonicalizePath + +-- | Like a FilePath, but Nix friendly +newtype SnackNix = SnackNix FilePath mkSnackNix :: FilePath -> IO SnackNix -mkSnackNix = fmap SnackNix . makeAbsolute +mkSnackNix = fmap SnackNix . canonicalizePath + +-- | Like a FilePath, but Nix friendly +newtype SnackLib = SnackLib FilePath + +mkSnackLib :: FilePath -> IO SnackLib +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 + { nixNCores :: Int } + +type SnackConfig = SnackConfig_ 'ConfigReady +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 + } data Command = Build - | Run + | Run [String] -- Run with extra args | Ghci + | Test main :: IO () main = do - opts <- Opts.execParser (Opts.info (options <**> Opts.helper) mempty) - runCommand (mode opts) (command opts) + opts <- + prepareOptions =<< + Opts.execParser (Opts.info (parseOptions <**> Opts.helper) mempty) + runCommand (snackConfig opts) (mode opts) (command opts) -data Options = Options - { mode :: Mode +type OptionsRaw = Options_ 'ConfigRaw +type Options = Options_ 'ConfigReady + +data Options_ c = Options + { snackConfig :: SnackConfig_ c + , mode :: Mode_ c , command :: Command } -parseMode :: Opts.Parser Mode +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 + ) <*> + forM (snackNix raw) mkSnackNix <*> + pure (snackNixCfg raw) + +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") + ) + +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." + ] + ) + ) + ) <*> + Opts.optional ( + Opts.strOption + (Opts.long "snack-nix" + <> Opts.short 'b' + <> Opts.metavar "PATH") + ) <*> + parseNixConfig + +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") ) <|> @@ -77,10 +187,14 @@ parseMode = <> Opts.metavar "PATH") ) -options :: Opts.Parser Options -options = Options <$> parseMode <*> parseCommand +parseOptions :: Opts.Parser OptionsRaw +parseOptions = + Options <$> + parseSnackConfig <*> + parseMode <*> + parseCommand -newtype ModuleName = ModuleName { unModuleName :: T.Text } +newtype ModuleName = ModuleName T.Text deriving newtype (Ord, Eq, Aeson.FromJSONKey) deriving stock Show @@ -115,13 +229,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 @@ -129,7 +241,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 @@ -152,7 +264,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 @@ -162,38 +274,59 @@ 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 :: SnackConfig -> [NixArg] -> NixExpr -> Sh NixPath +nixBuild snackCfg extraNixArgs nixExpr = NixPath <$> runStdin1 (T.pack [i| { #{ intercalate "," funArgs } }: let spec = builtins.fromJSON specJson; - pkgs = import (builtins.fetchTarball - { 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 - ''; - snack = pkgs.callPackage libDir {}; + config = #{ pkgsSrc }; + pkgs = config.pkgs; + libDir = #{ libDir }; + snack = (import libDir) config; in #{ T.unpack $ unNixExpr $ nixExpr } |]) "nix-build" cliArgs where + pkgsSrc :: String + pkgsSrc = case snackNix snackCfg of + Just (SnackNix fp) -> + [i|(import #{ fp })|] + Nothing -> + [i| + { 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 + 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 , "--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 @@ -208,36 +341,37 @@ nixBuild extraNixArgs nixExpr = toCliArgs narg = case argType narg of { Arg -> "--arg"; ArgStr -> "--argstr" } : [ argName narg , argValue narg ] + nixCfg = snackNixCfg snackCfg -snackBuild :: SnackNix -> Sh BuildResult -snackBuild snackNix = do - NixPath out <- nixBuild +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 :: SnackNix -> Sh GhciBuild -snackGhci snackNix = do - NixPath out <- nixBuild +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 -snackBuildHPack :: PackageYaml -> Sh BuildResult -snackBuildHPack packageYaml = do - NixPath out <- nixBuild +snackBuildHPack :: SnackConfig -> PackageYaml -> Sh BuildResult +snackBuildHPack snackCfg packageYaml = do + NixPath out <- nixBuild snackCfg [ NixArg { argName = "packageYaml" , argValue = T.pack $ unPackageYaml packageYaml @@ -247,9 +381,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 :: SnackConfig -> PackageYaml -> Sh GhciBuild +snackGhciHPack snackCfg packageYaml = do + NixPath out <- nixBuild snackCfg [ NixArg { argName = "packageYaml" , argValue = T.pack $ unPackageYaml packageYaml @@ -261,39 +395,51 @@ 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 - Ghci -> - runExe =<< ghciExePath <$> (mkSnackNix snackNix >>= quiet . snackGhci) -runCommand (HPack packageYaml) = \case - Build -> mkPackageYaml packageYaml >>= S.shelly . void . snackBuildHPack - Run -> - mkPackageYaml packageYaml >>= quiet . snackBuildHPack >>= runBuildResult - Ghci -> - runExe =<< - ghciExePath <$> (mkPackageYaml packageYaml >>= quiet . snackGhciHPack) - -runBuildResult :: BuildResult -> IO () -runBuildResult = \case - BuiltExecutable (ExecutableBuild p) -> runExe p +runCommand :: SnackConfig -> Mode -> Command -> IO () +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 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 + 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.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 (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 @@ -305,9 +451,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/bin/snack.nix b/bin/package.nix similarity index 100% rename from bin/snack.nix rename to bin/package.nix 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" } 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/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/Imports.hs b/snack-lib/Imports.hs index 82f4e4e..b8e7a16 100644 --- a/snack-lib/Imports.hs +++ b/snack-lib/Imports.hs @@ -1,13 +1,19 @@ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE MagicHash #-} +module Main (main) where + import Control.Monad.IO.Class +import Data.List (stripPrefix) 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,13 +25,13 @@ import qualified Parser import qualified SrcLoc import qualified StringBuffer import qualified System.Process as Process +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 @@ -33,26 +39,46 @@ 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 + -- 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 + 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, _) <- liftIO $ DriverPipeline.preprocess hsc_env (fp, Nothing) - hsc_env <- GHC.setSession hsc_env { HscTypes.hsc_dflags = dflags } + (dflags2, fp2) <- liftIO $ + DriverPipeline.preprocess hsc_env (fp, Nothing) + _ <- GHC.setSessionDynFlags dflags2 + + -- Read the file that we want to parse + str <- liftIO $ filterBOM <$> 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 dflags2 spn e -- Extract the imports from the parsed module let imports' = @@ -63,6 +89,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 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 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; diff --git a/snack-lib/default.nix b/snack-lib/default.nix index 1947389..fc20899 100644 --- a/snack-lib/default.nix +++ b/snack-lib/default.nix @@ -1,18 +1,13 @@ # 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 +, ghc-version ? "ghc822" +, ghcWithPackages ? pkgs.haskell.packages.${ghc-version}.ghcWithPackages }: +with pkgs; + with (callPackage ./build.nix {}); with (callPackage ./files.nix {}); with (callPackage ./ghci.nix {}); @@ -23,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 @@ -45,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'; @@ -72,26 +67,12 @@ 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 = 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) @@ -123,7 +104,7 @@ let ( builtins.toJSON ( let pkgSpecs = hpackSpecs packageYaml; - pkgSpec = mkPackageSpec (import snackNix); + pkgSpec = mkPackageSpec (import packageNix); drv = let exeSpecs = builtins.attrValues pkgSpecs.executables; in @@ -141,7 +122,7 @@ let } )); - snackSpec = snackNix: mkPackageSpec (import snackNix); + snackSpec = packageNix: mkPackageSpec (import packageNix); hpackSpecs = packageYaml: let descrs = pkgDescrsFromHPack packageYaml; @@ -151,6 +132,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 +161,6 @@ in buildAsLibrary snackSpec hpackSpec + mkPackage ; } diff --git a/snack-lib/files.nix b/snack-lib/files.nix index b6ff048..a67920e 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 '' diff --git a/snack-lib/hpack.nix b/snack-lib/hpack.nix index e9e9ba9..efa8ed3 100644 --- a/snack-lib/hpack.nix +++ b/snack-lib/hpack.nix @@ -1,7 +1,6 @@ -{ lib, dhall-json, glibcLocales, callPackage, writeText, runCommand, haskellPackages }: +{ lib, glibcLocales, callPackage, writeText, runCommand, haskellPackages }: with (callPackage ./lib.nix {}); -with (callPackage ./files.nix {}); with (callPackage ./modules.nix {}); let @@ -25,9 +24,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 +35,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,8 +56,14 @@ in topExtensions = optAttr package "default-extensions" []; packageLib = withAttr package "library" null (component: { src = - let base = builtins.dirOf packageYaml; - in builtins.toPath "${builtins.toString base}/${component.source-dirs}"; + let base = builtins.dirOf pkgHpackSrc; + 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" []); } @@ -80,9 +81,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 pkgHpackSrc; + 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 275450d..b12b543 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: @@ -73,40 +73,35 @@ 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: + lib.lists.unique + ( + foldDAG + { f = modSpec: + lib.lists.foldl + (x: y: 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: 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/modules.nix b/snack-lib/modules.nix index 1e3b8ad..cd6293f 100644 --- a/snack-lib/modules.nix +++ b/snack-lib/modules.nix @@ -31,15 +31,15 @@ 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 # 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 @@ -51,12 +51,16 @@ 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 ]; - } "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" @@ -65,6 +69,6 @@ rec { } '' - ${importParser} ${singleOutModulePath base modName} > $out + ${importParser} ${singleOutModulePath base modName} ${modExts} > $out ''; } 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; } 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 9f3f180..a2e5fcd 100755 --- a/tests/any-paths/test +++ b/tests/any-paths/test @@ -8,5 +8,7 @@ test() { $SNACK run } -SNACK="snack -s snack.nix" test -SNACK="snack --package-yaml package.yaml" test +SNACK="snack -j4" 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-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 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/magichash/snack.nix b/tests/cpp/package.nix similarity index 100% rename from tests/magichash/snack.nix rename to tests/cpp/package.nix 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 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..12a09a1 --- /dev/null +++ b/tests/extended-config/nix/default.nix @@ -0,0 +1,23 @@ +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..7ac1785 --- /dev/null +++ b/tests/extended-config/snack.nix @@ -0,0 +1,4 @@ +rec { + pkgs = import ./nix; + ghcWithPackages = pkgs.haskellPackages.ghcWithPackages; +} 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..8fc298d --- /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 --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 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 7ff56f4..62b4e14 100755 --- a/tests/extensions/test +++ b/tests/extensions/test @@ -15,6 +15,7 @@ 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 ./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 7ad1251..fbb5104 100755 --- a/tests/hpack/test +++ b/tests/hpack/test @@ -5,9 +5,9 @@ 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 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/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 05615b7..66914ad 100755 --- a/tests/library-2/test +++ b/tests/library-2/test @@ -15,6 +15,7 @@ test() { rm $TMP_FILE } -SNACK="snack" test -SNACK="snack -s ./snack.nix" test +SNACK="snack -j4" 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 05615b7..2d4bfe9 100755 --- a/tests/library-3/test +++ b/tests/library-3/test @@ -16,5 +16,6 @@ test() { } SNACK="snack" test -SNACK="snack -s ./snack.nix" 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 7ff56f4..ebfd06d 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 ./package.nix" test +SNACK="snack -j4 --package-yaml ./package.yaml" test diff --git a/tests/utf-8/snack.nix b/tests/magichash/package.nix similarity index 100% rename from tests/utf-8/snack.nix rename to tests/magichash/package.nix diff --git a/tests/magichash/test b/tests/magichash/test index 7ff56f4..ebfd06d 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 ./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 e823825..0f7c742 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 ./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 e823825..0f7c742 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 ./package.nix" test +SNACK="snack -j4 --package-yaml ./package.yaml" test 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/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 3269970..76b6b2c 100755 --- a/tests/readme/test +++ b/tests/readme/test @@ -8,7 +8,9 @@ test() { $SNACK run } -SNACK="snack" test -SNACK="snack -s ./snack.nix" test -SNACK="snack --package-yaml ./package.yaml" test -SNACK="snack --package-yaml ./package.dhall" test +SNACK="snack -j4" 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 +SNACK="snack -j4 --package-yaml ./package.dhall" test +SNACK="snack -j4 --package-yaml ./package-import.dhall" test 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/package.nix b/tests/run-args/package.nix new file mode 100644 index 0000000..c1aac2a --- /dev/null +++ b/tests/run-args/package.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 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 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 7ff56f4..ebfd06d 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 ./package.nix" test +SNACK="snack -j4 --package-yaml ./package.yaml" test 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/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 0000000..61668ba Binary files /dev/null and b/tests/swp/src/Foo/.Bar.hs.swp differ 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 diff --git a/tests/swp/test b/tests/swp/test new file mode 100755 index 0000000..cfefea5 --- /dev/null +++ b/tests/swp/test @@ -0,0 +1,14 @@ +#!/usr/bin/env bash +# vim: ft=sh sw=2 et + +set -euo pipefail + +test() { + TMP_FILE=$(mktemp) + + cat $($SNACK build) | jq -M '.result | keys' > $TMP_FILE + + diff golden.jq $TMP_FILE +} + +SNACK="snack -j4" test diff --git a/tests/template-haskell-2/code/snack.nix b/tests/template-haskell-2/code/package.nix similarity index 59% rename from tests/template-haskell-2/code/snack.nix rename to tests/template-haskell-2/code/package.nix index 292fe3a..354b573 100644 --- a/tests/template-haskell-2/code/snack.nix +++ b/tests/template-haskell-2/code/package.nix @@ -2,5 +2,5 @@ src = ./.; dependencies = ["file-embed"]; extra-directories = - (modName: if modName == "Main" then [ ../. ] else []); + { Main = [../.]; }; } diff --git a/tests/template-haskell-2/test b/tests/template-haskell-2/test index 233dd86..cbebe94 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/package.nix +snack -j4 run -s code/package.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/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-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/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-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/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/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/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 3df8b70..ca79687 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 ./package.nix" test # Note: no HPack test, because HPack doesn't support multi library 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.nix b/tests/utf-8-BOM/package.nix new file mode 100644 index 0000000..3b4022a --- /dev/null +++ b/tests/utf-8-BOM/package.nix @@ -0,0 +1,3 @@ +{ main = "Main"; + src = ./.; +} 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/test b/tests/utf-8-BOM/test new file mode 100755 index 0000000..a301102 --- /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 ./package.nix" test +SNACK="snack --package-yaml ./package.yaml" test diff --git a/tests/utf-8/package.nix b/tests/utf-8/package.nix new file mode 100644 index 0000000..3b4022a --- /dev/null +++ b/tests/utf-8/package.nix @@ -0,0 +1,3 @@ +{ main = "Main"; + src = ./.; +} 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