packages feed

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 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++[![Build Status](https://travis-ci.org/input-output-hk/stack2nix.svg)](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"