stack2nix (empty) → 0.1.1.0
raw patch · 11 files changed
+629/−0 lines, 11 filesdep +Cabaldep +Globdep +SafeSemaphoresetup-changed
Dependencies added: Cabal, Glob, SafeSemaphore, async, base, bytestring, containers, data-fix, directory, filepath, hnix, monad-parallel, optparse-applicative, process, stack2nix, temporary, text, yaml
Files
- LICENSE +19/−0
- README.md +59/−0
- Setup.hs +2/−0
- src/Stack2nix.hs +362/−0
- src/Stack2nix/External.hs +7/−0
- src/Stack2nix/External/Cabal2nix.hs +43/−0
- src/Stack2nix/External/Util.hs +27/−0
- src/Stack2nix/External/VCS.hs +5/−0
- src/Stack2nix/External/VCS/Git.hs +29/−0
- stack2nix.cabal +54/−0
- stack2nix/Main.hs +22/−0
+ LICENSE view
@@ -0,0 +1,19 @@+Copyright (c) 2017 IOHK++Permission is hereby granted, free of charge, to any person obtaining a copy of+this software and associated documentation files (the "Software"), to deal in+the Software without restriction, including without limitation the rights to+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies+of the Software, and to permit persons to whom the Software is furnished to+do so, subject to the following conditions:++The above copyright notice and this permission notice shall be included in+all copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN+THE SOFTWARE.
+ README.md view
@@ -0,0 +1,59 @@+# stack2nix++[](https://travis-ci.org/input-output-hk/stack2nix)++## About++`stack2nix` automates conversion from [Stack](https://docs.haskellstack.org/en/stable/README/) configuration file to [Nix](http://nixos.org/nix/) expressions.++`stack2nix` high-level workflow:++- invoke `stack list-dependencies` to determine complete fixed version list of packages based on resolver+- apply any additional configuration (local packages, extra dependencies, etc) from `stack.yaml`+- generate complete list of dependencies to Nix expressions, replacing upstream `hackage-packages.nix`++## Installation++There are two options. The first is to install stack2nix and its dependencies on your machine directly, and the second is to use the supported virtual machine configuration.++If there are difficulties please file an issue. Generally the virtual machine approach should be more reliable.++### Native Environment++1. Install [nix](https://nixos.org/nix/).+2. Clone this repo.+3. Run `stack install` to install.+4. Ensure `cabal2nix` v2.2.1 or higher and `stack2nix` are in your `$PATH`.++### Virtual Machine++1. Install [VirtualBox](https://www.virtualbox.org/wiki/VirtualBox) and [Vagrant](https://www.vagrantup.com/).+2. Clone this repo.+3. Run `./scripts/vagrant.sh` and take a coffee break.+4. If there are no errors, log into the VM: `vagrant ssh`.++## Usage++Nix expressions generated by stack2nix depend on a somewhat recent upstream change to nixpkgs. If `nix-env` or `nix-build` fails with an error mentioning `initialPackages`, try setting the `NIX_PATH` environment variable: `NIX_PATH=nixpkgs=https://github.com/NixOS/nixpkgs/archive/21a8239452adae3a4717772f4e490575586b2755.tar.gz`.++### Remote Packages++Stack2nix can generate a nix expressions for Haskell packages hosted in git repositories.++```+ $ stack2nix --revision 242e2a064f6a32b22e1599bbfe72e64d7b6203b8 https://github.com/jgm/pandoc.git > demo.nix+ $ nix-build -A pandoc demo.nix+```++### Local Packages++Sometimes it's convenient to build local Haskell packages. Assuming the current directory is a locally maintained fork of Pandoc:++```+ $ stack2nix . > default.nix+ $ nix-build -A pandoc+```++## Testing++Run `./scripts/travis.sh` to build and test.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ src/Stack2nix.hs view
@@ -0,0 +1,362 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}++module Stack2nix+ ( Args(..)+ , Package(..)+ , RemotePkgConf(..)+ , StackConfig(..)+ , parseStackYaml+ , stack2nix+ , version+ ) where++import Control.Concurrent.Async+import Control.Concurrent.MSem+import Control.Exception (SomeException, catch,+ onException)+import Control.Monad (unless)+import qualified Data.ByteString as BS+import Data.Fix (Fix (..))+import Data.List (foldl', sort, union, (\\))+import qualified Data.Map.Strict as Map+import Data.Maybe (fromMaybe, listToMaybe)+import Data.Monoid ((<>))+import Data.Text (Text, pack, unpack)+import qualified Data.Traversable as T+import Data.Version (Version (..), parseVersion,+ showVersion)+import Data.Yaml (FromJSON (..), (.!=), (.:),+ (.:?))+import qualified Data.Yaml as Y+import Distribution.Text (display)+import Nix.Expr (Binding (..), NExpr, NExprF (..),+ NKeyName (..), ParamSet (..),+ Params (..))+import Nix.Parser (Result (..), parseNixFile,+ parseNixString)+import Nix.Pretty (prettyNix)+import Paths_stack2nix (version)+import Stack2nix.External (cabal2nix)+import Stack2nix.External.Util (runCmd, runCmdFrom)+import Stack2nix.External.VCS.Git (Command (..), ExternalCmd (..),+ InternalCmd (..), git)+import System.Directory (doesFileExist)+import System.Environment (getEnv)+import System.FilePath (dropExtension, isAbsolute,+ normalise, takeDirectory,+ takeFileName, (</>))+import System.FilePath.Glob (glob)+import System.IO (hPutStrLn, stderr, stdout)+import System.IO.Temp (withSystemTempDirectory)+import Text.ParserCombinators.ReadP (readP_to_S)++data Args = Args+ { argRev :: Maybe String+ , argOutFile :: Maybe FilePath+ , argUri :: String+ }+ deriving (Show)++data StackConfig = StackConfig+ { resolver :: Text+ , packages :: [Package]+ , extraDeps :: [Text]+ }+ deriving (Show, Eq)++data Package = LocalPkg FilePath+ | RemotePkg RemotePkgConf+ deriving (Show, Eq)++data RemotePkgConf = RemotePkgConf+ { gitUrl :: Text+ , commit :: Text+ , extraDep :: Bool+ }+ deriving (Show, Eq)++instance FromJSON StackConfig where+ parseJSON (Y.Object v) =+ StackConfig <$>+ v .: "resolver" <*>+ v .: "packages" <*>+ v .: "extra-deps"+ parseJSON _ = fail "Expected Object for StackConfig value"++instance FromJSON Package where+ parseJSON (Y.String v) = return $ LocalPkg $ unpack v+ parseJSON obj@(Y.Object _) = RemotePkg <$> parseJSON obj+ parseJSON _ = fail "Expected String or Object for Package value"++instance FromJSON RemotePkgConf where+ parseJSON (Y.Object v) = do+ loc <- v .: "location"+ gitUrl <- loc .: "git"+ commit <- loc .: "commit"+ extra <- v .:? "extra-dep" .!= False+ return $ RemotePkgConf gitUrl commit extra+ parseJSON _ = fail "Expected Object for RemotePkgConf value"++parseStackYaml :: BS.ByteString -> Maybe StackConfig+parseStackYaml = Y.decode++checkRuntimeDeps :: IO ()+checkRuntimeDeps = do+ checkVer "cabal2nix" "2.2.1"+ checkVer "git" "2"+ checkVer "cabal" "1"+ where+ checkVer prog minVer = do+ hPutStrLn stderr $ unwords ["Ensuring", prog, "version is >=", minVer, "..."]+ result <- runCmd prog ["--version"] `onException` (error $ "Failed to run " ++ prog ++ ". Not found in PATH.")+ case result of+ Right out ->+ let+ -- heuristic for parsing version from stdout+ firstLine = head . lines+ lastWord = head . reverse . words+ ver = parseVer . lastWord . firstLine $ out+ in+ unless (ver >= parseVer minVer) $ error $ unwords ["ERROR:", prog, "version must be", minVer, "or higher. Current version:", maybe "[parse failure]" showVersion ver]+ Left err -> error err++ parseVer :: String -> Maybe Version+ parseVer =+ fmap fst . listToMaybe . reverse . readP_to_S parseVersion++stack2nix :: Args -> IO ()+stack2nix args@Args{..} = do+ checkRuntimeDeps+ updateCabalPackageIndex+ isLocalRepo <- doesFileExist $ argUri </> "stack.yaml"+ if isLocalRepo+ then handleStackConfig Nothing argUri+ else withSystemTempDirectory "s2n-" $ \tmpDir ->+ tryGit tmpDir >> handleStackConfig (Just argUri) tmpDir+ where+ updateCabalPackageIndex :: IO ()+ updateCabalPackageIndex =+ getEnv "HOME" >>= \home -> runCmdFrom home "cabal" ["update"] >> return ()++ tryGit :: FilePath -> IO ()+ tryGit tmpDir = do+ git $ OutsideRepo $ Clone argUri tmpDir+ case argRev of+ Just r -> git $ InsideRepo tmpDir $ Checkout r+ Nothing -> return mempty++ handleStackConfig :: Maybe String -> FilePath -> IO ()+ handleStackConfig remoteUri localDir = do+ let fname = localDir </> "stack.yaml"+ alreadyExists <- doesFileExist fname+ unless alreadyExists $ runCmdFrom localDir "stack" ["init", "--system-ghc"]+ >> return ()+ contents <- BS.readFile fname+ case parseStackYaml contents of+ Just config -> toNix args remoteUri localDir config+ Nothing -> error $ "Failed to parse " <> (localDir </> "stack.yaml")++-- Credit: https://stackoverflow.com/a/18898822/204305+mapPool :: T.Traversable t => Int -> (a -> IO b) -> t a -> IO (t b)+mapPool max' f xs = do+ sem <- new max'+ mapConcurrently (with sem . f) xs++c2nPoolSize :: Int+c2nPoolSize = 4++toNix :: Args -> Maybe String -> FilePath -> StackConfig -> IO ()+toNix Args{..} remoteUri baseDir StackConfig{..} =+ withSystemTempDirectory "s2n" $ \outDir -> do+ _ <- mapPool c2nPoolSize (genNixFile outDir) packages+ overrides <- mapPool c2nPoolSize overrideFor =<< updateDeps outDir+ _ <- mapPool c2nPoolSize (genNixFile outDir) packages+ _ <- mapPool c2nPoolSize patchNixFile =<< glob (outDir </> "*.nix")+ writeFile (outDir </> "initialPackages.nix") $ initialPackages $ sort overrides+ pullInNixFiles $ outDir </> "initialPackages.nix"+ nf <- parseNixFile $ outDir </> "initialPackages.nix"+ case nf of+ Success expr ->+ case argOutFile of+ Just fname -> writeFile fname $ defaultNix expr+ Nothing -> hPutStrLn stdout $ defaultNix expr+ _ -> error "failed to parse intermediary initialPackages.nix file"+ where+ updateDeps :: FilePath -> IO [FilePath]+ updateDeps outDir = do+ hPutStrLn stderr $ "Updating deps from " ++ baseDir+ result <- runCmdFrom baseDir "stack" ["list-dependencies", "--system-ghc", "--separator", "-"]+ case result of+ Right pkgs -> do+ let pkgs' = ["hscolour", "jailbreak-cabal", "cabal-doctest", "happy", "stringbuilder"] ++ lines pkgs+ hPutStrLn stderr "Haskell dependencies:"+ mapM_ (hPutStrLn stderr) pkgs'+ _ <- mapPool c2nPoolSize (\d -> catch (handleExtraDep outDir d) ignoreError) $ pack <$> pkgs'+ return ()+ Left err -> error $ unlines ["FAILED: stack list-dependencies", err]+ glob (outDir </> "*.nix")++ -- TODO: Remove this.+ ignoreError :: SomeException -> IO ()+ ignoreError _ = return ()++ genNixFile :: FilePath -> Package -> IO ()+ genNixFile outDir (LocalPkg relPath) =+ cabal2nix (fromMaybe baseDir remoteUri) (pack <$> argRev) (Just relPath) (Just outDir)+ genNixFile outDir (RemotePkg RemotePkgConf{..}) =+ cabal2nix (unpack gitUrl) (Just commit) Nothing (Just outDir)++ patchNixFile :: FilePath -> IO ()+ patchNixFile fname = do+ contents <- readFile fname+ case parseNixString contents of+ Success expr ->+ case takeFileName fname of+ "hspec.nix" -> do+ writeFile fname $ show $ prettyNix $ (addParam "stringbuilder" . stripNonEssentialDeps) expr+ _ -> writeFile fname $ show $ prettyNix $ stripNonEssentialDeps expr+ _ -> error "failed to parse intermediary nix package file"++ addParam :: String -> NExpr -> NExpr+ addParam param expr =+ let contents = show $ prettyNix expr+ (l:ls) = lines contents+ (openBrace, params) = splitAt 1 (words l)+ l' = unwords $ openBrace ++ [param ++ ", "] ++ params+ in+ case parseNixString $ unlines (l':ls) of+ Success expr' -> expr'+ _ -> expr++ stripNonEssentialDeps :: NExpr -> NExpr+ stripNonEssentialDeps expr =+ let sectsToDrop = ["testHaskellDepends", "testToolDepends", "benchmarkHaskellDepends", "benchmarkToolDepends"]+ sectsToKeep = ["executableHaskellDepends", "executableToolDepends", "libraryHaskellDepends", "librarySystemDepends", "libraryToolDepends", "setupHaskellDepends"]+ collectDeps sects = foldr union [] $ fmap (dependenciesFromSection expr) sects+ depsToStrip = collectDeps sectsToDrop \\ collectDeps sectsToKeep+ expr' = foldl dropDependencySection expr sectsToDrop+ in+ dropParams expr' depsToStrip++ handleExtraDep :: FilePath -> Text -> IO ()+ handleExtraDep outDir dep =+ cabal2nix ("cabal://" <> unpack dep) Nothing Nothing (Just outDir)++ overrideFor :: FilePath -> IO String+ overrideFor nixFile = do+ deps <- externalDeps+ return $ " " <> (dropExtension . takeFileName) nixFile <> " = callPackage ./" <> takeFileName nixFile <> " { " <> deps <> " };"+ where+ externalDeps :: IO String+ externalDeps = do+ deps <- librarySystemDeps nixFile+ return . unwords $ fmap (\d -> unpack d <> " = pkgs." <> unpack d <> ";") deps++ pullInNixFiles :: FilePath -> IO ()+ pullInNixFiles nixFile = do+ nf <- parseNixFile nixFile+ case nf of+ Success expr ->+ case expr of+ Fix (NAbs paramSet (Fix (NAbs fnParam (Fix (NSet attrs))))) -> do+ attrs' <- mapM patchAttr attrs+ let expr' = Fix (NAbs paramSet (Fix (NAbs fnParam (Fix (NSet attrs')))))+ writeFile nixFile $ (++ "\n") $ show $ prettyNix expr'+ _ ->+ error $ "unhandled nix expression format\n" ++ show expr+ _ -> error "failed to parse nix file!"+ where+ patchAttr :: Binding (Fix NExprF) -> IO (Binding (Fix NExprF))+ patchAttr attr =+ case attr of+ NamedVar k (Fix (NApp (Fix (NApp (Fix (NSym "callPackage")) pkg)) deps)) -> do+ pkg' <- patchPkgRef pkg+ return $ NamedVar k (Fix (NApp (Fix (NApp (Fix (NSym "callPackage")) pkg')) deps))+ _ -> error $ "unhandled NamedVar"++ patchPkgRef (Fix (NLiteralPath path)) = do+ let p = if isAbsolute path then path else normalise $ takeDirectory nixFile </> path+ nf <- parseNixFile p+ case nf of+ Success expr -> return expr+ _ -> error $ "failed to parse referenced nix file '" ++ path ++ "'"+ patchPkgRef x = return x++ dependenciesFromSection :: NExpr -> String -> [Text]+ dependenciesFromSection expr name = do+ case expr of+ Fix (NAbs _ (Fix (NApp _ (Fix (NSet namedVars))))) ->+ case lookupNamedVar namedVars name of+ Just (Fix (NList deps)) ->+ foldl' (\acc x ->+ case x of+ Fix (NSym n) -> n : acc+ _ -> acc) [] deps+ _ -> []+ _ -> []++ librarySystemDeps :: FilePath -> IO [Text]+ librarySystemDeps nixFile = do+ nf <- parseNixFile nixFile+ case nf of+ Success expr -> return $ dependenciesFromSection expr "librarySystemDepends"+ _ -> return []++ dropDependencySection :: NExpr -> String -> NExpr+ dropDependencySection expr name =+ case expr of+ Fix (NAbs a (Fix (NApp b (Fix (NSet namedVars))))) ->+ Fix (NAbs a (Fix (NApp b (Fix (NSet $ dropNamedVar namedVars name)))))+ _ -> expr++ lookupNamedVar :: [Binding a] -> String -> Maybe a+ lookupNamedVar [] _ = Nothing+ lookupNamedVar (x:xs) name =+ case x of+ NamedVar [StaticKey k] val ->+ if unpack k == name+ then Just val+ else lookupNamedVar xs name+ _ -> lookupNamedVar xs name++ dropNamedVar :: [Binding a] -> String -> [Binding a]+ dropNamedVar xs name = filter differentName xs+ where+ differentName (NamedVar [StaticKey k] _) = unpack k /= name+ differentName _ = True++ dropParams :: NExpr -> [Text] -> NExpr+ dropParams (Fix (NAbs (ParamSet (FixedParamSet paramMap) x)+ (Fix (NApp mkDeriv (Fix (NSet args)))))) names =+ Fix (NAbs (ParamSet (FixedParamSet $ foldr Map.delete paramMap names) x)+ (Fix (NApp mkDeriv (Fix (NSet args)))))+ dropParams x _ = x++ initialPackages overrides = unlines $+ [ "{ pkgs, stdenv, callPackage }:"+ , ""+ , "self: {"+ ] ++ overrides +++ [ "}"+ ]++ defaultNix pkgsNixExpr = unlines+ [ "# Generated using stack2nix " ++ display version ++ "."+ , "#"+ , "# Only works with sufficiently recent nixpkgs, e.g. \"NIX_PATH=nixpkgs=https://github.com/NixOS/nixpkgs/archive/21a8239452adae3a4717772f4e490575586b2755.tar.gz\"."+ , ""+ , "{ pkgs ? (import <nixpkgs> {})"+ , ", compiler ? pkgs.haskell.packages.ghc802"+ , ", ghc ? pkgs.haskell.compiler.ghc802"+ , "}:"+ , ""+ , "with (import <nixpkgs/pkgs/development/haskell-modules/lib.nix> { inherit pkgs; });"+ , ""+ , "let"+ , " stackPackages = " ++ show (prettyNix pkgsNixExpr) ++ ";"+ , "in"+ , "compiler.override {"+ , " initialPackages = stackPackages;"+ , "}"+ ]
+ src/Stack2nix/External.hs view
@@ -0,0 +1,7 @@+module Stack2nix.External+ ( module Stack2nix.External.Cabal2nix+ , module Stack2nix.External.VCS+ ) where++import Stack2nix.External.Cabal2nix+import Stack2nix.External.VCS
+ src/Stack2nix/External/Cabal2nix.hs view
@@ -0,0 +1,43 @@+module Stack2nix.External.Cabal2nix (+ cabal2nix+ ) where++import Data.List (stripPrefix, takeWhile)+import Data.Maybe (fromMaybe)+import Data.Monoid ((<>))+import Data.Text (Text, unpack)+import Stack2nix.External.Util (runCmd)+import System.FilePath ((</>))++-- Requires cabal2nix >= 2.2 in PATH+cabal2nix :: FilePath -> Maybe Text -> Maybe FilePath -> Maybe FilePath -> IO ()+cabal2nix uri commit subpath odir = do+ result <- runCmd exe (args $ fromMaybe "." subpath)+ case result of+ Right stdout ->+ let basename = pname stdout <> ".nix"+ fname = maybe basename (</> basename) odir+ in+ writeFile fname stdout+ Left stderr -> error stderr+ where+ exe = "cabal2nix"++ args :: FilePath -> [String]+ args dir = concat+ [ maybe [] (\c -> ["--revision", unpack c]) commit+ -- , maybe [] (\d -> ["--subpath", d]) subpath+ , ["--subpath", dir]+ , ["--no-check", "--no-haddock"] -- TODO: only use on repos that need it.+ , [uri]+ ]++ pname :: String -> String+ pname = pname' . lines++ pname' :: [String] -> String+ pname' [] = error "nix expression generated by cabal2nix is missing the 'pname' attr"+ pname' (x:xs) =+ case stripPrefix " pname = \"" x of+ Just x' -> takeWhile (/= '"') x'+ Nothing -> pname' xs
+ src/Stack2nix/External/Util.hs view
@@ -0,0 +1,27 @@+module Stack2nix.External.Util where++import Data.List (intercalate)+import Data.Monoid ((<>))+import System.Directory (getCurrentDirectory)+import System.Exit (ExitCode (..))+import System.Process (CreateProcess (..))+import System.Process (proc, readCreateProcessWithExitCode)++runCmdFrom :: FilePath -> String -> [String] -> IO (Either String String)+runCmdFrom dir prog args = do+ (exitCode, stdout, stderr) <- readCreateProcessWithExitCode (fromDir dir (proc prog args)) ""+ case exitCode of+ ExitSuccess -> return $ Right stdout+ _ -> return $ Left $ "Command failed: " ++ cmd ++ "\n" ++ stderr+ where+ fromDir :: FilePath -> CreateProcess -> CreateProcess+ fromDir d procDesc = procDesc { cwd = Just d }++ cmd = "cd \"" <> dir <> "\" && " <> intercalate " " (prog:args)++runCmd :: String -> [String] -> IO (Either String String)+runCmd prog args = getCurrentDirectory >>= (\d -> runCmdFrom d prog args)++failHard :: Show a => Either a b -> IO ()+failHard (Left stderr) = error $ show stderr+failHard (Right _) = mempty
+ src/Stack2nix/External/VCS.hs view
@@ -0,0 +1,5 @@+module Stack2nix.External.VCS+ ( module Stack2nix.External.VCS.Git+ ) where++import Stack2nix.External.VCS.Git
+ src/Stack2nix/External/VCS/Git.hs view
@@ -0,0 +1,29 @@+module Stack2nix.External.VCS.Git+ ( Command(..), ExternalCmd(..), InternalCmd(..)+ , git+ ) where++import Stack2nix.External.Util (failHard, runCmd, runCmdFrom)++data Command = OutsideRepo ExternalCmd+ | InsideRepo FilePath InternalCmd+data ExternalCmd = Clone String FilePath+data InternalCmd = Checkout CommitRef++type CommitRef = String++exe :: String+exe = "git"++-- Requires git binary in PATH+git :: Command -> IO ()+git (OutsideRepo cmd) = runExternal cmd+git (InsideRepo dir cmd) = runInternal dir cmd++runExternal :: ExternalCmd -> IO ()+runExternal (Clone uri dir) = do+ runCmd exe ["clone", uri, dir] >>= failHard++runInternal :: FilePath -> InternalCmd -> IO ()+runInternal repoDir (Checkout ref) = do+ runCmdFrom repoDir exe ["checkout", ref] >>= failHard
+ stack2nix.cabal view
@@ -0,0 +1,54 @@+name: stack2nix+version: 0.1.1.0+synopsis: Convert stack.yaml files into Nix build instructions.+description: Convert stack.yaml files into Nix build instructions.+license: MIT+license-file: LICENSE+author: Jacob Mitchell+maintainer: jacob.mitchell@iohk.io+category: Distribution, Nix+build-type: Simple+extra-source-files: README.md+cabal-version: >= 1.10++source-repository head+ type: git+ location: https://github.com/input-output-hk/stack2nix.git++library+ hs-source-dirs: src+ build-depends: base >=4.9 && <4.10+ , Cabal+ , async+ , bytestring+ , containers+ , data-fix+ , directory+ , filepath+ , Glob+ , hnix >= 0.3.4+ , monad-parallel+ , process+ , SafeSemaphore+ , temporary+ , text+ , yaml+ exposed-modules: Stack2nix+ other-modules: Stack2nix.External+ , Stack2nix.External.Cabal2nix+ , Stack2nix.External.VCS+ , Stack2nix.External.VCS.Git+ , Stack2nix.External.Util+ , Paths_stack2nix+ ghc-options: -Wall+ default-language: Haskell2010++executable stack2nix+ main-is: Main.hs+ build-depends: base >=4.9 && <4.10+ , Cabal+ , stack2nix+ , optparse-applicative+ hs-source-dirs: stack2nix+ ghc-options: -Wall+ default-language: Haskell2010
+ stack2nix/Main.hs view
@@ -0,0 +1,22 @@+module Main ( main ) where++import Data.Semigroup ((<>))+import Distribution.Text (display)+import Options.Applicative+import Stack2nix++args :: Parser Args+args = Args+ <$> optional (strOption $ long "revision" <> help "revision to use when fetching from VCS")+ <*> optional (strOption $ short 'o' <> help "output file for generated nix expression")+ <*> strArgument (metavar "URI")++main :: IO ()+main = stack2nix =<< execParser opts+ where+ opts = info+ (helper+ <*> infoOption ("stack2nix " ++ display version) (long "version" <> help "Show version number")+ <*> args) $+ fullDesc+ <> progDesc "Generate a nix expression for a Haskell package using stack"