module Stack.Nix
(reexecWithOptionalShell
,nixCmdName
,nixHelpOptName
) where
import Control.Arrow ((***))
import Control.Exception (Exception,throw)
import Control.Monad hiding (mapM)
import Control.Monad.IO.Class (liftIO)
import Control.Monad.Logger (logDebug)
import Data.Maybe
import Data.Monoid
import qualified Data.Text as T
import Data.Traversable
import Data.Typeable (Typeable)
import Data.Version (showVersion)
import Path
import Path.IO
import qualified Paths_stack as Meta
import Prelude hiding (mapM)
import Stack.Config (getInNixShell, getInContainer)
import Stack.Config.Nix (nixCompiler)
import Stack.Constants (platformVariantEnvVar,inNixShellEnvVar,inContainerEnvVar)
import Stack.Exec (exec)
import Stack.Types.Config
import Stack.Types.Docker
import Stack.Types.Nix
import Stack.Types.Compiler
import Stack.Types.Internal
import Stack.Types.StackT
import System.Environment (getArgs,getExecutablePath,lookupEnv)
import qualified System.FilePath as F
import System.Process.Read (getEnvOverride)
reexecWithOptionalShell
:: (StackM env m, HasConfig env)
=> Maybe (Path Abs Dir)
-> IO CompilerVersion
-> IO ()
-> m ()
reexecWithOptionalShell mprojectRoot getCompilerVersion inner =
do config <- view configL
inShell <- getInNixShell
inContainer <- getInContainer
isReExec <- view reExecL
let getCmdArgs = do
origArgs <- liftIO getArgs
let args | inContainer = origArgs
| otherwise =
("--" ++ reExecArgName ++ "=" ++ showVersion Meta.version) : origArgs
exePath <- liftIO getExecutablePath
return (exePath, args)
if nixEnable (configNix config) && not inShell && (not isReExec || inContainer)
then runShellAndExit mprojectRoot getCompilerVersion getCmdArgs
else liftIO inner
runShellAndExit
:: (StackM env m, HasConfig env)
=> Maybe (Path Abs Dir)
-> IO CompilerVersion
-> m (String, [String])
-> m ()
runShellAndExit mprojectRoot getCompilerVersion getCmdArgs = do
config <- view configL
envOverride <- getEnvOverride (configPlatform config)
(cmnd,args) <- fmap (escape *** map escape) getCmdArgs
mshellFile <-
traverse (resolveFile (fromMaybeProjectRoot mprojectRoot)) $
nixInitFile (configNix config)
compilerVersion <- liftIO getCompilerVersion
inContainer <- getInContainer
let pkgsInConfig = nixPackages (configNix config)
ghc = nixCompiler compilerVersion
pkgs = pkgsInConfig ++ [ghc]
pkgsStr = "[" <> T.intercalate " " pkgs <> "]"
pureShell = nixPureShell (configNix config)
addGCRoots = nixAddGCRoots (configNix config)
nixopts = case mshellFile of
Just fp -> [toFilePath fp, "--arg", "ghc"
,"with (import <nixpkgs> {}); " ++ T.unpack ghc]
Nothing -> ["-E", T.unpack $ T.concat
["with (import <nixpkgs> {}); "
,"let inputs = ",pkgsStr,"; "
, "libPath = lib.makeLibraryPath inputs; "
, "stackExtraArgs = lib.concatMap (pkg: "
, "[ ''--extra-lib-dirs=${lib.getLib pkg}/lib'' "
, " ''--extra-include-dirs=${lib.getDev pkg}/include'' ]"
, ") inputs; in "
,"runCommand ''myEnv'' { "
,"buildInputs = lib.optional stdenv.isLinux glibcLocales ++ inputs; "
,T.pack platformVariantEnvVar <> "=''nix''; "
,T.pack inNixShellEnvVar <> "=1; "
,if inContainer
then T.pack inContainerEnvVar <> "=1; "
else ""
,"LD_LIBRARY_PATH = libPath;"
,"STACK_IN_NIX_EXTRA_ARGS = stackExtraArgs; "
,"} \"\""]]
fullArgs = concat [if pureShell then ["--pure"] else []
,if addGCRoots then ["--indirect", "--add-root"
,toFilePath (configWorkDir config)
F.</> "nix-gc-symlinks" F.</> "gc-root"] else []
,map T.unpack (nixShellOptions (configNix config))
,nixopts
,["--run", unwords (cmnd:"$STACK_IN_NIX_EXTRA_ARGS":args)]
]
pathVar <- liftIO $ lookupEnv "PATH"
$logDebug $ "PATH is: " <> T.pack (show pathVar)
$logDebug $
"Using a nix-shell environment " <> (case mshellFile of
Just path -> "from file: " <> T.pack (toFilePath path)
Nothing -> "with nix packages: " <> T.intercalate ", " pkgs)
exec envOverride "nix-shell" fullArgs
escape :: String -> String
escape str = "'" ++ foldr (\c -> if c == '\'' then
("'\"'\"'"++)
else (c:)) "" str
++ "'"
fromMaybeProjectRoot :: Maybe (Path Abs Dir) -> Path Abs Dir
fromMaybeProjectRoot = fromMaybe (throw CannotDetermineProjectRoot)
nixCmdName :: String
nixCmdName = "nix"
nixHelpOptName :: String
nixHelpOptName = nixCmdName ++ "-help"
data StackNixException
= CannotDetermineProjectRoot
deriving (Typeable)
instance Exception StackNixException
instance Show StackNixException where
show CannotDetermineProjectRoot =
"Cannot determine project root directory."