{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings, TemplateHaskell #-}
module Stack.Nix
(reexecWithOptionalShell
,nixCmdName
) where
import Control.Applicative
import Control.Monad
import Control.Monad.Catch (try,MonadCatch)
import Control.Monad.IO.Class (MonadIO,liftIO)
import Control.Monad.Logger (MonadLogger,logDebug)
import Control.Monad.Reader (MonadReader,asks)
import Control.Monad.Trans.Control (MonadBaseControl)
import Data.Char (toUpper)
import Data.List (intercalate)
import Data.Maybe
import Data.Monoid
import Data.Streaming.Process (ProcessExitedUnsuccessfully(..))
import qualified Data.Text as T
import Data.Version (showVersion)
import Network.HTTP.Client.Conduit (HasHttpManager)
import qualified Paths_stack as Meta
import Prelude
import Stack.Constants (stackProgName)
import Stack.Docker (reExecArgName)
import Stack.Exec (exec)
import System.Process.Read (getEnvOverride)
import Stack.Types
import Stack.Types.Internal
import System.Environment (lookupEnv,getArgs,getExecutablePath)
import System.Exit (exitSuccess, exitWith)
reexecWithOptionalShell
:: M env m
=> IO ()
-> m ()
reexecWithOptionalShell inner =
do config <- asks getConfig
inShell <- getInShell
isReExec <- asks getReExec
if nixEnable (configNix config) && not inShell && not isReExec
then runShellAndExit getCmdArgs
else liftIO inner
where
getCmdArgs = do
args <-
fmap
(("--" ++ reExecArgName ++ "=" ++ showVersion Meta.version) :)
(liftIO getArgs)
exePath <- liftIO getExecutablePath
return (exePath, args)
runShellAndExit :: M env m
=> m (String, [String])
-> m ()
runShellAndExit getCmdArgs = do
config <- asks getConfig
envOverride <- getEnvOverride (configPlatform config)
(cmnd,args) <- getCmdArgs
let mshellFile = nixInitFile (configNix config)
pkgsInConfig = nixPackages (configNix config)
nixopts = case mshellFile of
Just filePath -> [filePath]
Nothing -> ["-E", T.unpack $ T.intercalate " " $ concat
[["with (import <nixpkgs> {});"
,"runCommand \"myEnv\" {"
,"buildInputs=lib.optional stdenv.isLinux glibcLocales ++ ["],pkgsInConfig,["];"
,T.pack inShellEnvVar,"=1 ;"
,"STACK_IN_NIX_EXTRA_ARGS=''"]
, (map (\p -> T.concat
["--extra-lib-dirs=${",p,"}/lib"
," --extra-include-dirs=${",p,"}/include "])
pkgsInConfig), ["'' ;"
,"} \"\""]]]
fullArgs = concat [
map T.unpack (nixShellOptions (configNix config))
,nixopts
,["--command", intercalate " " (map escape (cmnd:args))
++ " $STACK_IN_NIX_EXTRA_ARGS"]
]
$logDebug $
"Using a nix-shell environment " <> (case mshellFile of
Just filePath -> "from file: " <> (T.pack filePath)
Nothing -> "with nix packages: " <> (T.intercalate ", " pkgsInConfig))
e <- try (exec envOverride "nix-shell" fullArgs)
case e of
Left (ProcessExitedUnsuccessfully _ ec) -> liftIO (exitWith ec)
Right () -> liftIO exitSuccess
escape :: String -> String
escape str = "'" ++ foldr (\c -> if c == '\'' then
("'\"'\"'"++)
else (c:)) "" str
++ "'"
getInShell :: (MonadIO m) => m Bool
getInShell = liftIO (isJust <$> lookupEnv inShellEnvVar)
inShellEnvVar :: String
inShellEnvVar = concat [map toUpper stackProgName,"_IN_NIXSHELL"]
nixCmdName :: String
nixCmdName = "nix"
type M env m =
(MonadIO m
,MonadReader env m
,MonadLogger m
,MonadBaseControl IO m
,MonadCatch m
,HasConfig env
,HasTerminal env
,HasReExec env
,HasHttpManager env
)