{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings #-}
module Stack.Nix
(reexecWithOptionalShell
,nixCmdName
,nixHelpOptName
) where
import Stack.Prelude
import qualified Data.Text as T
import Data.Version (showVersion)
import Lens.Micro (set)
import Path.IO
import qualified Paths_stack as Meta
import Stack.Config (getInNixShell, getInContainer)
import Stack.Config.Nix (nixCompiler)
import Stack.Constants (platformVariantEnvVar,inNixShellEnvVar,inContainerEnvVar)
import Stack.Types.Config
import Stack.Types.Docker
import Stack.Types.Nix
import Stack.Types.Runner
import Stack.Types.Compiler
import System.Environment (getArgs,getExecutablePath,lookupEnv)
import qualified System.FilePath as F
import RIO.Process (processContextL, exec)
reexecWithOptionalShell
:: HasConfig env
=> Maybe (Path Abs Dir)
-> IO (CompilerVersion 'CVWanted)
-> IO ()
-> RIO env ()
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
:: HasConfig env
=> Maybe (Path Abs Dir)
-> IO (CompilerVersion 'CVWanted)
-> RIO env (String, [String])
-> RIO env ()
runShellAndExit mprojectRoot getCompilerVersion getCmdArgs = do
config <- view configL
envOverride <- view processContextL
local (set processContextL envOverride) $ do
(cmnd,args) <- fmap (escape *** map escape) getCmdArgs
mshellFile <-
traverse (resolveFile (fromMaybeProjectRoot mprojectRoot)) $
nixInitFile (configNix config)
compilerVersion <- liftIO getCompilerVersion
inContainer <- getInContainer
ghc <- either throwIO return $ nixCompiler compilerVersion
let pkgsInConfig = nixPackages (configNix config)
pkgs = pkgsInConfig ++ [ghc, "git", "gcc", "gmp"]
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: " <> displayShow pathVar
logDebug $
"Using a nix-shell environment " <> (case mshellFile of
Just path -> "from file: " <> fromString (toFilePath path)
Nothing -> "with nix packages: " <> display (T.intercalate ", " pkgs))
exec "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 (impureThrow 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."